Структура данных для карты тайлов для использования с artemis

Я работал над пошаговой игрой на основе карт тайлов, используя artemis- odb и libGDX.

Я хотел, чтобы для карты были разные типы ландшафта, такие как трава, песок, вода, горы и т. Д., Причем каждый из этих типов ландшафта имел разные затраты на перемещение и различные дополнительные свойства, относящиеся к игровому процессу.

Я рассматриваю пару различных подходов в настоящее время:

  1. Я мог бы сделать карту системой GameMapSystem и каждый тип местности представлен объектом с соответствующими компонентами для каждого типа местности (TerrainStats и иногда заклинание компонентов эффекта Exploding например). Моя главная задача - как управлять отображением тайлов в сущности типа местности. Концептуально это должно быть так же просто, как поддержание int[][] со значениями, соответствующими идентификатору объекта Terrain, однако в этом случае временные компоненты маркера (Exploding) будет привязан ко всем заданным типам местности одновременно. Это кажется менее чем оптимальным. Так тогда мне нужно будет иметь отдельную сущность для каждой плитки? Разве я не создаю дополнительные издержки для структуры сущностей, если я сделаю это тогда?

  2. Я также рассмотрел создание игровой карты и типов местности POJOS, а затем просто создавал объекты-маркеры с компонентами-маркерами для специальных эффектов. Делая это таким образом, похоже, я бы проходил GameMap объект вокруг волей-неволей, чтобы различные системы могли обрабатывать его (для рендеринга, столкновения, прохождения пути и т. д.). Кроме того, разве моя игровая карта не должна также отслеживать объекты, которые находятся на карте в любой момент времени, с их позициями, чтобы выполнять мою логику прохождения? Я бы предпочел оставить управление объектами полностью в рамках структуры объектов, если это возможно, поскольку это означает немного более простое обслуживание.

Мне любопытно, есть ли какие-либо подходы, которые я еще не исследовал. В противном случае я чувствую склонность к методу № 2, если только нет способа исправить метод № 1, который я упустил.

2 ответа

Решение

Я закончил тем, что использовал что-то из обоих методов. Следующие фрагменты кода должны помочь проиллюстрировать метод, который я предпринял:

class TerrainType {
    public String displayName;
    public String regionName;
    public int movementCost;
    /* additional properties omitted */
    /* constructors omitted */
}

Эта структура содержит соответствующую информацию о типе местности, включая стоимость движения и другую статистику, связанную с игровым процессом (остальное я упустил для простоты), отображаемое имя типа местности, которое будет отображаться при осмотре, и имя TextureRegion вытащить из TextureAtlas что мой рендерер так добр ко мне относится.

class GameMapSystem extends EntityProcessingSystem {
    @Mapper private ComponentMapper<MapPosition> pm;
    @Mapper private ComponentMapper<SolidObject> som;

    private ListMultimap<MapPosition, Entity> entityByLocation;

    private int[][] map;
    private int width, height;
    private Array<TerrainType> terrainTypes;

    /**
     * Accepts an Array of TerrainType objects and an 2d integer array with
     * values corresponding to indices into the array for the correct type.
     * 
     * In my case, these values are gleaned by reading a level description
     * file, but any source should be fine.
     */
    public GameMapSystem(Array<TerrainType> terrainTypes, int[][] map) {
        super(Aspect.getForAll(MapPosition.class));
        this.terrainTypes = terrainTypes;
        this.map = map;
        this.width = map.length;
        this.height = map[0].length;
        this.entityByLocation = ArrayListMultimap.create();
    }

    public boolean isOccupied(int x, int y) {
        List<Entity> entities = entityByLocation(new MapPosition(x, y));
        for(Entity e : entities) {
            if(som.has(e)) {
                return true;
            }
        }
        return false;
    }

    @Override
    protected void inserted(Entity e) {
        this.entityByLocation.put(pm.get(e), e);
    }

    @Override
    protected void removed(Entity e) {
        this.entityByLocation.remove(pm.get(e), e);
    }

    /* additional EntityProcessingSystem overrides omitted */
}

это EntityProcessingSystem затем присоединяется к моему миру в пассивном режиме. Там не должно быть никаких реальных причин для какой-либо обработки для моего мира в этой системе, что я действительно хотел, чтобы иметь возможность слушать inserted а также removed события для размещения объектов на карте. Менеджер был бы излишним в этой ситуации только потому, что он сказал бы мне о КАЖДОЙ сущности, вставляемой или удаляемой, и я заботился только о связанных с картой (или, более конкретно, связанных с картой позициях). Затем у меня есть отдельная логика поиска пути, которая потребляет дополнительные (не замеченные здесь) методы, чтобы направлять ИИ, просто запрашивая эту пассивную систему у объекта мира.

Для полноты MapPosition класс также показан ниже. Важным является включение equals() а также hashcode() чтобы помочь в использовании MapPosition как ключ в коллекции.

public class MapPosition extends Component
{
    public int x, y;

    public MapPosition(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object other) {
        if(!(other instanceof MapPosition)) {
            return false;
        }

        MapPosition pos = (MapPosition)other;

        return (pos.x == this.x && pos.y == this.y);
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 59 * hash + this.x;
        hash = 59 * hash + this.y;
        return hash;
    }
}

Я, вероятно, буду пытаться найти более удобную структуру данных, чем использование гуавы Multimap в конце концов, но это работает на данный момент, и я чувствую себя комфортно, продолжая раскрывать остальную часть публичного API для этих классов. Если этот ответ поможет кому-то еще, имейте в виду, что производительность ArrayListMultimap Для этой реализации не прошли строгие испытания!

Я нахожусь в процессе выяснения того же самого. Просто хотел поделиться некоторыми ответами от разработчика Artemis об этом деле, которое фактически оставляет нас без ответа, но стоит упомянуть:

http://slick.ninjacave.com/forum/viewtopic.php?p=20125 http://slick.ninjacave.com/forum/viewtopic.php?p=20136

Честно говоря, Артемида "все еще считается экспериментальной". Это новая парадигма, на которую я хотел взглянуть, она многообещающая, но есть еще те вопросы, на которые я не нашел ответов, насколько большую роль играют системы, что вы не вкладываете в сущности / компоненты и т. Д. У меня в голове происходит битва за то, какую большую роль играют сущности / компоненты, когда дело доходит до того, что кажется не сущностью, такой как ландшафт, фоновая музыка и т. Д.

Также другой намек, данный им, должен различать как:

  • Системы "это может делать" на / с сущностями. ("может" AcquireEnemyTarget(System), "может" SpawnNewBaddies(System))
  • Компоненты представляют собой строки таблицы в соответствующей таблице компонентов и содержат данные для определенной функциональности / состояния.

Похоже, что мы должны исследовать реальное решение, так как парадигма не достаточно зрелая, чтобы иметь "правильный" ответ

Другие вопросы по тегам