我目前正在研究一个包含等轴测地图的二维引擎。 它运行得很好,但我不确定是否select了这种发动机的最佳方法。 为了给你一个想法,现在我们来看一下瓷砖贴图及其对象的基本对象:
public class TileMap { public List<MapRow> Rows = new List<MapRow>(); public int MapWidth = 50; public int MapHeight = 50; } public class MapRow { public List<MapCell> Columns = new List<MapCell>(); } public class MapCell { public int TileID { get; set; } }
有了这些对象,就可以将一个图块分配给一个MapCell。 我想要我的引擎支持的就像有一组MapCells,因为我想添加对象到我的瓷砖地图(例如一个大小为2×2瓷砖的房子)。 我该怎么做? 我应该编辑我的MapCell对象,它可能有其他相关的瓷砖的引用,以及如何find一个对象,同时点击单个MapCells? 或者我应该做另一种方法使用一个全局容器,其中的所有对象?
我将要谈论devise和性能。
你现在所称的是一个Jagged Array,它是灵活的,但在这里没有任何用处。
我的意见是,你应该将存在的对象(tile)存储到你的世界中,作为数组的一部分,以减less堆的浪费,内存碎片,增加局部性,减less间接性,完全避免参考开销。 不幸的是,你似乎正在使用java或C#,这将不允许你控制任何这些方面。 Jave / C#引用比C ++ shared_ptr快,但比原始指针慢,肯定比就地对象慢。 分配有缺点,因为在java / c#evreything必须单独分配你有一种艰难的运气情况。 但是不要误解我的意思,直到你的游戏成为双头4k分辨率,并且必须管理巨大而丰富的地形,今天的桌面机器不会被这些细节所困扰。
std::vector
,在java / C#中,你无法避免你的容器将无论如何都存储引用的事实,所以你松散就地对象优势的迭代。 如果您绝对需要考虑所有元素(例如生物实体的逻辑更新)来运行IA例程,则迭代非常有用。 或者,对于装饰元素的animation来更新他们当前的帧,即使这是可以讨论的,animation更新也可以在渲染过程中完成,因为如果瓦片不在屏幕上,可以决定animation没有任何用途,在这种情况下,第二种方法将会足够的研究方法。 将id(索引)存储到您的空间分区结构中,这样您可以获得只有一个数组的主存储区以及将id存储到主数组中的基于位置的存储区。
如果你不得不删除你的世界中的东西,只要保持一个平行arrays来存储这些洞的索引。 这将被称为freelist。 有些人更喜欢用数组中的最后一个元素来交换元素,然后popup。 这也很棒,但是已经持有最后一个对象的指针/引用/索引的人必须更新,这可能很困难。 特别是你的空间分区结构是持有指数,所以这至less要更新。 在这方面,freelist方法更简单快捷。 下一次,你想添加一个对象,只需selectfreelist中的最后一个索引,并将其popup。 如果在你的世界中添加了太多的对象,并且主arrays变满,立即崩溃游戏,find一个新的适当的大小,并重新编码新的大小。 dynamicresize是不可取的,游戏场景控制良好,只需执行“configuration文件”运行即可确定最大限制是多less。 如果您的引擎将被运送到您不知道他们将使用哪种场景的客户端,请将此常量显示在构建设置中。 (如一些CMakevariables),并logging下来。
在devise方面,锯齿arrays有维护问题,因为它强制重复代码遵循行和列方面的双重间接,这实际上没有任何帮助。 线性方法具有灵活性的优点,如果必须将其更改为哈希映射,则索引仍然可以用作标识符,以便其他系统不会受到影响。 或者更疯狂的,一个可以stream式处理的虚拟容器。 指数可以在一个需要(无限?)漫游的世界中随时加载,因此容器可能特别复杂,但是标识符在这个意义上创建了一个很好的松耦合。
另外当你反序列化时,你不必考虑行列的顺序,只要按照命运决定的顺序推送到数组,就会很酷。 无论如何,地点访问都是由不同的结构加速的。 祝你好运
忽略事实上,你使用的数组看起来像数组的数据..
只需将TileID存储在多行中,即可放置单元格。
这样,如果你做一些find最接近的/ A *任何边/边将被发现。
如果你想要成为一个主要的单元格,否定了次要的单元格,所以你不要在区域searchfunction中重复计数,但仍然知道它是types的。 给你使用一个int TitleID你不会丢失任何东西从你的ID空间。