【问题标题】:Data structure for tile map for use with artemis与 artemis 一起使用的瓦片地图的数据结构
【发布时间】:2014-06-06 23:21:18
【问题描述】:

我一直在使用artemis-odblibGDX 开发基于回合制地图的游戏。

我希望地图有不同的地形类型,例如草、沙、水、山脉等,每种不同的地形类型都有不同的移动成本和与游戏相关的各种附加属性。

我目前正在考虑几种不同的方法:

  1. 我可以使地图成为一个系统GameMapSystem,并让每种类型的地形都由一个实体表示,每种类型的地形都有相关的组件(TerrainStats,偶尔还有法术效果组件Exploding 例如)。我主要关心的是如何管理瓷砖到地形类型实体的映射。从概念上讲,这应该像维护int[][] 一样简单,其值对应于地形实体的 id,但是在这种情况下,临时标记组件 (Exploding) 将一次附加到所有给定的地形类型.这似乎不太理想。那么我需要为每个图块设置一个单独的实体吗?如果我这样做,我不会为实体框架增加额外的开销吗?

  2. 我还考虑过制作游戏地图和地形类型 POJOS,然后简单地创建带有特殊效果的标记组件的标记实体。但是,这样做,看起来我会随意传递GameMap 对象,以便让各种系统能够对其进行处理(用于渲染、碰撞、路径等)。此外,我的游戏地图是否还需要在任何给定时间跟踪地图上的实体及其位置以执行我的路径逻辑?如果可能的话,我更愿意将实体的管理完全保留在实体框架的范围内,因为这意味着维护会稍微容易一些。

我很好奇是否有任何我尚未研究过的方法。否则我会倾向于方法 #2,除非有某种方法可以修复我忽略的方法 #1。

【问题讨论】:

    标签: java libgdx 2d-games artemis


    【解决方案1】:

    我最终使用了两种方法中的一些东西。以下代码 sn-ps 应该有助于说明我采用的方法:

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

    此结构包含有关地形类型的相关信息,包括移动成本和其他与游戏相关的统计数据(为简单起见,我省略了其余部分)、地形类型的显示名称(如果检查)以及 @ 的名称987654322@ 从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 然后以被动模式连接到我的世界。不应该有任何真正的理由在这个系统中对我的世界进行任何处理,我真正想要的是能够收听insertedremoved 事件以将实体放入地图中。在这种情况下,经理会矫枉过正,因为它会告诉我每个实体都被插入或删除,而我只关心与地图相关的实体(或者更具体地说是与地图相关的具有位置的实体)。然后,我有一些单独的寻路逻辑,它使用额外的(此处未显示)方法来引导 AI,只需从世界对象请求这个被动系统即可。

    为了完整起见,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;
        }
    }
    

    我最终可能会尝试找到一个比使用 guava Multimap 更方便的数据结构,但它现在适用,我很乐意继续充实这些类的其余公共 API。如果这个答案确实对其他人有所帮助,请记住,ArrayListMultimap 在此实现中的性能尚未经过严格的测试!

    【讨论】:

      【解决方案2】:

      我正在解决同样的问题。只是想分享一些来自 Artemis 开发人员关于这个案例的答案,这让我们实际上没有答案,但值得一提:

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

      老实说,Artemis“仍然被认为是实验性的”。这是我想研究的一个新范式,它很有希望,但仍有一些问题我还没有真正找到答案,系统扮演的角色有多大,你没有将什么放入实体/组件等。当涉及到看似非实体的事物(例如地形、背景音乐等)时,我的脑海中正在发生一些关于实体/组件所扮演的角色的争论。

      他给出的另一个提示是区分如下:

      • 在实体上/与实体“它可以做”的系统。 (“它可以”AcquireEnemyTarget(System),“它可以”SpawnNewBaddies(System))
      • 组件是相应组件表中的表行,包含特定功能/状态的数据。

      所以看起来应该由我们来探索实际的解决方案,因为范式还不够成熟,无法给出“正确”的答案

      【讨论】:

      • 欢迎来到 StackOverflow!请务必拨打Tour 并仔细阅读帮助中心以获取How to write a good answer 等信息。在这种情况下,您至少可以通过总结链接中讨论的问题来改进您的答案,而不是简单地粘贴链接本身。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-17
      • 1970-01-01
      • 2017-06-10
      • 1970-01-01
      相关资源
      最近更新 更多