feat(init): 搭建初始的项目框架

This commit is contained in:
2025-10-14 12:39:53 +08:00
parent 3cf503bfa6
commit eba8d5792d
89 changed files with 2782 additions and 100 deletions

View File

@@ -0,0 +1,37 @@
using System;
using UnityEngine;
namespace Core
{
/// <summary>
/// Base class for all UI components.
/// </summary>
public abstract class UIBase : MonoBehaviour
{
public bool IsOpenOnFirstLoad;
/// <summary>
/// Called when the UI is shown.
/// </summary>
public virtual void Show()
{
gameObject.SetActive(true);
}
/// <summary>
/// Called when the UI is hidden.
/// </summary>
public virtual void Hide()
{
gameObject.SetActive(false);
}
/// <summary>
/// Called when the UI is initialized.
/// </summary>
public virtual void Initialize()
{
// Override in derived classes for initialization logic.
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 68b45d1df56287c4ba5489104835f768
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
using UnityEngine;
namespace Core
{
[RequireComponent(typeof(Camera))]
public class UICamera : MonoBehaviour
{
public static UICamera Instance { get; private set; }
private Camera uiCamera;
private void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
uiCamera = GetComponent<Camera>();
uiCamera.clearFlags = CameraClearFlags.Depth;
uiCamera.cullingMask = LayerMask.GetMask("UI");
uiCamera.orthographic = true;
uiCamera.depth = 100; // 确保在主相机之后渲染
}
public Camera GetCamera()
{
return uiCamera;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6653afda52a99784a9e0d5650ce2df10
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,18 @@
using UnityEngine;
namespace Core
{
// 挂在 NormalCanvas 上的脚本
public class UILayerRoot : MonoBehaviour
{
public UILayer layer;
void Awake()
{
if (UIManager.Instance != null)
{
UIManager.Instance.RegisterLayer(layer, transform);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7a5cdded3cae71848bcb0e7ca91e4570
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,139 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
namespace Core
{
public enum UILayer
{
Background,
Normal,
Popup,
Top
}
/// <summary>
/// 单例UI管理者控制UI的层级关系UI的开关
/// </summary>
public class UIManager : MonoSingleton<UIManager>
{
public bool IsHasNonBackgroundUIActive
{
get
{ return openedUIs.Values.Any(ui => ui.gameObject.activeSelf && ui.transform.parent != layerRoots[UILayer.Background]); }
}
private Dictionary<UILayer, Transform> layerRoots = new Dictionary<UILayer, Transform>();
private Dictionary<string, UIBase> openedUIs = new Dictionary<string, UIBase>();
public void RegisterLayer(UILayer layer, Transform root)
{
if (!layerRoots.ContainsKey(layer))
{
layerRoots[layer] = root;
// 注册场景中已存在的UI
// 遍历子对象
foreach (Transform child in root)
{
var uiName = child.gameObject.name;
UIBase uiBase = child.GetComponent<UIBase>();
if (uiBase != null && !openedUIs.ContainsKey(uiName))
{
openedUIs[uiName] = uiBase;
uiBase.Hide(); // 默认关闭
if (uiBase.IsOpenOnFirstLoad)
{
uiBase.Show();
}
}
}
}
}
public T OpenUI<T>(UILayer layer = UILayer.Normal) where T : Component
{
string uiName = typeof(T).Name;
if (openedUIs.ContainsKey(uiName))
{
openedUIs[uiName].Show();
UpdateCursorState();
return openedUIs[uiName] as T;
}
// 加载UI预制体
GameObject prefab = Resources.Load<GameObject>("UI/" + uiName); // 从 Resources/UI 文件夹加载UI预制体
if (prefab == null)
{
Debug.LogError("UI Prefab not found: " + uiName);
return null;
}
GameObject uiObj = Instantiate(prefab, layerRoots[layer]); // 实例化并设置父对象
UIBase uiBase = uiObj.GetComponent<UIBase>(); // 获取UIBase组件
openedUIs[uiName] = uiBase; // 注册到字典中
uiBase.Show(); // 显示UI
UpdateCursorState(); // 更新鼠标状态
return uiBase as T;
}
public void CloseUI<T>() where T : Component
{
string uiName = typeof(T).Name;
if (openedUIs.ContainsKey(uiName))
{
openedUIs[uiName].Hide();
UpdateCursorState();
}
}
// 来回切换UI状态按同一个键实现UI的开关
public void SwitchUI<T>(UILayer layer = UILayer.Normal) where T : Component
{
string uiName = typeof(T).Name;
if (openedUIs.ContainsKey(uiName) && openedUIs[uiName].gameObject.activeSelf)
{
CloseUI<T>();
}
else
{
OpenUI<T>(layer);
}
}
public Dictionary<UILayer, List<GameObject>> GetExistingUIsByLayer()
{
var result = new Dictionary<UILayer, List<GameObject>>();
foreach (var kvp in layerRoots)
{
var layer = kvp.Key;
var root = kvp.Value;
var uiList = new List<GameObject>();
foreach (Transform child in root)
{
UIBase uiBase = child.GetComponent<UIBase>();
if (uiBase != null)
uiList.Add(uiBase.gameObject);
}
result[layer] = uiList;
}
return result;
}
private void UpdateCursorState()
{
bool shouldLockCursor = !IsHasNonBackgroundUIActive; //&& !isInMainMenu; // 仅在没有非Background UI且不在主菜单时锁定鼠标
if (shouldLockCursor)
{
InputManager.Instance.SetCursorState(false, CursorLockMode.Locked);
}
else
{
InputManager.Instance.SetCursorState(true, CursorLockMode.None);
}
InputManager.Instance.SetInputForLook(shouldLockCursor);
InputManager.Instance.SetInputForMove(shouldLockCursor);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d2e569d59b9a9da499d7a33a426c18f6