I've known for awhile that it's better to index everything into a giant grid and then query said grid for entities, than querying the entire world. I do have a few questions left unanswered, and I hope some of you can shed light on this.

I’ve tried to write this in an understandable way, and I even provide some illustrations.

A perfect situation of querying a grid, would be this:The circle represents your entity, and it should be clear that the entity is indeed in cell (1, 1).

It’s easy to ask (in code) “Hey! Is anything in the way, over at (1, 1)”, and it’s easy to find out that the circle is there.

Consider this next scenario:The circle is the same. It still fits within one square, but it’s not entirely inside a single square. This scenario is possible within a ton of games, but how do we handle it?What cell is it actually inside? If we decide that it’s still (1, 1), then what happens if we need to check if there are no collisions in cell (2, 1)? Obviously the outcome isn’t great, because the game won’t recognize anything in the (2, 1) area.

Here’s an actual question: With the above in mind, do I want duplicate entries in the grid? If yes, I no longer have to worry about -||-I always thought that I didn’t want duplicate entries, and so I’ve had a few thoughts on how to handle the problem.

Here’s one: I can query multiple cells, based on how big my entities are. Let’s say that my largest entity, is equal to the size of the blue circle:

Now, if I want to query cell (3, 3), I need to do it like this:

Because my largest entity can leak over 9 cell, I need to query the 8 cell around the starting point to make sure I don’t miss anything (provided that there is only one entry of each entity). That makes performance a lot worse.

Is there any ways around this problem? Is there an easy way to find out what cells an entity is touching, if the entities center vector, and radius is available?

Also, how is pathfinding in an environment like this possible? I would make targets at the center of each cell, and just use the heuristic to get my entity there. That makes it easy to apply A* for instance. However, that way it can easily become difficult to find a valid path. I don’t want to avoid a cell, just because the edge of an entity is slightly leaking into it. That doesn’t pose an actual obstacle, especially if the mover is a much smaller circle, that can easily maneuver past the edge of the leaking entity, without leaving the cell. I’ve illustrated this below.

Another thing is, how does one query collisions accurately, in this environment? Say, I want to know whether a small circle is intersecting with anything, but it’s radius is a cell’s width 1/20 (like a bullet in a game).

For completely practical reasons: How big should each cell be? Not too small because it’ll be slower, but not too big either. Is there any general rule of thumb, like equal to the size of my smallest entity?

I know a bunch of you have been working with an environment like this, so please enlighten me.

An entity that spans multiple cells is in all of them. Finding out which cells an entity touches is as simple as making a bounding box around it -- this should be pretty trivial for circles. This will result in some false positives on corners, but the idea isn't for an exact position, it's just to narrow down the list of entities you do more precise comparisons with.

A cell should be "big enough" to partition your space so that most cells contain only one or two objects, but that you're not wasting space with empty cells between objects or having to inhabit several cells for every entity. You should consider dynamically adjusting it and benchmarking to see which arrangement fits best.

Big question: What sort of game will this be for? If it's a platformer, then there will be slightly different rules for how you design the node-set for your game (Pathfinding it is about the same, however). If it's for something that's relatively top-down, or where movement occurs unit by unit and only in strict compass directions (N-NE-E-SE-S-SW-W-NW) or something similiar, then your grid can be used for pathfinding.

Either way, a lot deals with how your movement rules work. Here are some things to consider (Which weren't made too obvious by your post that you should think about. Sorry if I'm just not understanding your intention and mentioning things you felt answered!)

0) Are movements/positions going to be grid-aligned at all?1) How do entities move? Platform: Do jumps have a calculable height/distance that they can typically be expected to attain, are there different 'rules' for walking across different spaces, etc. Actual Grid: Is it a single 'step' per turn (Think a rogue like) or is do they have a moment range (Think Final Fantasy Tactics) per turn?2) Decide whether entities can coexist on a given grid square. If it's a strict no then this makes things pretty straight forward. If it's a yes then you need to decide how you're going to handle overlaps and the like.3) Like Sprongie said, your grid should be just the right size. Selecting the smallest entity that you possess and using that to designate your grid side could work, or a grid square that is the smallest possible movement distance per turn. This one depends on question 1, in that you'll have some sort of trade off. If you select the grid by entity, but you have movement increments smaller than that, you'll end up having an entity straddling several squares that way. If you select the grid by movement, but your units are all bigger than the smallest unit, then all units will straddle several squares simply due to size.

Why are these important?Number of Paths Needed: Depending on how your movement system works, your entities are likely going to have to use the pathfinder quite a few times, perhaps as often as once per turn. For 'Step' method this is mainly to do with passable squares becoming impassable (See question 2) and can result in a form of thrashing (Think when you're walking towards someone and you both step to the side to let the other pass). For the 'range' you'll typically still end up needing to find paths as often, however the chance of the thrash mentioned above is less likely.Path accuracy: Depending on how you're handling overlap, you can end up with lots of sub-optimal paths (Which typically aren't too much of an issue). This can get better/worse if you only find a new path when there's an interruption on the current one or when you change targets.

Either way, a lot deals with how your movement rules work. Here are some things to consider (Which weren't made too obvious by your post that you should think about. Sorry if I'm just not understanding your intention and mentioning things you felt answered!)

0) Are movements/positions going to be grid-aligned at all?

You nailed it, and I admit I left out on quite a few things. I'm going to let you imagine how movement works for "TLOZ: A Link To The Past", and assume you know your classics Each entity is not blocking (In Zelda it was only imaginary because Link jumps back), and they are not aligned to the grid. They move completely independently of the grid, and eachother (in theory - I can still do a BB-overlap test, within the cell). I thought I could make pathfinding work with the cells though (imagine the cell size as big as the tiles in Zelda), and just use trig to guide an entity towards the middle of each tile/node, until the node is reached. Then proceed to the next node. This should work great, but it doesn't take into account other entities. It'll just check whether or not a tile is occupied, and it could be occupied by just the nosetip of some skeleton, which is inaccurate if the entity is small enough. Else, I can disregard entities, but then I can't have any collidable entities. I'd like to have that, since I might have boulders and other things to interract with.

/* * Copyright (c) 2003-onwards Shaven Puppy Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'Shaven Puppy' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */packagepuppytron;

/** Map of Entities to ReadableRectangles; this maps Entities to the cells in which they have been placed */privateMap<Entity, ReadableRectangle> entityMap = newHashMap<Entity, ReadableRectangle>(INITIAL_ENTITIES);

Thank you so much Cas, that will definitely come in handy. I do find that quite hard to look at, and it doesn't seem apparent which segments executes when. I'm not sure I get the "pair" sentiment, and I'm definitely not going to resize my grid over any period of time just yet (I have little idea when to, and what sizes are desirable).

I want to understand your code, but I think a lot of understandability (In lack of a better word) went down the drain as performance improved (That's usually how it goes ).

It's not exactly intended for public consumption but it might give you the gist of how you might implement it yourself That particular implementation assumes all entities are circular, and every time an entity is moved or its radius changes, you must remove() the entity and then store() it again. You call checkCollisions() once per tick to process all collisions, after all movement has taken place. There are a couple of other checkCollisions() methods to query the grid and return results as well.

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org