added text entry system for debug console
This commit is contained in:
parent
e25a691ad9
commit
4375350901
4 changed files with 309 additions and 3 deletions
223
Assets/_Project/Scripts/Dev/DebugConsole.cs
Normal file
223
Assets/_Project/Scripts/Dev/DebugConsole.cs
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
// Assets/_Project/Scripts/Dev/DebugConsole.cs
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UIElements;
|
||||
using TD.UI;
|
||||
|
||||
namespace TD.Dev
|
||||
{
|
||||
/// <summary>
|
||||
/// Standalone debug console overlay: press the toggle key to open a text
|
||||
/// input, type a command, Enter to submit, Escape to cancel.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Deliberately decoupled from <see cref="HUDController"/> — own
|
||||
/// GameObject, own UIDocument/PanelSettings — so it can be dropped out of
|
||||
/// a build entirely by not including that GameObject, the same way
|
||||
/// <see cref="DevWaveControls"/> is kept separate from shipping systems.
|
||||
///
|
||||
/// This first pass only wires up open/close/focus/text-capture. Submit
|
||||
/// currently just logs the typed text — command parsing/dispatch (e.g.
|
||||
/// unlocking spells) comes next.
|
||||
/// </remarks>
|
||||
[RequireComponent(typeof(UIDocument))]
|
||||
public class DebugConsole : MonoBehaviour
|
||||
{
|
||||
public static DebugConsole Instance { get; private set; }
|
||||
|
||||
[Tooltip("Keyboard shortcut that opens and closes the console. Uses the new Input " +
|
||||
"System (UnityEngine.InputSystem.Key). Backquote is the physical ` / ~ key.")]
|
||||
[SerializeField] private Key toggleKey = Key.Backquote;
|
||||
|
||||
private VisualElement consoleContainer;
|
||||
private TextField commandInput;
|
||||
private bool consoleOpen;
|
||||
|
||||
// Frame on which the console was opened or closed. The toggle key and Enter
|
||||
// are ignored on that frame and the next so the keypress that opened/closed
|
||||
// the console doesn't also get typed into the field or immediately submit it.
|
||||
// Mirrors HUDController's chatToggleSuppressFrame.
|
||||
private int toggleSuppressFrame = -1;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Debug.LogError("[DebugConsole] Duplicate instance detected. Destroying the new one.");
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var doc = GetComponent<UIDocument>();
|
||||
if (doc == null)
|
||||
{
|
||||
Debug.LogError("[DebugConsole] No UIDocument component found.");
|
||||
return;
|
||||
}
|
||||
|
||||
var root = doc.rootVisualElement;
|
||||
if (root == null)
|
||||
{
|
||||
Debug.LogError("[DebugConsole] rootVisualElement is null. " +
|
||||
"Check that Panel Settings is assigned.");
|
||||
return;
|
||||
}
|
||||
|
||||
BuildConsoleUi(root);
|
||||
}
|
||||
|
||||
private void BuildConsoleUi(VisualElement uiRoot)
|
||||
{
|
||||
// Top-anchored bar, closed by default. Distinct position from the HUD's
|
||||
// bottom-left chat panel so the two never overlap.
|
||||
consoleContainer = new VisualElement();
|
||||
consoleContainer.pickingMode = PickingMode.Ignore;
|
||||
consoleContainer.style.position = Position.Absolute;
|
||||
consoleContainer.style.left = 0;
|
||||
consoleContainer.style.right = 0;
|
||||
consoleContainer.style.top = 0;
|
||||
consoleContainer.style.paddingTop = 8;
|
||||
consoleContainer.style.paddingBottom = 8;
|
||||
consoleContainer.style.paddingLeft = 12;
|
||||
consoleContainer.style.paddingRight = 12;
|
||||
consoleContainer.style.backgroundColor = new Color(0f, 0f, 0f, 0.75f);
|
||||
consoleContainer.style.display = DisplayStyle.None;
|
||||
|
||||
commandInput = new TextField();
|
||||
commandInput.style.minHeight = 28;
|
||||
commandInput.style.width = Length.Percent(100);
|
||||
commandInput.maxLength = 200;
|
||||
commandInput.isDelayed = false;
|
||||
StyleConsoleInput(commandInput);
|
||||
|
||||
// Focus tracking — contributes to the same flag HUDController's chat
|
||||
// input sets, so camera/builder/spell input all stay gated correctly
|
||||
// regardless of which text field currently has focus.
|
||||
commandInput.RegisterCallback<FocusInEvent>(_ => HUDController.IsTextInputActive = true);
|
||||
commandInput.RegisterCallback<FocusOutEvent>(_ => HUDController.IsTextInputActive = false);
|
||||
|
||||
consoleContainer.Add(commandInput);
|
||||
uiRoot.Add(consoleContainer);
|
||||
}
|
||||
|
||||
// Mirrors HUDController.StyleChatInputDark — dark background, white text,
|
||||
// a thin border, and inner padding so ascenders/descenders don't clip.
|
||||
// Green border (vs. chat's neutral gray) as a quick visual "this is the
|
||||
// debug console, not chat" cue.
|
||||
private static void StyleConsoleInput(TextField field)
|
||||
{
|
||||
var darkBg = new Color(0.05f, 0.05f, 0.05f, 0.95f);
|
||||
var borderClr = new Color(0.35f, 0.75f, 0.35f);
|
||||
|
||||
field.style.backgroundColor = darkBg;
|
||||
field.style.color = Color.white;
|
||||
field.style.borderTopWidth = field.style.borderBottomWidth =
|
||||
field.style.borderLeftWidth = field.style.borderRightWidth = 1;
|
||||
field.style.borderTopColor = field.style.borderBottomColor =
|
||||
field.style.borderLeftColor = field.style.borderRightColor = borderClr;
|
||||
|
||||
var inner = field.Q("unity-text-input");
|
||||
if (inner != null)
|
||||
{
|
||||
inner.style.backgroundColor = darkBg;
|
||||
inner.style.color = Color.white;
|
||||
inner.style.paddingTop = 4;
|
||||
inner.style.paddingBottom = 4;
|
||||
inner.style.paddingLeft = 6;
|
||||
inner.style.paddingRight = 6;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var kb = Keyboard.current;
|
||||
if (kb == null) return; // no keyboard connected (e.g. headless server)
|
||||
|
||||
if (Time.frameCount <= toggleSuppressFrame) return;
|
||||
|
||||
if (toggleKey != Key.None && kb[toggleKey].wasPressedThisFrame)
|
||||
{
|
||||
if (consoleOpen) CloseConsole();
|
||||
else OpenConsole();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!consoleOpen) return;
|
||||
|
||||
bool enterDown = kb.enterKey.wasPressedThisFrame || kb.numpadEnterKey.wasPressedThisFrame;
|
||||
bool escDown = kb.escapeKey.wasPressedThisFrame;
|
||||
|
||||
if (enterDown) SubmitCommand();
|
||||
else if (escDown) CloseConsole();
|
||||
}
|
||||
|
||||
private void OpenConsole()
|
||||
{
|
||||
if (commandInput == null) return;
|
||||
consoleContainer.style.display = DisplayStyle.Flex;
|
||||
consoleContainer.pickingMode = PickingMode.Position;
|
||||
commandInput.SetValueWithoutNotify(string.Empty);
|
||||
|
||||
// Suppress the toggle key and Enter for this frame and the next so the
|
||||
// keypress that opened the console doesn't also get typed into the field.
|
||||
// Focus is deferred a frame for the same reason — UI Toolkit would
|
||||
// otherwise route the open keypress to the freshly-focused TextField.
|
||||
toggleSuppressFrame = Time.frameCount + 1;
|
||||
consoleOpen = true;
|
||||
StartCoroutine(FocusNextFrame());
|
||||
}
|
||||
|
||||
private IEnumerator FocusNextFrame()
|
||||
{
|
||||
yield return null;
|
||||
if (consoleOpen && commandInput != null)
|
||||
{
|
||||
commandInput.Focus();
|
||||
commandInput.SelectAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseConsole()
|
||||
{
|
||||
if (commandInput != null)
|
||||
{
|
||||
commandInput.SetValueWithoutNotify(string.Empty);
|
||||
commandInput.Blur();
|
||||
}
|
||||
if (consoleContainer != null)
|
||||
{
|
||||
consoleContainer.style.display = DisplayStyle.None;
|
||||
consoleContainer.pickingMode = PickingMode.Ignore;
|
||||
}
|
||||
|
||||
consoleOpen = false;
|
||||
toggleSuppressFrame = Time.frameCount + 1;
|
||||
|
||||
// The Enter that submitted/cancelled this console also gets polled by
|
||||
// HUDController's chat this same frame — suppress its "Enter opens chat"
|
||||
// branch so closing the debug console doesn't pop chat open too.
|
||||
HUDController.SuppressChatOpenUntilFrame = Time.frameCount + 1;
|
||||
}
|
||||
|
||||
// Placeholder — command parsing/dispatch (unlocking spells, granting gold,
|
||||
// etc.) comes next. For now this just proves the text made it through.
|
||||
private void SubmitCommand()
|
||||
{
|
||||
string text = commandInput?.value ?? string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(text))
|
||||
Debug.Log($"[DebugConsole] Command entered: \"{text}\"");
|
||||
|
||||
CloseConsole();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/_Project/Scripts/Dev/DebugConsole.cs.meta
Normal file
2
Assets/_Project/Scripts/Dev/DebugConsole.cs.meta
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7c6a808e8291552d0b64e36a210879c4
|
||||
|
|
@ -157,8 +157,19 @@ namespace TD.UI
|
|||
|
||||
// Set true whenever the chat input or any other text field on the HUD has
|
||||
// keyboard focus. Camera, builder input, and hotkey handlers all gate on
|
||||
// this to keep typing from driving gameplay.
|
||||
public static bool IsTextInputActive { get; private set; }
|
||||
// this to keep typing from driving gameplay. Setter is internal (not
|
||||
// private) so other standalone text-input surfaces — e.g. TD.Dev.DebugConsole,
|
||||
// which is not part of HUDController — can contribute to the same flag
|
||||
// instead of every gameplay script needing to know about every input widget.
|
||||
public static bool IsTextInputActive { get; internal set; }
|
||||
|
||||
// Frame until which chat's "Enter opens chat" behavior is suppressed. Other
|
||||
// standalone text-input surfaces (e.g. TD.Dev.DebugConsole) set this when their
|
||||
// own Enter-driven submit/close already consumed the keypress, so the same
|
||||
// Enter press doesn't also get interpreted as "open chat" this frame. Without
|
||||
// this, submitting a debug console command with Enter would immediately pop
|
||||
// chat open too, since both systems poll the raw key independently.
|
||||
internal static int SuppressChatOpenUntilFrame = -1;
|
||||
|
||||
// ----- State ------------------------------------------------------
|
||||
|
||||
|
|
@ -2017,7 +2028,7 @@ namespace TD.UI
|
|||
|
||||
if (!chatInputOpen)
|
||||
{
|
||||
if (enterDown) OpenChatInput();
|
||||
if (enterDown && Time.frameCount > SuppressChatOpenUntilFrame) OpenChatInput();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue