SpawnOnBake (Sample Script)
Provided as a part of the Samples
Spawn On Bake (Sample)
Namespace: MegaCrush.NavmeshBaker.Sample
Purpose
Safely spawn characters or AI agents after the first successful runtime bake, avoiding errors like:
“Failed to create agent because there is no valid NavMesh.”
How it works
Subscribes to
BakerEvents.OnBakeCompleted
.On each completed bake:
Chooses a spawn position (from Spawn Point or the GameObject this script is attached to).
Calls
NavMesh.SamplePosition()
to snap to a valid NavMesh point.Instantiates the prefab.
If Only Once is enabled, ignores subsequent bakes.
Inspector
Prefab (GameObject) The object to spawn (e.g., enemy, NPC, player). If it’s an AI, it should include a
NavMeshAgent
.Spawn Point (Transform, optional) If set, the prefab spawns here; otherwise it spawns at this component’s transform.
Only Once (bool) If enabled, spawns on the first completed bake only.
Sample Max Distance (float) Radius around the desired position to search for a valid NavMesh point.
Area Mask (int) NavMesh area mask for sampling (default:
NavMesh.AllAreas
).
Example usage
Ensure your scene has a BakerCoordinator with a NavMeshBakeProfile and Center Target.
Add one or more DynamicNavMeshSurface components for the regions you want baked.
Create an empty GameObject and add SpawnOnBake.
Assign a Prefab (e.g., an enemy with
NavMeshAgent
).Optionally assign a Spawn Point.
Press Play — once the first bake finishes, the prefab spawns on valid NavMesh.
Why it’s useful
Guarantees AI never spawns off-navmesh.
Robust for procedural or streaming worlds where navmesh is generated at runtime.
Demonstrates listening to
BakerEvents.OnBakeCompleted
to gate gameplay logic.
Script
using MegaCrush.RuntimeNavmeshBaker;
using UnityEngine;
using UnityEngine.AI;
namespace MegaCrush.NavmeshBaker.Sample
{
/// <summary>
/// Spawns a prefab after the first completed runtime bake.
/// Listens to BakerEvents instead of directly binding to the service.
/// </summary>
public sealed class SpawnOnBake : MonoBehaviour
{
[Header("Prefab to Spawn")]
[SerializeField] private GameObject prefab;
[Header("Spawn Settings")]
[SerializeField] private Transform spawnPoint;
[SerializeField] private bool onlyOnce = true;
[Header("NavMesh Sampling")]
[SerializeField, Min(0f)] private float sampleMaxDistance = 5f;
[SerializeField] private int areaMask = NavMesh.AllAreas;
private bool _hasSpawned;
private void OnEnable()
{
BakerEvents.OnBakeCompleted += HandleBakeCompleted;
}
private void OnDisable()
{
BakerEvents.OnBakeCompleted -= HandleBakeCompleted;
}
private void HandleBakeCompleted(DynamicNavMeshSurface surface, Bounds region, float durationSeconds)
{
if (!prefab) return;
if (onlyOnce && _hasSpawned) return;
Vector3 desired = spawnPoint ? spawnPoint.position : transform.position;
if (NavMesh.SamplePosition(desired, out NavMeshHit hit, sampleMaxDistance, areaMask))
{
Instantiate(prefab, hit.position, Quaternion.identity);
_hasSpawned = true;
#if UNITY_EDITOR
Debug.Log($"[SpawnOnBake] Spawned '{prefab.name}' at {hit.position} (region {region.center}±{region.extents})");
#endif
}
else
{
#if UNITY_EDITOR
Debug.LogWarning($"[SpawnOnBake] No valid NavMesh within {sampleMaxDistance}m of {desired}. " +
"Increase Sample Max Distance or adjust Spawn Point.");
#endif
}
}
}
}
Last updated