Skip to content

Core.GridSystem.SpatialEntityGrid

Dennis Steffen edited this page Jan 4, 2026 · 1 revision

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.

How it Works

  1. Grid Division: The world is divided into a grid where each cell has a fixed size (). CellSize
  2. Indexing: Every object () has a . The grid calculates which cells that box overlaps using simple math: gridX = (int)(position.X / CellSize). IGridItem``BoundingBox
  3. : The dictionary stores a list of items for every active cell coordinate . Storage_grid``(x, y)
  4. 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

Use in a Collision System

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.

Examples

1. Finding potential collision partners for a Bullet

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; 
        }
    }
}

2. Efficient "Area of Effect" (AoE) Damage

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));
        }
    }
}

3. Updating the Grid when an Entity moves

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);
    }
}

Key Benefits for your Project:

  • 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

Clone this wiki locally