简体   繁体   English

C# XNA - tilemap 的 int[,] 数组或 Tile[,] 数组

[英]C# XNA - int[,] array or Tile[,] array for tilemap

First off to spare anyone not very interested in reading this whole novel, there is no code in this question it is purely theoretical.首先,为了避免对阅读整本小说不太感兴趣的人,这个问题中没有代码,它纯粹是理论性的。 I am on my phone and this is a question about which choice is best fitting and easiest to work with in the situation.我正在打电话,这是一个关于哪种选择最适合且最容易在这种情况下使用的问题。

So to the question, I am working on what I believe should be a basic Tilemap engine* and I'm in doubt about my approach.所以对于这个问题,我正在研究我认为应该是一个基本的 Tilemap 引擎*,我对我的方法有疑问。 I have already rewritten it several times.我已经重写了几次。 Not that it have taken too long, the project is still in its diaper to say the least.并不是说它花了太长时间,至少可以说该项目仍在尿布中。

Basically, I have a Sprite class with this constructor:string name, int Id, texture2d texture.基本上,我有一个带有这个构造函数的 Sprite 类:字符串名称、整数 ID、纹理 2d 纹理。

And then there is my Tile class that derives from the Sprite class, with these added to the constructor : Int X, int Y, bool walkable.然后是我的 Tile 类派生自 Sprite 类,将这些添加到构造函数中:Int X、int Y、bool walkable。

At my first attempt I used a 2D integer array and assigned the ID value of the desired tile to the position on the array.在我第一次尝试时,我使用了一个 2D 整数数组并将所需图块的 ID 值分配给数组上的位置。 And, then when drawing the tilemap I would double loop** the entire array and find the tile with the Same ID as the array position currently at in the loop with a Tile function, which would return a new Tile, that I would then continue to draw with the loops current X and Y(multiplied with texture size, for obvious reasons).然后,在绘制 tilemap 时,我会双重循环**整个数组,并使用 Tile 函数找到与当前在循环中的数组位置具有相同 ID 的 tile,该函数将返回一个新的 Tile,然后我将继续绘制循环电流 X 和 Y(乘以纹理大小,原因很明显)。 Because at this point, my Tile class didn't have X and Y values.因为此时,我的 Tile 类没有 X 和 Y 值。 Which seemed like an awful lot of throwing forth and back, and for some reason I felt it was a sort of primitive approach.这似乎是来回折腾的次数太多了,出于某种原因,我觉得这是一种原始的方法。

But then I went with another approach, I made a 2D Tile array and instead of having to assign each of the arrays positions an ID value, and then matching it, making a new instance of it, and then drawing it I could just loop the array and make the array position a new instance of the desired tile with the current X and Y in the constructor, and then draw it with the Tile.X and Tile.Y.但是后来我采用了另一种方法,我创建了一个 2D Tile 数组,而不必为每个数组分配一个 ID 值,然后匹配它,创建它的一个新实例,然后绘制它,我可以循环数组并使用构造函数中的当前 X 和 Y 将数组定位为所需瓦片的新实例,然后使用 Tile.X 和 Tile.Y 绘制它。 Seemed more fitting.看起来更合适。

At least for about a second before I ran the code, it did.至少在我运行代码之前大约一秒钟,它确实如此。 See, now I needed to fully populate the whole array with tiles before I tried to draw it, or else it would return a null object reference( of course ), because previously I had a tile with the ID of 0, which it would just draw as the default if nothing else had been assigned.看,现在我需要在尝试绘制它之前用瓷砖完全填充整个数组,否则它会返回一个空对象引用(当然),因为以前我有一个 ID 为 0 的瓷砖,它只会如果没有指定任何其他内容,则绘制为默认值。 Somewhere in all the mess I had before, I also had a simple little "if" that just told it not to draw if the ID was 0, but it was later on I added that.在我之前所有混乱的某个地方,我还有一个简单的小“如果”,只是告诉它如果 ID 为 0 就不要绘制,但后来我添加了它。 I realize now that I could do the same with the Tile array, just instead of checking if 0, then checking if null I guess.我现在意识到我可以对 Tile 数组做同样的事情,而不是检查是否为 0,然后检查是否为空。 But, I don't care much for checking if null.但是,我不太关心检查是否为空。 Again, that seems primitive in some way to me, as If the code is not fully thought through and doing something it's not supposed to.同样,这在某种程度上对我来说似乎很原始,因为如果代码没有经过充分考虑并做了不应该做的事情。 Also, I would now be in sort of a pickle if I tried to parse a simple xml file as tilemap, because I wouldn't know how to match the xml with a tile, where I previously just could've used the ID's.此外,如果我尝试将一个简单的 xml 文件解析为 tilemap,我现在会有点不高兴,因为我不知道如何将 xml 与 tile 匹配,而我以前只能使用 ID。 Btw, I've never worked with xml before, so I can only guess if that would be a problem.顺便说一句,我以前从未使用过 xml,所以我只能猜测这是否会成为问题。

*although if anyone would clarify exactly when or why a "project" could be considered an engine, however basic it may be, then that'd be cool too. *尽管如果有人能确切说明何时或为什么可以将“项目”视为引擎,无论它多么基本,那也很酷。 Because I don't think I understand that fully yet.因为我认为我还没有完全理解这一点。

** I realized that it would be more fitting to use foreach in this situation, but that would only work with the Tile array, afaik. ** 我意识到在这种情况下使用 foreach 会更合适,但这只适用于 Tile 数组,afaik。

TL;DR So to end my question.. what is most fitting? TL;DR 所以结束我的问题......什么是最合适的? The int[,] or the Tile[,] for a tilemap?瓷砖地图的 int[,] 或 Tile[,] ? And, should I instead of using 2 unrelated X and Y ints use vector2 in the Tile class for positioning?而且,我应该在 Tile 类中使用 vector2 来代替 2 个不相关的 X 和 Y 整数进行定位吗?

Note - I am a computer science student, and we have only had the basic "hello world" training in so far, the rest I've learned is from personal interest.注意 - 我是一名计算机科学专业的学生,​​到目前为止我们只接受了基本的“hello world”培训,我学到的其余知识是出于个人兴趣。 Also, please do correct me if there is any flaws, or terms used wrongly.另外,如果有任何缺陷或使用错误的术语,请纠正我。 I'm asking here because I want help, and I'll take any help offered, whether it's on- or off-topic.我在这里问是因为我需要帮助,我会接受所提供的任何帮助,无论是关于主题还是离题。 However, I would appreciate if it's given in a properly and constructive manner, please.但是,如果以适当和建设性的方式提供,我将不胜感激。

Edit: holy sht, I didn't even realize I had written that much.编辑:天啊,我什至没有意识到我写了那么多。 To whoever reads all of this, you're the real MVP!对于阅读所有这些内容的人来说,您是真正的 MVP!

It's possible some of the regions in your game won't have just one tile, they may have multiple tiles on top of each other.游戏中的某些区域可能不会只有一个图块,它们可能有多个图块相互叠加。

For example, a table that stands on the floor so you can see both floor tile and semi-transparent table tile.例如,一张放在地板上的桌子,这样您就可以看到地砖和半透明的桌砖。 This means a simple 2d array of tiles won't be enough in that case.这意味着在这种情况下,简单的二维瓷砖阵列是不够的。
Perhaps a list of tile instances each having it's draw coordinates?也许是一个瓦片实例列表,每个都有它的绘制坐标? Then you just loop through the list and draw tiles on the screen in whatever order they go.然后,您只需遍历列表并按照它们的顺序在屏幕上绘制图块。

I don't know if it still relevant but anyway.我不知道它是否仍然相关,但无论如何。 From my experience, definitely use Tile[,] and not just int[,], think about the future (especially when your are building engines, which have to be very generic), maybe someone will want the tile to be animated or enter some logic to the tile.根据我的经验,一定要使用 Tile[,] 而不仅仅是 int[,],想想未来(特别是当你正在构建引擎时,必须非常通用),也许有人会希望 tile 动画或输入一些瓷砖的逻辑。

What you can do is build the tilemap using int[,] but it is way better to use XML.您可以做的是使用 int[,] 构建 tilemap,但使用 XML 更好。 tip: instead of looping on TileID or using a long switch-case (or a lot of ifs ofc), you can store them in a dictionary (which is c# implementation of HashMap) and then you'll get them with complexity of O(1) instead O(n) (Which might be crucial and might not, depends on how much tiles you got).提示:不是在 TileID 上循环或使用长 switch-case(或大量 ifs ofc),您可以将它们存储在字典中(这是 HashMap 的 c# 实现),然后您将获得复杂度为 O( 1) 取而代之的是 O(n)(这可能很关键,也可能不是,这取决于你得到了多少瓷砖)。 You can store in a dictionary the delegate the the creating function or so.您可以将创建函数的委托存储在字典中。

The main issue with not using Tile[,] or int[,] is the collision detection.不使用 Tile[,] 或 int[,] 的主要问题是碰撞检测。 You will have to loop on the entire collection of tiles, instead of just calculate the correct indexes (or range of indexes).您将不得不循环整个图块集合,而不仅仅是计算正确的索引(或索引范围)。 The calculation should look like this j = x / tilewidth;计算应如下所示j = x / tilewidth; (Again O(1) instead of O(n), here it is very crucial). (同样是 O(1) 而不是 O(n),这里非常关键)。

If you want to have more than one layer on your tilmap (for tables, chairs, plants, etc..) you can create Layer class (which should contain Tile[,]) or use Tile[,,,] where the first index is the layer.如果您想在 tilmap 上有多个图层(用于桌子、椅子、植物等),您可以创建图层类(应包含 Tile[,])或使用 Tile[,,,] 其中第一个索引是层。 I think the first is better because the last is more complex to work with and less generic.我认为第一个更好,因为最后一个更复杂,更通用。

You could do both !你可以两者兼得!

Store your "general tile data" somewhere, and store map-tile specific data (data related to 1 tile in the map, could be called MapTile or something) in a list.将您的“一般瓦片数据”存储在某处,并将地图瓦片特定数据(与地图中的 1 个瓦片相关的数据,可以称为 MapTile 或其他名称)存储在列表中。

So, your Tile class would hold the sprite, the "walkable" state and such, and your MapTile would hold a reference to it's tile model, it's position in the map, it's layer, and maybe it's state, in short, any data unique to this specific tile in the map.所以,你的 Tile 类将保存精灵、“可行走”状态等,而你的 MapTile 将保存对它的瓷砖模型的引用,它在地图中的位置,它的图层,也许它的状态,简而言之,任何唯一的数据到地图中的这个特定图块。

When you want to draw your tiles, you just scroll through the list of MapTile, and draw the model related at the position of the tile.当你想绘制你的瓷砖时,你只需滚动MapTile列表,并在瓷砖位置绘制相关的模型。

This way, you reduce memory usage and still have the functionality of the Tile[,] method.这样,您可以减少内存使用量,并且仍然具有 Tile[,] 方法的功能。

I would suggest to use ints.我建议使用整数。 If your different tiles are less than 65.000 I'd suggest you use ushorts.如果您的不同瓷砖小于 65.000,我建议您使用 ushorts。

Let's assume you want 8000x2000 Tiles.假设您想要 8000x2000 的瓷砖。 Thats equals to 16.000.000.这等于 16.000.000。

If you choose to store References (64 bit) then you'd occupy 16.000.000*64 = 1024000000 bits (~ 1000 MB RAM)如果您选择存储引用(64 位),那么您将占用 16.000.000*64 = 1024000000 位(~ 1000 MB RAM)

If you choose to store ints(32bit) then you'd occupy 16.000.000*32 = 512000000 bits (~ 500 MB RAM)如果您选择存储整数(32 位),那么您将占用 16.000.000*32 = 512000000 位(~ 500 MB RAM)

If you choose to store uShorts(16bit) then you'd occupy 16.000.000*16 = 256000000 bits (~ 250 MB RAM)如果您选择存储 uShorts(16bit),那么您将占用 16.000.000*16 = 256000000 位(~ 250 MB RAM)

In my oppion it's worth to invest the extra CPU time to do a lookup in your Tile array but this depends on your situation.在我看来,投入额外的 CPU 时间来在 Tile 阵列中进行查找是值得的,但这取决于您的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM