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
Add the Manager
Create an empty GameObject (e.g.,
ProceduralTileManager
) and addProceduralTileManager
.Assign Center Target (player or camera). If unassigned, Main Camera is used by default.
Optionally assign Tiles Parent (a container transform for spawned tiles).
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.
Provide Prefabs
Implement
IProceduralTileProvider
(below) or assign a sample provider scriptable (e.g., a noise/biome-based provider).Drag that asset into Provider Asset.
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.
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)
Converts the Center Target position to a grid cell using
Tile Size XZ
and Hysteresis.Builds the set of wanted cells for the configured Rings.
Despawns any active tiles not in the wanted set; spawns any missing wanted tiles.
If Enqueue Bake After Changes is enabled, calls the baker service to bake the current ring.
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