replace the NPC scaffold with data-driven prefabs, and retire the builders
NPCStandIn was written to be dropped on a sphere and carried its identity in per-instance public fields. The cost of that showed up in the scene itself: of the two spheres, one was the Bartender and the other was named "Bartender (1)" in the hierarchy while being configured as the Chair — data changed, name never was. Nothing outside the scene could see what existed, and two copies could disagree. Identity now lives in an InteractableDefinition asset and the scene only places a prefab. The migration matched the old objects by yarn node rather than by name, which is the only reason the chair ended up as the chair. InteractableDefinition rather than the planned "character definition": it covers a character and a talkable object equally, so Bartender.prefab sits under Prefabs/Characters and Chair.prefab under Prefabs/Interactables. Three components collapse into one. DialogueInteractable was in no scene at all — a dead trigger-zone variant of the same idea. ClickInteractableBridge existed only to adapt NPCStandIn without modifying it, a constraint that died with NPCStandIn; DialogueInteractor implements IClickInteractable itself. The parts worth keeping were lifted out rather than discarded: WorldSpaceLabel and InteractionPrompt are now normal components on prefab children, so a label can be seen and positioned in the Editor instead of existing only at runtime. InputDeviceTracker moved into NightclubArcadia.Core and gained a namespace, leaving no global-namespace runtime types. Characters get the same pipeline as Skills and Candidates: Assets/Characters/characters.json is the source of truth, CharacterSetup regenerates the definition assets on script reload and removes orphans. Adding an NPC is a JSON entry plus a prefab — no code, and no Unity for the data half. Phase 5.4 deletes the one-shot builders rather than disabling them. UILayerSetup, PlayerControlSetup, UILayerSetupAutoRun and YarnDemoSetup assumed the single-scene layout, had already destroyed a level scene and turned Systems binary once each, and after NPCStandIn went they no longer compiled. The prefabs they used to generate are authored and committed; the wiring knowledge is in those prefabs and in git history. The asset generators — Skill, Candidate, Character — are untouched and still work. EditMode 42/42, PlayMode 7/7, YarnCheck 9 files / 35 nodes. All three scenes still text. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -1,93 +0,0 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using Yarn.Unity;
|
||||
using NightclubArcadia.Core;
|
||||
|
||||
namespace NightclubArcadia.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// Trigger-zone interactable: player walks in range, presses E, and this
|
||||
/// starts the given Yarn node. The reverse of DialogueTransitionController's
|
||||
/// <<enter_environment>> — this is the "step into dialogue" half of the loop.
|
||||
/// </summary>
|
||||
public class DialogueInteractable : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private DialogueRunner dialogueRunner;
|
||||
[SerializeField] private GameObject dialogueUI;
|
||||
[SerializeField] private string yarnNodeName = "Bartender_Chat";
|
||||
|
||||
private bool playerInRange;
|
||||
private InputAction interactAction;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// Best-effort and silent; the real lookup happens at the point of use, since
|
||||
// Systems may not have loaded when this level object awakes. See Runner.
|
||||
dialogueRunner = SceneServices.TryResolve(dialogueRunner);
|
||||
|
||||
interactAction = new InputAction("Interact");
|
||||
interactAction.AddBinding("<Keyboard>/e");
|
||||
interactAction.AddBinding("<Gamepad>/buttonNorth");
|
||||
interactAction.performed += _ =>
|
||||
{
|
||||
if (playerInRange)
|
||||
{
|
||||
StartConversation();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The dialogue runner, resolved on demand rather than in Awake, so that load
|
||||
/// order between this level scene and Systems does not matter. Cached once found.
|
||||
/// </summary>
|
||||
DialogueRunner Runner =>
|
||||
dialogueRunner != null
|
||||
? dialogueRunner
|
||||
: dialogueRunner = SceneServices.TryResolve<DialogueRunner>(null);
|
||||
|
||||
void OnEnable() => interactAction?.Enable();
|
||||
void OnDisable() => interactAction?.Disable();
|
||||
|
||||
void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (other.CompareTag("Player"))
|
||||
{
|
||||
playerInRange = true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (other.CompareTag("Player"))
|
||||
{
|
||||
playerInRange = false;
|
||||
}
|
||||
}
|
||||
|
||||
async void StartConversation()
|
||||
{
|
||||
var runner = Runner;
|
||||
if (runner == null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[DialogueInteractable] {name}: no DialogueRunner in any loaded scene. " +
|
||||
"It lives in Systems.unity — open Bootstrap, or Tools → Nightclub Arcadia → " +
|
||||
"Open Game Scenes.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Player controls are locked by DialogueTransitionController off the
|
||||
// runner's onDialogueStart event — nothing to do for that here.
|
||||
if (DialogueUIVisibility.Show(dialogueUI))
|
||||
{
|
||||
// Only reached if the UI was found deactivated; give TMP a
|
||||
// frame to re-cache its Canvas in OnEnable before any text is
|
||||
// generated.
|
||||
await Awaitable.NextFrameAsync();
|
||||
}
|
||||
|
||||
await runner.StartDialogue(yarnNodeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99afbbd28c8054d59a7a0dec94f5ec8f
|
||||
@@ -11,7 +11,7 @@ namespace NightclubArcadia.Dialogue
|
||||
///
|
||||
/// The lock is driven by the DialogueRunner's own lifecycle events rather
|
||||
/// than by each interactable, so every entry point — auto-started nodes,
|
||||
/// NPCStandIn, DialogueInteractable — gets the same behaviour without
|
||||
/// DialogueInteractor — gets the same behaviour without
|
||||
/// having to remember to do it. Control locking is delegated to
|
||||
/// <see cref="PlayerControlLock"/> so the player menu can hold a second
|
||||
/// independent lock without re-enabling movement mid-conversation.
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace NightclubArcadia.Dialogue
|
||||
/// <summary>
|
||||
/// Shows and hides the dialogue UI *without* deactivating its GameObject.
|
||||
/// Forwards to <see cref="PanelVisibility"/> — kept as a thin wrapper so
|
||||
/// existing call sites (NPCStandIn, DialogueInteractable,
|
||||
/// existing call sites (DialogueInteractor,
|
||||
/// DialogueTransitionController) continue to compile unchanged.
|
||||
/// </summary>
|
||||
public static class DialogueUIVisibility
|
||||
|
||||
Reference in New Issue
Block a user