Initial Unity project commit

This commit is contained in:
2026-07-08 19:53:15 +08:00
commit 75f254212e
15657 changed files with 11633879 additions and 0 deletions
@@ -0,0 +1,96 @@
using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace UI.PanelStack
{
public class PlayerInputActionRouter : MonoBehaviour
{
[SerializeField] private bool debugLogs = true;
[SerializeField] private string pauseActionName = "Pause";
[SerializeField] private string cancelActionName = "Cancel";
#if ENABLE_INPUT_SYSTEM
private PlayerInput playerInput;
private void OnEnable()
{
TryBind();
}
private void OnDisable()
{
Unbind();
}
private void TryBind()
{
playerInput = FindPrimaryPlayerInput();
if (playerInput == null)
{
if (debugLogs) Debug.Log("[PlayerInputActionRouter] No PlayerInput found; input routing disabled.");
return;
}
playerInput.onActionTriggered += OnActionTriggered;
if (debugLogs)
{
string map = playerInput.currentActionMap != null ? playerInput.currentActionMap.name : "(null)";
bool hasPause = playerInput.actions != null && playerInput.actions.FindAction(pauseActionName, false) != null;
bool hasCancel = playerInput.actions != null && playerInput.actions.FindAction(cancelActionName, false) != null;
Debug.Log($"[PlayerInputActionRouter] Bound. currentMap={map}, hasPause={hasPause}, hasCancel={hasCancel}");
}
}
private static PlayerInput FindPrimaryPlayerInput()
{
Player.PlayerController playerController = FindObjectOfType<Player.PlayerController>(true);
if (playerController != null)
{
PlayerInput playerInput = playerController.GetComponentInChildren<PlayerInput>(true);
if (playerInput != null) { return playerInput; }
}
return FindObjectOfType<PlayerInput>(true);
}
private void Unbind()
{
if (playerInput != null)
{
playerInput.onActionTriggered -= OnActionTriggered;
}
playerInput = null;
}
private void OnActionTriggered(InputAction.CallbackContext ctx)
{
if (ctx.action == null) { return; }
if (ctx.performed == false) { return; }
if (UIPanelStack.Instance == null) { return; }
string actionName = ctx.action.name;
if (actionName == cancelActionName)
{
bool closed = UIPanelStack.Instance.CloseTop();
if (debugLogs) Debug.Log($"[PlayerInputActionRouter] {cancelActionName} performed. closedTop={closed}");
return;
}
if (actionName == pauseActionName)
{
if (UIPanelStack.Instance.IsAnyOpen())
{
if (debugLogs) Debug.Log($"[PlayerInputActionRouter] {pauseActionName} performed but stack not empty.");
return;
}
bool opened = UIPanelStack.Instance.TryOpenOnEscapeWhenEmpty();
if (debugLogs) Debug.Log($"[PlayerInputActionRouter] {pauseActionName} performed. openedMenu={opened}");
}
}
#endif
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dbb84ff6c82d9df468c6cc9c439f9f95
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,25 @@
using UI.PanelStack;
using UnityEngine;
namespace UI.PanelStack
{
public class UIActionMapRouter : MonoBehaviour
{
[SerializeField] private bool debugLogs = true;
public void OnUICancel()
{
if (UIPanelStack.Instance == null) { return; }
bool closed = UIPanelStack.Instance.CloseTop();
if (debugLogs) Debug.Log($"[UIActionMapRouter] UI/Cancel triggered. closedTop={closed}");
}
public void OnUIPause()
{
if (UIPanelStack.Instance == null) { return; }
if (UIPanelStack.Instance.IsAnyOpen()) { return; }
bool opened = UIPanelStack.Instance.TryOpenOnEscapeWhenEmpty();
if (debugLogs) Debug.Log($"[UIActionMapRouter] UI/Pause triggered. openedMenu={opened}");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f71ed2608c4634c41821d378c37ae68f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,11 @@
namespace UI.PanelStack
{
public enum UIPanelKind
{
Settings = 0,
Modal = 1,
Pause = 2,
Custom = 100
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c207c3d0faf77841a5b735083d1d4b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace UI.PanelStack
{
public class UIPanelStack : MonoBehaviour
{
public static UIPanelStack Instance { get; private set; }
private readonly List<Entry> stack = new List<Entry>();
private Func<bool> escapeOpenHandler;
[SerializeField] private bool debugLogs = true;
private sealed class Entry
{
public UIPanelKind kind;
public GameObject root;
public Action closeAction;
}
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
public void Push(UIPanelKind kind, GameObject root, Action closeAction)
{
if (root == null) { return; }
Remove(root);
stack.Add(new Entry
{
kind = kind,
root = root,
closeAction = closeAction
});
if (debugLogs) Debug.Log($"[UIPanelStack] Push kind={kind} root={root.name} count={stack.Count}");
}
public bool Remove(GameObject root)
{
if (root == null) { return false; }
for (int i = stack.Count - 1; i >= 0; i--)
{
if (stack[i].root == root)
{
stack.RemoveAt(i);
if (debugLogs) Debug.Log($"[UIPanelStack] Remove root={root.name} count={stack.Count}");
return true;
}
}
return false;
}
public bool CloseTop()
{
CleanupInactive();
if (stack.Count == 0) { return false; }
Entry top = stack[stack.Count - 1];
stack.RemoveAt(stack.Count - 1);
if (top.root == null) { return false; }
if (top.closeAction != null)
{
if (debugLogs) Debug.Log($"[UIPanelStack] CloseTop via action kind={top.kind} root={top.root.name} remain={stack.Count}");
top.closeAction.Invoke();
return true;
}
if (debugLogs) Debug.Log($"[UIPanelStack] CloseTop via SetActive(false) kind={top.kind} root={top.root.name} remain={stack.Count}");
top.root.SetActive(false);
return true;
}
public void SetEscapeOpenHandler(Func<bool> handler)
{
escapeOpenHandler = handler;
if (debugLogs) Debug.Log($"[UIPanelStack] SetEscapeOpenHandler set={(handler!=null)}");
}
public void ClearEscapeOpenHandler(Func<bool> handler)
{
if (handler == null) { return; }
if (escapeOpenHandler == handler)
{
escapeOpenHandler = null;
}
}
public bool TryOpenOnEscapeWhenEmpty()
{
if (escapeOpenHandler == null) { return false; }
bool result = escapeOpenHandler.Invoke();
if (debugLogs) Debug.Log($"[UIPanelStack] TryOpenOnEscapeWhenEmpty result={result}");
return result;
}
public bool IsAnyOpen()
{
CleanupInactive();
return stack.Count > 0;
}
private void CleanupInactive()
{
for (int i = stack.Count - 1; i >= 0; i--)
{
if (stack[i].root == null || !stack[i].root.activeInHierarchy)
{
stack.RemoveAt(i);
}
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 35ee98817252d334e8278aafbe75db00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,35 @@
using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
#endif
namespace UI.PanelStack
{
public class UIPanelStackInput : MonoBehaviour
{
[SerializeField] private bool enabledInput = true;
private void Update()
{
if (!enabledInput) { return; }
if (UIPanelStack.Instance == null) { return; }
if (IsEscapePressed())
{
if (!UIPanelStack.Instance.CloseTop())
{
UIPanelStack.Instance.TryOpenOnEscapeWhenEmpty();
}
}
}
private bool IsEscapePressed()
{
#if ENABLE_INPUT_SYSTEM
return Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame;
#else
return Input.GetKeyDown(KeyCode.Escape);
#endif
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cf4367c679ee66743bd76b7243797601
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: