-
Notifications
You must be signed in to change notification settings - Fork 0
Core.GridSystem.SpatialEntityGrid
The class is a Spatial Partitioning system. Its primary purpose is to optimize queries in a 2D world—specifically for collision detection—by dividing the world into a grid of "cells." SpatialEntityGrid
Instead of checking every object against every other object (which is very slow), you only check objects that occupy the same or neighboring grid cells.
-
Grid Division: The world is divided into a grid where each cell has a fixed size ().
CellSize -
Indexing: Every object () has a . The grid calculates which cells that box overlaps using simple math:
gridX = (int)(position.X / CellSize).IGridItem``BoundingBox - : The dictionary stores a list of items for every active cell coordinate . Storage
_grid``(x, y) -
Tracking: It keeps track of so that when an object moves, the grid can efficiently remove it from its old cells and add it to the new ones only when necessary.
_previousPositions
In a typical collision system, acts as the Broad Phase. SpatialEntityGrid
- Broad Phase (Optimization): The grid quickly identifies a small subset of objects that might be colliding because they are in the same area.
- Narrow Phase (Accuracy): You then perform the expensive, precise math (like pixel-perfect or geometric intersection) only on those few objects returned by the grid.
Without this grid, if you had 1,000 entities, you would need to perform 1,000,000 checks every frame. With a grid, you might only perform 5 or 10 checks per entity.
If you have a fast-moving bullet, you don't want to check it against every wall and enemy in the entire game level.
// Inside a Collision System or Bullet Update
public void CheckBulletCollision(Bullet bullet, SpatialEntityGrid spatialGrid)
{
// Query the grid for anything inside the bullet's current bounding box
HashSet<IGridItem> potentialTargets = spatialGrid.Query(bullet.BoundingBox);
foreach (var target in potentialTargets)
{
// Skip self
if (target == bullet) continue;
// Perform the actual collision logic (Narrow Phase)
if (bullet.BoundingBox.Intersects(target.BoundingBox))
{
bullet.OnHit(target);
break;
}
}
}If an explosion happens at a specific point, you can find all entities affected instantly.
public void Explode(Vector2 position, float radius, SpatialEntityGrid spatialGrid)
{
// Create a bounding box for the explosion area
RectF explosionArea = new RectF(
position.X - radius,
position.Y - radius,
radius * 2,
radius * 2
);
// Get only the entities near the explosion
var affectedEntities = spatialGrid.Query(explosionArea);
foreach (var entity in affectedEntities)
{
// Apply damage based on proximity
float distance = Vector2.Distance(position, entity.Position);
if (distance <= radius)
{
entity.TakeDamage(100 * (1 - distance / radius));
}
}
}To keep the collision system accurate, entities must notify the grid when they move.
public class Player : IGridItem
{
public RectF BoundingBox { get; private set; }
public void Move(Vector2 velocity, SpatialEntityGrid grid)
{
// Update the internal position/box
this.BoundingBox = new RectF(newPos.X, newPos.Y, width, height);
// Tell the grid to re-index this object based on its new position
grid.Update(this);
}
}-
Thread Safety: It uses and , meaning your collision detection could potentially run on multiple CPU cores without crashing.
ConcurrentDictionary``ThreadLocal -
Memory Efficiency: By using lists for queries, it avoids creating thousands of temporary list objects every second, reducing "Garbage Collection" stutters in your game.
ThreadLocal