split the scene into Bootstrap, Systems and Level
The game now starts from Bootstrap.unity, which additively loads Systems (the dialogue runner, skills, UI layer, camera rig and the player) and then a level (geometry, light, navmesh, reveal cameras). The level becomes the active scene so new objects and lighting land there. The player moved into Systems. It had been parented under Room_101 — under level geometry — and it is persistent content, not level content. Moving it also removes most of the cross-scene breakage on its own. Unity nulls any serialized reference that crosses a scene boundary. Measured rather than guessed: the pre-split scene was pulled from git and its null references diffed against the split result. Exactly seven were lost — both NPCs' dialogueRunner, dialogueUI and player, plus the reveal camera's tracking target. A first naive audit reported 210, which turned out to be pre-existing Unity defaults like Image.m_Material; the baseline diff is what separated the two. Each of the seven now resolves at runtime. SceneServices.Resolve fills in a null Inspector reference by searching the loaded scenes, keeping an assigned value if there is one; CinemachineFollowsPlayer binds the reveal camera once the player exists. Both follow the pattern already used by CameraFramingVolume and by NPCStandIn's player lookup, rather than introducing a new one. Assets/Tests/PlayMode is new, and it immediately paid for itself. BootstrapTests loads Bootstrap and asserts the whole game comes up; on its first run it caught a real bug the split had introduced. The player's NavMeshAgent lives in Systems while the NavMesh is baked into the level, so during load the agent exists off-mesh and ResetPath logs an error. ClickToMoveController now guards on agent.isOnNavMesh rather than a bare null check. No EditMode test could have seen that, because none of it has run yet. Two of those checks reach their types by name through reflection: NPCStandIn and the Cinematics namespace live in Assembly-CSharp, and an asmdef test assembly cannot reference the predefined assemblies. Per-area asmdefs remove the need. Build settings list all three scenes with Bootstrap at index 0, which LoadSceneAsync by name requires. EditMode 42/42, PlayMode 5/5, YarnCheck 9 files / 35 nodes. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using System.Collections;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
using NightclubArcadia.Core;
|
||||
|
||||
namespace NightclubArcadia.Cinematics
|
||||
{
|
||||
/// <summary>
|
||||
/// Points a CinemachineCamera at the player once the player exists.
|
||||
///
|
||||
/// Level cameras — the reveal cameras in particular — used to hold a serialized
|
||||
/// reference to the PlayerArmature. The player now lives in the Systems scene, so
|
||||
/// that reference cannot be serialized any more and has to be bound at runtime.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(CinemachineCamera))]
|
||||
public sealed class CinemachineFollowsPlayer : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Also drive LookAt, not just Follow.")]
|
||||
[SerializeField] bool alsoLookAt;
|
||||
|
||||
[Tooltip("Seconds to keep retrying while the Systems scene finishes loading.")]
|
||||
[SerializeField] float resolveTimeout = 5f;
|
||||
|
||||
IEnumerator Start()
|
||||
{
|
||||
var cam = GetComponent<CinemachineCamera>();
|
||||
var deadline = Time.unscaledTime + resolveTimeout;
|
||||
|
||||
while (SceneServices.Player == null && Time.unscaledTime < deadline)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
var player = SceneServices.Player;
|
||||
if (player == null)
|
||||
{
|
||||
Debug.LogError($"[CinemachineFollowsPlayer] {name}: no object tagged Player appeared " +
|
||||
$"within {resolveTimeout}s.", this);
|
||||
yield break;
|
||||
}
|
||||
|
||||
cam.Follow = player;
|
||||
if (alsoLookAt)
|
||||
{
|
||||
cam.LookAt = player;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user