# 4. Context-Sensitive Prompt Volumes Part of [[Context-Sensitive Prompts and Onboarding]]. Prev: [[3. Glyph Prompts in UI Toolkit]]. Next: [[5. Worked Example Door Prompt]]. The view does not know about doors. A small world component says "the player can Interact with me" and a broker picks one. That is the same idea as a single HUD hint slot in [[3.HUD and Inventory Example]]. ## 4.1 A prompt source ```csharp using UnityEngine; public interface IPromptSource { string Verb { get; } int Priority { get; } bool IsAvailable { get; } } /// <summary> /// Trigger volume that offers a prompt while the player is inside. /// NOT compiled in Unity. /// </summary> [RequireComponent(typeof(Collider))] public sealed class PromptVolume : MonoBehaviour, IPromptSource { [SerializeField] string verb = "Interact"; [SerializeField] int priority = 0; [SerializeField] PromptBroker broker; public string Verb => verb; public int Priority => priority; public bool IsAvailable { get; set; } = true; public void SetVerb(string value) { verb = value; if (_inside) broker?.Offer(this); } bool _inside; void Awake() { var col = GetComponent<Collider>(); col.isTrigger = true; } void OnTriggerEnter(Collider other) { if (!IsPlayer(other)) return; _inside = true; broker?.Offer(this); } void OnTriggerExit(Collider other) { if (!IsPlayer(other)) return; _inside = false; broker?.Withdraw(this); } void OnDisable() { if (_inside) broker?.Withdraw(this); _inside = false; } public void RefreshAvailability() { if (_inside) broker?.Offer(this); } static bool IsPlayer(Collider other) { return other.GetComponentInParent<CharacterController>() != null || other.CompareTag("Player"); } } ``` Set `IsAvailable` false when the interactable should not be used (door opening, empty chest). Call `RefreshAvailability` so the broker can hide the prompt. A locked door can stay available and change the **verb** to "Locked" (or a localized equivalent) rather than disappearing - Level 12 wants the door visible. ## 4.2 The broker ```csharp using System.Collections.Generic; using UnityEngine; /// <summary> /// One visible prompt: the highest-priority available source in range. /// NOT compiled in Unity. /// </summary> public sealed class PromptBroker : MonoBehaviour { [SerializeField] PromptView view; readonly List<IPromptSource> _offered = new(); public void Offer(IPromptSource source) { if (source == null) return; if (!_offered.Contains(source)) _offered.Add(source); Push(); } public void Withdraw(IPromptSource source) { _offered.Remove(source); Push(); } public void Push() { IPromptSource best = null; for (int i = 0; i < _offered.Count; i++) { var s = _offered[i]; if (s == null || !s.IsAvailable) continue; if (best == null || s.Priority > best.Priority) best = s; } if (view == null) return; if (best == null) { view.SetVisible(false); return; } view.SetVerb(best.Verb); view.SetVisible(true); view.Refresh(); } } ``` Interact still goes to the interactable (a `KeyDoor`, a pickup), not to the broker. If you need "the current target," add `Current` and let an interactor query it - but the action must stay on the gameplay object ([[3. Lock-and-Key Data]] already listens for Interact while the player is in its use volume). Two overlap volumes both listening to Interact is a design problem; give the door and the pickup different ranges, or have only the broker's current source accept the press. ## 4.3 Exercise Place two volumes with different priorities. Walk so both overlap and confirm only the higher one shows. Disable `IsAvailable` on it and confirm the lower one appears, then neither when you walk away. ## Sources - [[3.HUD and Inventory Example]] - [[3. Lock-and-Key Data]]