Procedural Tile System (Sample)

Example Procgen system (see Samples)

The Procedural Tile System keeps a ring of world tiles centered on the player (or any transform). As the center moves, tiles outside the ring despawn while new tiles in front spawn in. This is designed to pair cleanly with Grid Cells mode in the NavMeshBakerService, but it also works with Continuous AABB (the service falls back to “around target” bakes when needed).

At runtime the manager:

  • Converts the center’s world position to a grid cell.

  • Maintains a configurable number of rings around that cell (e.g., rings=1 → 3×3; rings=2 → 5×5).

  • Spawns tiles by asking a provider which prefab to use per cell.

  • Optionally pools tiles for smooth, GC-friendly streaming.

  • (Optional) Notifies the NavMeshBakerService to enqueue bakes after tile changes.


When to Use

  • Open worlds or endless runners built from repeated “tile” prefabs.

  • Procgen/streaming maps where you want consistent tile alignment with runtime NavMesh baking.

  • Any scene where you want a simple, deterministic grid centered on the player.


Setup

  1. Add the Manager

    • Create an empty GameObject (e.g., ProceduralTileManager) and add ProceduralTileManager.

    • Assign Center Target (player or camera). If unassigned, Main Camera is used by default.

    • Optionally assign Tiles Parent (a container transform for spawned tiles).

  2. Pick Tile Size & Rings

    • Set Tile Size XZ to match your tile prefab’s footprint (e.g., 16×16 or 32×32).

    • Set Rings (1 = 3×3, 2 = 5×5, etc.). Larger rings mean more tiles alive at once.

  3. Provide Prefabs

    • Implement IProceduralTileProvider (below) or assign a sample provider scriptable (e.g., a noise/biome-based provider).

    • Drag that asset into Provider Asset.

  4. NavMesh Integration (recommended)

    • Enable Enqueue Bake After Changes so the NavMeshBakerService refreshes local cells when tiles change.

    • In your NavMeshBakeProfile, set Bake Mode = Grid Cells and Cell Size XZ equal to your Tile Size XZ for perfect alignment.

  5. Play

    • Move the player — tiles stream in front, old tiles despawn behind, and the service bakes navmesh for new cells.


Inspector Reference

References

  • Center Target (Transform): Usually your player or camera.

  • Tiles Parent (Transform): Optional parent for spawned tiles; defaults to the manager object.

Tiling

  • Tile Size XZ (Vector2): World footprint per tile in meters.

  • Rings (int): 0=current cell only; 1=3×3; 2=5×5; etc.

  • Y Height (float): World Y position to place tile centers.

Spawning

  • Poll Rate Seconds (float): How often the manager checks for cell changes.

  • Hysteresis (0..0.9): “Stickiness” to avoid thrashing near cell boundaries.

  • Use Pooling (bool): Reuse despawned tile instances per prefab.

Tile Provider

  • Provider Asset (ScriptableObject): Must implement IProceduralTileProvider.

NavMesh Integration

  • Enqueue Bake After Changes (bool): If enabled, calls NavMeshBakerService.EnqueueCurrentGridCells() after tiles spawn/despawn.


Provider Interface

public interface IProceduralTileProvider
{
    GameObject GetPrefabForCell(Vector2Int cell);
}
  • Return a prefab (tile) for the given cell coordinate.

  • Return null to spawn an empty container tile (useful for testing layout).

  • You can encode biomes, noise, or “seeded” content using the cell index.

Example Provider (Sample)

using UnityEngine;

[CreateAssetMenu(menuName = "Runtime Navmesh Baker/Simple Noise Tile Provider")]
public class SimpleNoiseTileProvider : ScriptableObject, MegaCrush.RuntimeNavmeshBaker.IProceduralTileProvider
{
    [SerializeField] private GameObject grassPrefab;
    [SerializeField] private GameObject rockPrefab;
    [SerializeField] private float threshold = 0.5f;
    [SerializeField] private float scale = 0.1f;

    public GameObject GetPrefabForCell(Vector2Int cell)
    {
        float n = Mathf.PerlinNoise(cell.x * scale, cell.y * scale);
        return n < threshold ? grassPrefab : rockPrefab;
    }
}

Object Pooling

  • When Use Pooling is enabled, the manager keeps a stack per prefab.

  • Despawned tiles are deactivated and pushed back to the pool; new tiles pop from the pool if available.

  • In Editor, despawns use DestroyImmediate outside Play Mode; in Play Mode, Destroy.


How It Works (Runtime Flow)

  1. Converts the Center Target position to a grid cell using Tile Size XZ and Hysteresis.

  2. Builds the set of wanted cells for the configured Rings.

  3. Despawns any active tiles not in the wanted set; spawns any missing wanted tiles.

  4. If Enqueue Bake After Changes is enabled, calls the baker service to bake the current ring.

  5. Repeats every Poll Rate Seconds.


Public API

  • ForceRefreshNow() Rebuilds the tile ring immediately (ignores poll cadence and hysteresis).

  • SetCenterTarget(Transform t) Changes the follow target at runtime (useful for switching player/camera).


Best Practices

  • Match Sizes: In Grid Cells mode, set Profile.Cell Size XZ exactly equal to Tile Size XZ to keep baking aligned to tiles.

  • Keep Rings Low: Start with rings=1 (3×3) and increase only if you see visible pop-in.

  • Balance Polling: pollRateSeconds of 0.1–0.25s is responsive without being noisy.

  • Use Layers: Put tile geometry on layers included in the Profile Layer Mask so the baker collects the right sources.

  • Surface Placement: If your tiles contain geometry that should be baked using CollectObjects.Children, put a DynamicNavMeshSurface on the tile root prefab (or a higher parent) as appropriate for your project.


Troubleshooting

  • “Nothing spawns” Ensure Provider Asset is assigned and returns a prefab for the requested cells.

  • “Agents can’t find navmesh” Verify NavMeshBakerService is in the scene, a NavMeshBakeProfile is assigned, and Enqueue Bake After Changes is enabled (or manually enqueue bakes).

  • “Tiles flicker near boundaries” Increase Hysteresis (e.g., 0.2–0.3) or lower Poll Rate Seconds.

  • “CPU spikes during moves” Reduce Rings, use Pooling, and lower Max Concurrent Bakes in the profile.

Last updated