CrackedGrids Documentation

A comprehensive 2D isometric grid-based game framework for Unity with dynamic grid generation, object placement, pathfinding, and visual feedback systems.

Quick Start

Minimal Setup

  1. Create an empty GameObject and add the GridManager component
  2. Configure grid dimensions (default: 7x7)
  3. Add cell prefabs to the cellPrefabs list
  4. Click "Initialize Grid" in the inspector
  5. Add IsometricCameraController to your Main Camera
using CrackedGames.Grid;

public class GameSetup : MonoBehaviour
{
    [SerializeField] private GridManager gridManager;
    [SerializeField] private GridObject playerPrefab;

    void Start()
    {
        // Initialize grid
        gridManager.InitializeGrid(7, 7);

        // Spawn player
        var player = Instantiate(playerPrefab);
        gridManager.RegisterObject(player, new GridPosition(3, 3));
    }
}

Core Concepts

Namespaces

using CrackedGames.Grid;       // Core grid functionality
using CrackedGames.Attributes; // Utility attributes (ReadOnly)

GridPosition

A lightweight struct representing a position on the grid using integer X, Y coordinates.

// Creating positions
GridPosition pos = new GridPosition(3, 4);
GridPosition origin = GridPosition.Zero;

// Direction constants
GridPosition.Up        // (0, 1)
GridPosition.Down      // (0, -1)
GridPosition.Left      // (-1, 0)
GridPosition.Right     // (1, 0)
GridPosition.UpLeft    // (-1, 1)
GridPosition.UpRight   // (1, 1)
GridPosition.DownLeft  // (-1, -1)
GridPosition.DownRight // (1, -1)

// Direction arrays
GridPosition.CardinalDirections // Up, Down, Left, Right
GridPosition.DiagonalDirections // UpLeft, UpRight, DownLeft, DownRight
GridPosition.AllDirections      // All 8 directions

// Arithmetic
GridPosition newPos = pos + GridPosition.Up;
GridPosition offset = pos - other;
GridPosition scaled = pos * 2;

// Distance calculations
int manhattan = pos.ManhattanDistance(other);
float euclidean = pos.EuclideanDistance(other);
bool adjacent = pos.IsAdjacent(other);

// Conversions
Vector2Int v2 = pos.ToVector2Int();
Vector3 worldPos = pos.ToWorldPosition(cellSize: 1f);

GridCell

Represents a single cell in the grid. Manages occupancy, walkability, and highlighting.

GridCell cell = gridManager.GetCell(position);

// Properties
cell.Position      // GridPosition
cell.IsWalkable    // Can units walk here?
cell.IsOccupied    // Are objects on this cell?
cell.IsBlocked     // Not walkable OR contains blocking object
cell.IsAvailable   // Walkable AND not blocked

// Object management
cell.AddObject(gridObject);
cell.RemoveObject(gridObject);
List<GridObject> objects = cell.GetObjects();

// World position
Vector3 worldPos = cell.GetWorldPosition();

Grid Setup

GridManager Configuration

PropertyDefaultDescription
gridWidth7Number of columns
gridHeight7Number of rows
cellSize1.0World size of each cell
gridOrigin(0,0,0)World position of grid origin
erosionChance0.2Probability of edge erosion (0-1)
erosionIterations1Number of erosion passes
erosionDither0.1Erosion increase per iteration
cellPrefabs-Weighted list of cell prefabs
wallGenerator-Reference to wall generator
showGridGizmostrueShow grid lines in scene view

Initialization

// Initialize with specific dimensions
gridManager.InitializeGrid(10, 10);

// Clear and rebuild
gridManager.ClearGrid();   // Remove cells only
gridManager.ClearAll();    // Remove cells and walls

// Apply procedural erosion
gridManager.ApplyIrregularity();

Coordinate Conversion

// Grid to world
Vector3 worldPos = gridManager.GetWorldPosition(gridPosition);

// World to grid
GridPosition gridPos = gridManager.WorldToGridPosition(worldPosition);

// Get grid center
Vector3 center = gridManager.GetCenterPosition();

Grid Objects

CrackedGrids uses a component-based architecture for grid objects. The system is split into two main components:

  • GridObject: Handles grid state (position, blocking, interactions)
  • GridMovement: Handles movement animations (optional)

This separation allows static objects (obstacles, items) to use only GridObject, while moving entities add GridMovement for animated transitions.

GridObject

Base component for any object that exists on the grid. Handles position tracking, blocking state, and basic placement.

public class MyUnit : GridObject
{
    protected override void Awake()
    {
        base.Awake();
        // Custom initialization
    }
}

Inspector Properties

PropertyDefaultDescription
isBlockingtrueBlocks other objects from entering this cell
isPickabletrueCan be picked up/interacted with
isImmovablefalseCannot be pushed or knocked back
groundHeight0.0Y offset from ground level

Runtime Properties

GridPosition pos = myObject.GridPosition;  // Current position (read-only)
bool blocks = myObject.IsBlocking;
bool pickable = myObject.IsPickable;
bool immovable = myObject.IsImmovable;
float height = myObject.GroundHeight;
Vector3 offset = myObject.VisualOffset;    // For centering large objects

Events

// Fired when movement starts (position is already updated)
myObject.OnMoveStart += (targetPos) => { };

// Fired when movement animation completes
myObject.OnMoveComplete += (targetPos) => { };

Visual Offset

For objects that need visual centering (e.g., a large boss spanning multiple cells), override visualOffset in your subclass:

public class LargeBoss : GridObject
{
    protected override void Awake()
    {
        base.Awake();
        visualOffset = new Vector3(0.5f, 0, 0.5f); // Center on 2x2 area
    }
}

GridMovement

Optional component that provides animated movement for GridObjects. Add this component to any GridObject that needs smooth movement transitions.

Requires: GridObject (auto-added via [RequireComponent])

Inspector Properties

PropertyDefaultDescription
moveSpeed5.0Base movement speed (units per second)
jumpHeight2.0Maximum height of jump arc
dashSpeedMultiplier3.0Speed multiplier for dash movement

GridEntity

Extended GridObject for 2D sprite-based entities with billboarding support.

public class MySprite : GridEntity
{
    // Automatically faces camera when billboard = true
}

Additional Properties

PropertyDefaultDescription
billboardtrueSprite faces camera
verticalOffset0.5Y offset for sprite pivot
baseScale(5,5,5)Entity scale

Registering Objects

// Spawn and register
GridObject unit = Instantiate(unitPrefab);
gridManager.RegisterObject(unit, new GridPosition(3, 3));

// Remove from grid
gridManager.RemoveObject(unit);

Object Interactions

// Push an object in a direction (returns false if immovable or blocked)
bool success = target.Push(GridPosition.Right, gridManager);

// Swap positions with another object (instant teleport)
objectA.Swap(objectB, gridManager);

// Teleport instantly (no animation, fires OnMoveComplete)
myObject.TeleportTo(new GridPosition(5, 5));

Movement System

Movement is handled by the optional GridMovement component. Objects without this component will teleport instantly when MoveTo() is called.

Movement Types

public enum MovementType
{
    Walk,      // Smooth easing (smoothstep interpolation)
    Teleport,  // Instant position change
    Jump,      // Parabolic arc (4 * height * t * (1-t))
    Dash,      // Fast with ease-out (cosine interpolation)
    Knockback  // Fixed 0.2s duration with sine ease
}

Component Setup

// For static objects (no animation needed):
// - Add only GridObject component

// For moving units (with animation):
// - Add GridObject component
// - Add GridMovement component

Moving Objects

// Via GridManager (recommended)
gridManager.MoveObject(unit, targetPosition, MovementType.Walk);

// With completion callback
gridManager.MoveObject(unit, targetPosition, MovementType.Jump, () => {
    Debug.Log("Movement complete!");
});

// Direct object movement
unit.MoveTo(targetPosition, MovementType.Dash, OnMoveComplete);

Movement Events

Events are fired on the GridObject, regardless of whether GridMovement is present:

unit.OnMoveStart += (pos) => {
    // Position is already updated at this point
    Debug.Log($"Started moving to {pos}");
};

unit.OnMoveComplete += (pos) => {
    Debug.Log($"Arrived at {pos}");
};

Pathfinding

CrackedGrids includes A* pathfinding with support for diagonal movement.

// Find path between two positions
List<GridPosition> path = gridManager.FindPath(startPos, endPos);

if (path != null && path.Count > 0)
{
    // Path found - execute movement
    StartCoroutine(MoveAlongPath(path));
}
else
{
    Debug.Log("No valid path found");
}

IEnumerator MoveAlongPath(List<GridPosition> path)
{
    foreach (var pos in path)
    {
        bool moveComplete = false;
        gridManager.MoveObject(unit, pos, MovementType.Walk, () => moveComplete = true);
        yield return new WaitUntil(() => moveComplete);
    }
}

Pathfinding Details

  • Uses Manhattan distance heuristic
  • Cardinal movement cost: 10
  • Diagonal movement cost: 14
  • Respects cell walkability and blocking objects
  • Returns null if no path exists

Range Queries

Manhattan Distance (Diamond Shape)

// Get all cells within range 3 (diamond pattern)
List<GridCell> cells = gridManager.GetCellsInRange(centerPos, 3);
List<GridCell> walkableOnly = gridManager.GetCellsInRange(centerPos, 3, onlyWalkable: true);

Chebyshev Distance (Square Shape)

// Get all cells within range 3 (square pattern)
List<GridCell> cells = gridManager.GetCellsInSquareRange(centerPos, 3);

Line Queries

// Get cells in a straight line
List<GridCell> line = gridManager.GetCellsInLine(startPos, GridPosition.Right, maxDistance: 10);

// Get cells until blocked
List<GridCell> path = gridManager.GetStraightPath(startPos, GridPosition.Up);

Neighbor Queries

// Cardinal neighbors only
List<GridCell> neighbors = gridManager.GetNeighbors(position, includeDiagonals: false);

// All 8 neighbors
List<GridCell> allNeighbors = gridManager.GetNeighbors(position, includeDiagonals: true);

Grid Validation

// Check if position is within grid bounds
bool valid = gridManager.IsValidPosition(position);

// Check if cell can be entered
bool available = gridManager.IsCellAvailable(position);

// Check if cell has objects
bool occupied = gridManager.IsCellOccupied(position);

Highlighting System

Highlight Types

public enum HighlightType
{
    None,       // No highlight
    ValidMove,  // Blue glow for valid movement
    Attack,     // Red glow for attack range
    Danger,     // Orange-red for threat zones
    Path,       // Cyan for pathfinding
    Hover       // Light gray for mouse hover
}

Cell Highlighting

// Set highlight type
cell.SetHighlight(HighlightType.ValidMove);
cell.SetHighlight(HighlightType.Attack);

// Custom color highlight
cell.SetHighlight(Color.green, intensity: 2f, pulseSpeed: 1.5f);

// Mark as danger zone (persistent, highest priority)
cell.SetDanger(true);

// Clear highlight
cell.ClearHighlight();

Batch Highlighting

// Highlight multiple cells
List<GridCell> cells = gridManager.GetCellsInRange(pos, 2);
gridManager.HighlightCells(cells, highlight: true);

// Clear all highlights
gridManager.ClearAllHighlights();

GridVisualizer

For advanced visualization with custom colors and curve drawing:

[SerializeField] private GridVisualizer visualizer;

// Highlight with custom color
List<GridPosition> positions = /* ... */;
visualizer.HighlightCells(positions, Color.magenta, intensity: 2.5f, pulseSpeed: 1.5f);

// Draw curves/paths
List<Vector3> curvePoints = /* ... */;
visualizer.DrawCurve(curvePoints, Color.yellow, width: 0.15f);

// Clear
visualizer.ClearHighlights();
visualizer.ClearCurves();
visualizer.ClearAll();

Highlight Priority

Highlights are applied in priority order (highest to lowest):

  1. Danger Zone
  2. Attack
  3. Valid Move / Path / Hover

Procedural Generation

Erosion System

Create organic, irregular grid edges instead of perfect rectangles.

// Configure in inspector or code
gridManager.erosionChance = 0.2f;      // 20% base chance
gridManager.erosionIterations = 2;     // Two passes
gridManager.erosionDither = 0.1f;      // +10% per iteration

// Apply erosion
gridManager.ApplyIrregularity();

Erosion Algorithm

  • Edge cells (1 empty neighbor): 10% of erosion chance
  • Corner cells (2+ empty neighbors): 120% of erosion chance
  • Internal cells: Never eroded
  • Each iteration increases chance by dither value

Safe Spawning

After erosion, use safe spawn methods to avoid placing objects on unstable edges:

// Get main connected area
List<GridCell> island = gridManager.GetMainIslandCells();

// Get safe spawn position
GridPosition spawnPos = gridManager.GetSafeSpawnPosition(
    island,
    minDistanceFromEdge: 2
);

// Spawn near a target position with randomness
GridPosition nearPos = gridManager.GetSafeSpawnPositionNear(
    island,
    minDistanceFromEdge: 2,
    targetPos: new GridPosition(3, 3),
    randomness: 5
);

// Check distance from void/edge
int distFromVoid = gridManager.GetDistanceFromVoid(position);

Weighted Cell Prefabs

Add variety to your grid by using multiple cell prefabs:

// In inspector, add to cellPrefabs list:
// - CellPrefab1 (weight: 0.7)
// - CellPrefab2 (weight: 0.2)
// - CellPrefab3 (weight: 0.1)

Camera System

IsometricCameraController

Automatically configures the camera for isometric or top-down views.

Camera Styles

public enum CameraStyle
{
    IsometricOrthographic,  // Classic isometric (45, 45) - orthographic
    IsometricPerspective,   // Isometric with depth perspective
    TopDownOrthographic     // Straight down (90, 0) - orthographic
}

Configuration

PropertyDefaultDescription
autoSetuptrueAuto-configure on Awake
cameraStyleIsometricOrthographicView style
isometricRotationX45Pitch angle
isometricRotationY45Yaw angle
cameraDistance12Distance from grid
orthographicSize6Orthographic camera size
sizeMultiplier1.2Extra padding

Runtime Control

[SerializeField] private IsometricCameraController cameraController;

// Manual setup
cameraController.SetupCamera();
cameraController.PositionCamera();

// Focus on specific cell
cameraController.FocusOnGridPosition(position, smoothTime: 0.5f);

// Move camera
cameraController.MoveCameraToPosition(worldTarget, duration: 1f);

// Zoom
cameraController.SetZoom(8f);
float currentZoom = cameraController.GetZoom();

Wall Generation

GridWallGenerator

Generates decorative walls on grid edges to frame the playable area.

Configuration

PropertyDefaultDescription
wallHeight3Number of stacked segments
wallSegmentHeight1.0Height of each segment
wallOffset0.5Distance from cell edge
wallHeightOffset0.0Y offset
wallSprites-Array of sprites for variety
wallMaterial-Material for wall quads
wallTint-Color tint

Usage

[SerializeField] private GridWallGenerator wallGenerator;

// Generate walls (usually done automatically after grid init)
wallGenerator.GenerateWalls();

// Clear walls
wallGenerator.ClearWalls();

Smart Wall Placement

  • Only generates walls in the "back triangle" to avoid obscuring the isometric camera view
  • Automatically detects edges created by erosion
  • Randomizes sprite selection for visual variety

Editor Tools

GridManager Inspector

  • Initialize Grid - Create/recreate the grid
  • Apply Erosion - Apply irregularity to edges
  • Clear All - Destroy all cells and walls
  • Enable Grid Selector - Interactive cell selection in scene view
    • Click cells to inspect them
    • View position, blocked state, occupied state

GridWallGenerator Inspector

  • Generate Walls - Create edge walls
  • Clear Walls - Remove all walls

Scene View Gizmos

GridManager

  • White grid lines showing cell boundaries (toggle with showGridGizmos)

GridCell (when selected)

  • Green wireframe: Walkable cell
  • Red wireframe: Occupied cell
  • Gray wireframe: Wall/blocked
  • Yellow lines: Connection to occupying objects

IsometricCameraController

  • Yellow line from camera to grid center
  • Wireframe sphere at grid center

ReadOnly Attribute

Mark serialized fields as read-only in the inspector:

using CrackedGames.Attributes;

[SerializeField, ReadOnly]
private int calculatedValue;

API Reference

GridPosition

// Constructors
GridPosition(int x, int y)

// Properties
int X { get; }
int Y { get; }

// Static Properties
static GridPosition Zero { get; }
static GridPosition Up { get; }
static GridPosition Down { get; }
static GridPosition Left { get; }
static GridPosition Right { get; }
static GridPosition UpLeft { get; }
static GridPosition UpRight { get; }
static GridPosition DownLeft { get; }
static GridPosition DownRight { get; }
static GridPosition[] CardinalDirections { get; }
static GridPosition[] DiagonalDirections { get; }
static GridPosition[] AllDirections { get; }

// Methods
Vector2Int ToVector2Int()
Vector3 ToWorldPosition(float cellSize = 1f)
int ManhattanDistance(GridPosition other)
float EuclideanDistance(GridPosition other)
bool IsAdjacent(GridPosition other)
bool IsCardinalDirection()
bool IsDiagonal()

// Operators
GridPosition + GridPosition
GridPosition - GridPosition
GridPosition * int
bool == / !=

GridCell

// Properties
GridPosition Position { get; }
bool IsWalkable { get; set; }
bool IsOccupied { get; }
bool IsBlocked { get; }
bool IsAvailable { get; }
HighlightType CurrentHighlight { get; }

// Methods
void Initialize(GridPosition position, bool walkable = true)
void AddObject(GridObject obj)
void RemoveObject(GridObject obj)
List<GridObject> GetObjects()
void Highlight(bool enable)
void SetHighlight(HighlightType type)
void SetHighlight(Color color, float intensity = 2f, float pulseSpeed = 1.5f)
void SetDanger(bool active)
void ClearHighlight()
Vector3 GetWorldPosition()

GridManager

// Properties
int Width { get; }
int Height { get; }
float CellSize { get; }
Vector3 Origin { get; }

// Grid Initialization
void InitializeGrid(int width, int height)
void ClearGrid()
void ClearAll()
void ApplyIrregularity()

// Coordinate Conversion
Vector3 GetWorldPosition(GridPosition pos)
GridPosition WorldToGridPosition(Vector3 worldPos)
Vector3 GetCenterPosition()

// Grid Queries
bool IsValidPosition(GridPosition position)
GridCell GetCell(GridPosition position)
bool IsCellAvailable(GridPosition position)
bool IsCellOccupied(GridPosition position)
List<GridCell> GetMainIslandCells()
int GetDistanceFromVoid(GridPosition pos)

// Spawn Helpers
GridPosition GetSafeSpawnPosition(List<GridCell> validCells, int minDistanceFromEdge)
GridPosition GetSafeSpawnPositionNear(List<GridCell> validCells, int minDistanceFromEdge, GridPosition targetPos, int randomness = 5)

// Range Queries
List<GridCell> GetCellsInRange(GridPosition center, int range, bool onlyWalkable = true)
List<GridCell> GetCellsInSquareRange(GridPosition center, int range, bool onlyWalkable = true)
List<GridCell> GetCellsInLine(GridPosition start, GridPosition direction, int maxDistance = 100)
List<GridCell> GetNeighbors(GridPosition position, bool includeDiagonals = false)
List<GridCell> GetStraightPath(GridPosition start, GridPosition direction)

// Object Management
void RegisterObject(GridObject obj, GridPosition position)
void MoveObject(GridObject obj, GridPosition targetPos, MovementType type = MovementType.Walk, Action onComplete = null)
void RemoveObject(GridObject obj)

// Pathfinding
List<GridPosition> FindPath(GridPosition start, GridPosition end)

// Visualization
void HighlightCells(List<GridCell> cells, bool highlight = true)
void ClearAllHighlights()

GridObject

// Inspector Properties
bool isBlocking          // Blocks other objects
bool isPickable          // Can be interacted with
bool isImmovable         // Cannot be pushed
float groundHeight       // Y offset from ground

// Runtime Properties
GridPosition GridPosition { get; }  // Current grid position (read-only)
bool IsBlocking { get; }
bool IsPickable { get; }
bool IsImmovable { get; }
float GroundHeight { get; }
Vector3 VisualOffset { get; }       // Visual centering offset

// Events
event Action<GridPosition> OnMoveStart    // Fired when movement begins
event Action<GridPosition> OnMoveComplete // Fired when movement ends

// Methods
virtual void Initialize(GridPosition position)
virtual void TeleportTo(GridPosition newPosition)
virtual void MoveTo(GridPosition newPosition, MovementType type = MovementType.Walk, Action onComplete = null)
bool Push(GridPosition direction, GridManager gridManager)
void Swap(GridObject other, GridManager gridManager)

// Internal (called by GridMovement)
void OnMoveStartInternal(GridPosition targetPos)
void OnMoveCompleteInternal(GridPosition targetPos)

GridMovement

// Requires: GridObject

// Inspector Properties
float moveSpeed              // Base movement speed (default: 5)
float jumpHeight             // Jump arc height (default: 2)
float dashSpeedMultiplier    // Dash speed multiplier (default: 3)

// Methods
void Move(GridPosition targetPos, MovementType type, Action onComplete)

GridEntity

// Inherits all from GridObject

// Additional Inspector Properties
bool billboard           // Face camera (default: true)
float verticalOffset     // Y offset for sprite pivot (default: 0.5)
Vector3 baseScale        // Entity scale (default: 5,5,5)

// Behavior: LateUpdate handles billboarding when enabled

Examples

Complete Game Setup

using UnityEngine;
using CrackedGames.Grid;
using System.Collections.Generic;

public class GameManager : MonoBehaviour
{
    [SerializeField] private GridManager gridManager;
    [SerializeField] private GridObject playerPrefab;
    [SerializeField] private GridObject obstaclePrefab;
    [SerializeField] private int obstacleCount = 5;

    private GridObject player;
    private List<GridCell> mainIsland;

    void Start()
    {
        SetupGame();
    }

    void SetupGame()
    {
        // Initialize grid with erosion
        gridManager.InitializeGrid(10, 10);
        gridManager.ApplyIrregularity();

        // Get valid spawn area
        mainIsland = gridManager.GetMainIslandCells();

        // Spawn player
        SpawnPlayer();

        // Spawn obstacles
        SpawnObstacles();
    }

    void SpawnPlayer()
    {
        GridPosition spawnPos = gridManager.GetSafeSpawnPosition(mainIsland, 2);
        player = Instantiate(playerPrefab);
        gridManager.RegisterObject(player, spawnPos);
    }

    void SpawnObstacles()
    {
        for (int i = 0; i < obstacleCount; i++)
        {
            GridPosition pos = gridManager.GetSafeSpawnPosition(mainIsland, 1);
            if (pos != GridPosition.Zero)
            {
                var obstacle = Instantiate(obstaclePrefab);
                gridManager.RegisterObject(obstacle, pos);
            }
        }
    }

    void Update()
    {
        HandleInput();
    }

    void HandleInput()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                GridPosition targetPos = gridManager.WorldToGridPosition(hit.point);
                MovePlayerTo(targetPos);
            }
        }
    }

    void MovePlayerTo(GridPosition targetPos)
    {
        if (!gridManager.IsCellAvailable(targetPos)) return;

        List<GridPosition> path = gridManager.FindPath(player.GridPosition, targetPos);
        if (path != null && path.Count > 0)
        {
            StartCoroutine(ExecutePath(path));
        }
    }

    System.Collections.IEnumerator ExecutePath(List<GridPosition> path)
    {
        foreach (var pos in path)
        {
            bool complete = false;
            gridManager.MoveObject(player, pos, MovementType.Walk, () => complete = true);
            yield return new WaitUntil(() => complete);
        }
    }
}

Range-Based Attack System

using UnityEngine;
using CrackedGames.Grid;
using System.Collections.Generic;

public class AttackSystem : MonoBehaviour
{
    [SerializeField] private GridManager gridManager;
    [SerializeField] private int attackRange = 3;

    private List<GridCell> highlightedCells = new List<GridCell>();

    public void ShowAttackRange(GridPosition from)
    {
        ClearHighlights();

        highlightedCells = gridManager.GetCellsInRange(from, attackRange);

        foreach (var cell in highlightedCells)
        {
            if (cell.IsOccupied)
            {
                cell.SetHighlight(HighlightType.Attack);
            }
            else
            {
                cell.SetHighlight(HighlightType.ValidMove);
            }
        }
    }

    public void ClearHighlights()
    {
        foreach (var cell in highlightedCells)
        {
            cell.ClearHighlight();
        }
        highlightedCells.Clear();
    }

    public bool IsInRange(GridPosition attacker, GridPosition target)
    {
        return attacker.ManhattanDistance(target) <= attackRange;
    }
}

Danger Zone System

using UnityEngine;
using CrackedGames.Grid;
using System.Collections.Generic;

public class DangerZoneSystem : MonoBehaviour
{
    [SerializeField] private GridManager gridManager;

    private HashSet<GridCell> dangerCells = new HashSet<GridCell>();

    public void MarkDangerZone(GridPosition center, int radius)
    {
        var cells = gridManager.GetCellsInRange(center, radius, onlyWalkable: false);

        foreach (var cell in cells)
        {
            cell.SetDanger(true);
            dangerCells.Add(cell);
        }
    }

    public void ClearDangerZones()
    {
        foreach (var cell in dangerCells)
        {
            cell.SetDanger(false);
        }
        dangerCells.Clear();
    }

    public bool IsInDanger(GridPosition pos)
    {
        var cell = gridManager.GetCell(pos);
        return cell != null && dangerCells.Contains(cell);
    }
}

Custom Grid Entity

using UnityEngine;
using CrackedGames.Grid;

public class PlayerUnit : GridEntity
{
    [SerializeField] private int health = 100;
    [SerializeField] private int attackPower = 25;
    [SerializeField] private int moveRange = 3;

    private SpriteRenderer spriteRenderer;

    protected override void Awake()
    {
        base.Awake();
        spriteRenderer = GetComponent<SpriteRenderer>();
    }

    public void TakeDamage(int damage)
    {
        health -= damage;

        // Flash red
        StartCoroutine(FlashColor(Color.red, 0.2f));

        if (health <= 0)
        {
            Die();
        }
    }

    public void Attack(GridObject target)
    {
        if (target is PlayerUnit enemy)
        {
            enemy.TakeDamage(attackPower);
        }
    }

    private System.Collections.IEnumerator FlashColor(Color color, float duration)
    {
        Color original = spriteRenderer.color;
        spriteRenderer.color = color;
        yield return new WaitForSeconds(duration);
        spriteRenderer.color = original;
    }

    private void Die()
    {
        // Handle death
        Destroy(gameObject);
    }
}

Performance Considerations

  • A* Pathfinding: Efficient dictionary-based implementation suitable for grids up to ~100x100
  • Cell Lookup: O(1) access via position dictionary
  • Flood Fill: Uses HashSet for O(1) membership checks
  • MaterialPropertyBlock: Used for highlights to avoid material instancing
  • Range Queries: Optimized using Manhattan/Chebyshev distance calculations

Troubleshooting

Cells Not Appearing
  • Ensure cellPrefabs list has at least one prefab with weight > 0
  • Check that prefabs have GridCell component and MeshRenderer
Pathfinding Returns Null
  • Verify start and end positions are valid and walkable
  • Check that blocking objects aren't preventing passage
  • Ensure grid is connected (not split by erosion)
Highlights Not Visible
  • GridCellHighlight component is auto-created but needs proper shader
  • Supported shaders: "CrackedGames/GridHighlight", URP/Unlit, Legacy/Transparent/Diffuse
Camera Not Positioning Correctly
  • Ensure GridManager reference is assigned
  • Call SetupCamera() after grid initialization if autoSetup is false
Walls Obscuring View
  • Walls are only generated in the "back triangle" by design
  • Adjust wallHeight and wallOffset if needed

Support

For issues and feature requests, please visit the project repository or contact support.

© 2025 Cracked Games LLC. All rights reserved.