我习惯用 PHP 编码,但我不太精通 Java,这已经成为一个问题有一段时间了。我希望这是一个相当简单的解决方案,但是我无法以任何方式找到任何好的示例代码,所以这里是:
我正在编写一个游戏,该游戏发生在基于图块的地图上的 2d 随机生成的无限世界中(挑剔) :我知道它不会是真正无限的。我只是希望世界很大)。 map[x][y] 多维数组的常用方法一开始是一个基本思想,但由于 Java 没有像 PHP 那样提供非整数(即负数)数组键恶作剧的方法,所以我不能正确地拥有 (- x,+x,-y,+y) 带有数组键的坐标系。
我需要能够在特定 x,y 坐标处找到图块上的对象,以及找到某个图块的“相邻图块”。 (如果我可以 getObjectAt(x,y),我可以 get(x+1,y) 等等,这很简单)
我已经阅读了有关四叉树和 R 树等的内容。这个概念很令人兴奋,但是我还没有看到任何好的、简单的 Java 示例实现。此外,我不确定这是否正是我所需要的。
欢迎任何建议
谢谢
I am used to coding in PHP but I am not really proficient with Java and this has been a problem for some time now. I expect it to be a fairly easy solution, however I cannot find any good example code any way I search it, so here goes:
I am programming a game that takes place in a 2d random generated infinite world on a tile based map (nitpicking: I know it will not be truly infinite. I just expect the world to be quite large). The usual approach of map[x][y] multidimensional array started out as a basic idea, but since Java does not provide a way for non-integer (i.e. negative) array key shenanigans like PHP does, I cannot properly have a (-x,+x,-y,+y) coordinate system with array keys.
I need to be able to find the objects on a tile at a specific x,y coordinate as well as finding "adjacent tiles" of a certain tile. (Trivial if I can getObjectAt(x,y), I can get(x+1,y) and so on)
I have read about quad trees and R-trees and the like. The concept is exciting, however I haven't seen any good, simple example implementation in Java. And besides I am not really sure if that is what I need exactly.
Any advice is welcome
Thank you
发布评论
评论(12)
1) 除了数组之外,您还可以使用
Map>
或Map
,这当然允许负数索引2) 如果您从一开始就知道世界的维度,您只需修改 getter 即可允许 API 接受负值并[线性]将它们转换为正值。例如,如果您的世界是 100x1000 个图块,并且您想要 (-5,-100),则您将拥有
WorldMap.getTile(-5,-100)
,这将转换为returntileArray[ x+mapWidth/2][y+mapHeight/2];
即 (45,400)1) Instead of an array you could use a
Map<Integer, Map<Integer, Tile>>
orMap<Point, Tile>
, which would of course allow negative indexes2) If you know the dimensions of your world from the start you could just modify your getter to allow the API to accept negatives and [linearly] transform them into positives. So for example if your world is 100x1000 tiles and you want (-5,-100), you would have
WorldMap.getTile(-5,-100)
which would translate toreturn tileArray[x+mapWidth/2][y+mapHeight/2];
which is (45,400)我遇到了同样的问题,但我的解决方案是使用 Map/HashMaps< /a>,但这些是一维的。
为了克服这个问题,我没有使用地图中的地图(这会很混乱且效率很低),而是使用通用的 Pair 类(不是你在库存 java 库中找到的东西),尽管你可以用 Position 类替换它(实际上相同的代码,但不是通用的,而是整数或浮点数)。
所以定义地图时:
Map tiles = new HashMap;
为了将图块对象放置到地图上,我使用了
tiles.put(new Pair(x, y), new GrassTile());
并用于检索对象tiles.get(new Pair(x, y));
。[x/y 可以是您想要放置的任何坐标(这允许负坐标,不会造成任何混乱!),“new GrassTile()”只是在地图期间放置某种类型的图块的示例创建。显然 - 如前所述 - Pair 类是可替换的。]
您可能会问为什么不是 ArrayLists?因为数组列表比映射更加线性,并且在我看来,添加和检索图块更加困难,尤其是在二维上。
更新:
对于任何想知道为什么 Java 中没有 Pair() 类的人,这里有一个 解释。
I came to this thread with the same problem, but my solution was to use Map/HashMaps, but these are one dimensional.
To overcome this, instead of using a map within a map (which would be messy and very inefficient) I used a generic Pair class (not something that you'll find in the stock java library) although you could replace this with a Position class (virtually the same code, but not generic, instead integers or floats).
So when defining the map:
Map<Pair, Tile> tiles = new HashMap<Pair, Tile>;
For placing tile objects onto the map I used
tiles.put(new Pair(x, y), new GrassTile());
and for retrieving the objecttiles.get(new Pair(x, y));
.[x/y would be any coordinate you wish to place (this allows negative coordinates without any mess!), "new GrassTile()" is just an example of placing a tile of a certain type during map creation. Obviously - as previously stated - the Pair class is replacable.]
Why not ArrayLists you may ask? Because array lists are much more linear than mapping, and in my opinion are more difficult to add and retrieve tiles, especially on 2 Dimensions.
Update:
For anyone wondering why there isn't a Pair() class in Java, here's an explanation.
树、四叉树、二叉树、红树和黑树 - 以及所有其他种类的树对你来说都是无用的(除非你计划拥有一张包含巨大森林的地图)。
专门的数据结构有其特定的用途。除非您能找出游戏需要空间索引的充分理由,否则不要构建空间索引。如果您的典型场景是“迭代可见区域,找出每个方块上可见的图块”,那么您需要一个结构来快速、随机地访问存储在特定键下的值。这样的结构就是 HashMap(PHP 使用的是一种 LinkedHashMap,但您可能没有使用“链接”部分)。
您需要遵循 xephox 的建议(并给予他信任),即:
最好的事情是:如果您继续使用地图界面,您将不会被锁定,并且您将能够做出很多改进。就像将 HashMap 包装到一个对象中,该对象使用一些算法技术创建映射的一部分。
Trees, Quad Trees, Binary trees, red and black trees - and all other kinds of trees are USELESS for you (unless you are planning to have a map with a huge forest).
Specialized data structures have their specific uses. Unless you can come up with a good reason why your game needs a spatial index, don't build one. If your typical scenario is "iterate over the visible area, find out what tile is visible at each of the squares", then you need a structure that gives you a quick, random, access to a value stored under a specific key. Such structure is a HashMap (what PHP uses is a kind of a LinkedHashMap, but you were probably not using the "linked" part).
You need to follow xephox's advice (and give him the credit), and that is:
The best thing: if you keep using the Map interface, you will not be locked out, and you will be able to make a lot of improvements. Like wrapping the HashMap into an object that creates parts of the map using some algorithmic techniques.
我不是游戏编程专家,但如果数组没问题,您可以简单地将坐标从 (-x, +x) 转换为 (0, 2x)(同上,对于 y 轴)。
或者,如果您习惯像 PHP 那样的关联数组,则可以使用 Java 中的相应结构,即 Map(HashMap 就可以了):使用适当的 equals 和 hashCode 方法定义一个
Cooperative
类,并使用HashMap<坐标>
。使坐标不可变可以使代码更加健壮,并允许缓存 hashCode。I'm not an expert in game programming, but if arrays are OK, you could simply translate your coordinates from (-x, +x) to (0, 2x) (idem for the y axis).
Or if you're used to associative arrays like PHP has, the use the corresponding structure in Java, which is a Map (HashMap would be OK) : define a
Coordinate
class with appropriate equals and hashCode methods, and use aHashMap<Coordinate>
. Making Coordinate immutable makes the code more robust, and allows caching the hashCode.你可以尝试使用QuadTree(这里是一个很好的例子:http:// www.mikechambers.com/blog/2011/03/21/javascript-quadtree-implementation/)
you could try a QuadTree (nice example here: http://www.mikechambers.com/blog/2011/03/21/javascript-quadtree-implementation/ )
将你的地图分成块怎么样(是的,Minecraft 粉丝,我也知道它用在哪里:P)?因此,您有两个坐标系,它们具有相同的原点:
x/y
坐标c1/c2
块坐标块始终是现实世界坐标的固定大小(例如 128x128) )。然后你有一个类
Chunk
,其中有一个固定数组(128x128),其中包含每个像素的所有信息。并且您将块存储到Map
中,正如其他人已经解释的那样。我会推荐一个HashMap
。每当你的玩家位于某个区域时,就会从地图中加载必要的块,然后你就可以访问那里的固定大小的数组。如果块知道它在 x/y 坐标中的位置,您甚至可以拥有一些支持函数,例如 Pixel Chunk#getPixel(long x, long y) 等...
顺便说一句:这也为您提供了一种简单的方法来推迟整个世界的生成,直到真正需要为止:开始时,不会生成任何内容,并且一旦在地图中访问了
Chunk
,尚未生成,您可以直接生成它。或者,如果对您来说更容易的话,您可以在启动时填写它。 (填充一个无限的世界需要很长时间,即使它是伪无限的)How about dividing your map into chunks (yes, Minecraft fans, I know where this is used as well :P)? So you have two coordinate systems, both with the same origin:
x/y
coordinatesc1/c2
chunk coordinatesA chunk is always a fixed size of real world coordinate (say 128x128). Then you have a class
Chunk
where you have a fixed array (128x128) with all the information for every pixel. And you store your chunks into aMap<ChunkCoordinates, Chunk>
as was already explained by others. I would recommend aHashMap
.Whenever your player is in a certain region, the neccessary chunks are loaded from the map and then you can access the fixed size array in there. If the chunk knows, where it is placed in
x/y
coordinates, you can even have some support function likePixel Chunk#getPixel(long x, long y)
or so...Btw: This also gives you an easy way to postpone generation of the whole world until it is really needed: At start, nothing is generated and as soon as a
Chunk
is accessed in the map, that is not yet generated, you can just generate it then. Or you could fill it up at startup if that's easier for you. (filling an infinite world will take a long time though, even if it is pseudo infinite)我用 Java 编写了一些您可能感兴趣的实验性备用数据结构。
最有趣的是 Octreap 我认为这是 Treap 和 八叉树 ,它具有以下功能:
I wrote a couple of experimental spare data structures in Java that you might be interested in.
The most interesting one was the Octreap which is what I believe is a completely novel cross between a Treap and an Octree, which had the following features:
您可能想使用 Map 的实现。 HashMap、SortedMap 等取决于您打算存储多少数据和您的访问模式(Sorted Map 非常适合顺序访问,HashMap 更适合随机访问)。
您可以使用二维映射,也可以将二维索引转换为一维映射的键。
You probably want to use an implementation of Map. HashMap, SortedMap, etc depending on how much data you intend to store and your access patterns (Sorted Map is very good for sequential access, HashMap is better for random access).
You can either use two-dimensional Maps or munge your 2-d indeces into a key for a 1-d Map.
这是两个独立的问题:如何模拟负数组索引,以便拥有“无限”地图,以及如何有效地存储图块。
关于第一个问题,一个技巧是维护四个独立的矩阵(矩阵?),每个象限一个。那么所有的指标都可以为正。
关于第二个问题,你需要一张稀疏地图。一种不是很有效的方法是使用哈希图的哈希图。事实上,这也可以解决第一个问题:
您可以执行自己的 Map 实现,将整数作为键,并且效率要高得多。
This is two separate questions: how to simulate negative array indicies so you can have an "infinite" map, and how to store tiles efficiently.
On the first question, one hack would be to maintain four separate matricies (matrixes?), one for each quadrant. Then all the indexes can be positive.
On the second question, you need a sparse map. One not-very-efficient way is to have a hashmap of hashmaps. In fact, that could solve the first problem as well:
You could do your own Map implementation that took integers as keys and was much, much more efficient.
如果您想要真正快速且真正可扩展,那么一定要使用稀疏八叉树。
我不知道有任何实现Java,不过是微不足道的。只需在单个字节中存储当前链接节点的位字段,并使用链接列表来保存这些引用(对于每个八叉树节点,有一个最多包含 8 个条目的单独列表)。
If you want to be really fast and really scalable definitely go with a Sparse Octree.
I am not aware of any implementations in Java, but it is trivial. Just store in a single byte a bitfield for which nodes are currently linked and use a linked list to keep those references (for each Octree-node a separate list of max. 8 entries).
哈希图样式的解决方案对于邻接计算来说非常糟糕,它们需要整个数据集的迭代。
像四叉树或八叉树这样的东西是完美的,除了它不是无限的,它是任意大小的(那里存在差异)。
然而,如果你仔细想想,ArrayList 并不是无限的,它只是任意增长的大小,对吧?
因此,四叉树是稀疏的,并且可以很好地进行广告邻接计算,除了“无限”规定之外,它是完美的。为什么不直接使用您认为可能需要的 100 倍大小的一个(它很稀疏,并不是什么大问题)。如果您到达接近边缘的位置,请分配一个更大的新四叉树。
我相信,如果你小心的话(你可能必须实现你自己的四叉树),你可以用很少的努力来“升级”四叉树,并且不需要复制——这应该只是在所有现有地址前加上一些位的前缀(地址是二进制的,四叉树的每一位都表示将现有宇宙在一个维度或另一个维度上一分为二)。
The hashmap style solutions are terrible for adjacency calculations, they require an iteration of the entire dataset.
Something like a quadtree or octree is perfect EXCEPT that it's not infinite, it's an arbitrary size (world of difference there).
However if you think about it, an ArrayList isn't infinite, it's just an arbitrary size that grows, right?
So a quadtree is sparce and pretty good ad adjacency calculations, except for the "infinite" provision it's perfect. Why not just use one of those sized to 100x what you think you might need (it's sparse, not really a big deal). If you ever get to the point where you are near the edge, allocate a new quadtree that is much bigger.
I believe if you are careful (you may have to implement your own quadtree) you can "upgrade" the quadtree with very little effort and no copying--it should be simply a matter of prefixing all your existing addresses with some bits (the addresses are in binary, quadtrees each bit represents dividing the existing universe in half in one dimension or the other).
使用 apache commons 中的 Pair 类。它有一个左值和一个右值,将 x 和 y 坐标实现为左/右。每对也分别根据其左值和右值进行哈希处理。
然后,您可以使用称为坐标的对的哈希集(Set<>)。这样,每个坐标都应该是唯一的。如果要将任何对象链接到坐标,则可以使用坐标及其相关对象的 HashMap。
Use the Pair class from apache commons. It has a left and a right value, implement x and y coordinate as left/right. Each Pair is also hashed based on its left and right value respectively.
You could then use a HashSet of Pairs (Set<<Pair<Integer, Integer>>) called coordinates. In this way, each coordinate will be unique, as it should be. If you want to link any Object to a coordinate, you can then use a HashMap of the coordinates with their relevant objects.