Структура данных для карты тайлов для использования с artemis
Я работал над пошаговой игрой на основе карт тайлов, используя artemis- odb и libGDX.
Я хотел, чтобы для карты были разные типы ландшафта, такие как трава, песок, вода, горы и т. Д., Причем каждый из этих типов ландшафта имел разные затраты на перемещение и различные дополнительные свойства, относящиеся к игровому процессу.
Я рассматриваю пару различных подходов в настоящее время:
Я мог бы сделать карту системой
GameMapSystem
и каждый тип местности представлен объектом с соответствующими компонентами для каждого типа местности (TerrainStats
и иногда заклинание компонентов эффектаExploding
например). Моя главная задача - как управлять отображением тайлов в сущности типа местности. Концептуально это должно быть так же просто, как поддержаниеint[][]
со значениями, соответствующими идентификатору объекта Terrain, однако в этом случае временные компоненты маркера (Exploding
) будет привязан ко всем заданным типам местности одновременно. Это кажется менее чем оптимальным. Так тогда мне нужно будет иметь отдельную сущность для каждой плитки? Разве я не создаю дополнительные издержки для структуры сущностей, если я сделаю это тогда?Я также рассмотрел создание игровой карты и типов местности 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))
- Компоненты представляют собой строки таблицы в соответствующей таблице компонентов и содержат данные для определенной функциональности / состояния.
Похоже, что мы должны исследовать реальное решение, так как парадигма не достаточно зрелая, чтобы иметь "правильный" ответ