# 5. Worked Example: A Door Prompt
Part of [[Context-Sensitive Prompts and Onboarding]]. Prev: [[4. Context-Sensitive Prompt Volumes]]. Next: [[6. Prompt and Onboarding Pitfalls]].
Use a door that already exists, or the `KeyDoor` from [[3. Lock-and-Key Data]]. The point is the *prompt*, not a new latch.
## 5.1 Scene
1. An Input Actions asset with Interact (and a Keyboard and Gamepad binding), already wired as in [[Input System]] / [[5. Worked Example Remappable Player]].
2. A `PanelRenderer` with the prompt UXML from part 3, Panel Settings, and a screen-space HUD ([[2.UI Toolkit Project Setup]]).
3. `PromptView` + `PromptBindingWatcher` on that GameObject. Assign the Interact `InputActionReference`, optional `PlayerInput`, and a `ControlIconSet` if you have a South-button sprite.
4. A door with a use trigger and a `PromptVolume` (`verb`: "Open" when unlocked, or start as "Locked" / "Open" based on state). `PromptBroker` in the scene, referenced by the volume and the view.
5. The door itself still handles Interact. Do not open the door from `PromptView`.
If you have `KeyDoor`, hook `Unlocked` to change the volume verb to "Open" and call `RefreshAvailability`. While locked and the player has no key, the prompt can read "Locked" plus the Interact glyph - the binding is still truthful; the verb tells the state.
## 5.2 A thin KeyDoor hook
```csharp
using UnityEngine;
public sealed class KeyDoorPromptHook : MonoBehaviour
{
[SerializeField] KeyDoor door;
[SerializeField] PromptVolume volume;
[SerializeField] InventoryHolder player;
void OnEnable()
{
if (door != null)
{
door.Unlocked += OnUnlocked;
door.Opened += OnOpened;
}
if (player != null && player.Inventory != null)
player.Inventory.Changed += OnInventoryChanged;
ApplyVerb();
}
void OnDisable()
{
if (door != null)
{
door.Unlocked -= OnUnlocked;
door.Opened -= OnOpened;
}
if (player != null && player.Inventory != null)
player.Inventory.Changed -= OnInventoryChanged;
}
void OnUnlocked(KeyDoor _) => ApplyVerb();
void OnOpened(KeyDoor _) => ApplyVerb();
void OnInventoryChanged() => ApplyVerb();
void ApplyVerb()
{
if (door == null || volume == null) return;
volume.SetVerb(player != null ? door.PromptFor(player) : (door.IsLocked ? "Locked" : "Open"));
volume.RefreshAvailability();
}
}
```
`Inventory.Changed` is the event from [[Inventory Systems]]; if your inventory type uses a different name, subscribe to that instead of polling in `Update`.
## 5.3 Playtest checklist
1. Walk up to the locked door on keyboard: the prompt shows the current Interact key (not a hard-coded "E") and a locked or open verb.
2. Plug in a gamepad, press a pad button so the scheme switches: the glyph or "A" / South label replaces the key without leaving the volume.
3. Rebind Interact in your rebind UI, return to the door: the prompt matches the new binding. `PromptBindingWatcher.RaiseRefreshed` must have run.
4. Unlock the door: verb changes, binding stays.
5. Walk away: the prompt hides. Interact does nothing to that door out of range.
6. Two volumes: only the higher priority shows.
## 5.4 Onboarding beat (after the verbs work)
When Jump, Interact, and a door already feel right, *then* gray-box a short first room: see the door, walk to it, read the prompt, open it (or fetch a key you can see). That is training built last ([[1. Prompt Design and Leave Training for Last]], [[ProBuilder Gray-Boxing and Landmarks]]). Do not invent a second Interact action for the tutorial.
## Exercise
Add a pickup volume with a lower priority than the door. Standing in both, the door wins. Pick up the key first in a slightly separated volume, then the door verb changes - the layout still shows the door first (Level 12).
## Sources
- [[3. Lock-and-Key Data]]
- [[5. Worked Example Remappable Player]]