feat(init): 搭建初始的项目框架
This commit is contained in:
17
Assets/Script/Core/Core.asmdef
Normal file
17
Assets/Script/Core/Core.asmdef
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Core",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:9e4105fe56ff4b1789a1683a3c08d507",
|
||||
"GUID:75469ad4d38634e559750d17036d5f7c"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
3
Assets/Script/Core/Core.asmdef.meta
Normal file
3
Assets/Script/Core/Core.asmdef.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd0e97c21c15497f9406b8ee23c1f67e
|
||||
timeCreated: 1760361503
|
13
Assets/Script/Core/GameManager.cs
Normal file
13
Assets/Script/Core/GameManager.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
public class GameManager : MonoSingleton<GameManager>
|
||||
{
|
||||
private void Start()
|
||||
{
|
||||
ScenesManager.Instance.LoadMainMenu();
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/Script/Core/GameManager.cs.meta
Normal file
3
Assets/Script/Core/GameManager.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db200c814b6e4465843d7ebc113fd9d0
|
||||
timeCreated: 1760362855
|
8
Assets/Script/Core/Input.meta
Normal file
8
Assets/Script/Core/Input.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 656856e514875b444af5c8db9035beea
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
91
Assets/Script/Core/Input/InputManager.cs
Normal file
91
Assets/Script/Core/Input/InputManager.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
public class InputManager : MonoSingleton<InputManager>
|
||||
{
|
||||
private PlayerInputActions _input; // 自动生成的输入类
|
||||
|
||||
// 当前输入值
|
||||
public Vector2 Move { get; private set; }
|
||||
public Vector2 Look { get; private set; }
|
||||
public bool PausePressed { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_input = new PlayerInputActions();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_input.Enable();
|
||||
|
||||
// 注册事件
|
||||
_input.Player.Move.performed += ctx => Move = ctx.ReadValue<Vector2>();
|
||||
_input.Player.Move.canceled += ctx => Move = Vector2.zero;
|
||||
|
||||
_input.Player.Look.performed += ctx => Look = ctx.ReadValue<Vector2>();
|
||||
_input.Player.Look.canceled += ctx => Look = Vector2.zero;
|
||||
//
|
||||
// _input.Player.Jump.performed += ctx => JumpPressed = true;
|
||||
// _input.Player.Jump.canceled += ctx => JumpPressed = false;
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
_input.Disable();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 在此更新一次性触发的输入,例如“按下瞬间触发”
|
||||
// if (PausePressed)
|
||||
// {
|
||||
// Debug.Log("Pause Pressed!");
|
||||
// PausePressed = false; // 手动清除
|
||||
// }
|
||||
}
|
||||
|
||||
// 🔧 示例方法:允许外部模块手动启用/禁用输入(比如暂停菜单)
|
||||
public void SetInputEnabled(bool enabled)
|
||||
{
|
||||
if (enabled) _input.Enable();
|
||||
else _input.Disable();
|
||||
}
|
||||
|
||||
public void SetCursorState(bool visible, CursorLockMode lockMode)
|
||||
{
|
||||
Cursor.visible = visible;
|
||||
Cursor.lockState = lockMode;
|
||||
}
|
||||
|
||||
public void SetInputForLook(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
_input.Player.Look.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
_input.Player.Look.Disable();
|
||||
Look = Vector2.zero; // 禁用时清除Look值
|
||||
}
|
||||
}
|
||||
|
||||
public void SetInputForMove(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
{
|
||||
_input.Player.Move.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
_input.Player.Move.Disable();
|
||||
Move = Vector2.zero; // 禁用时清除Move值
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/Script/Core/Input/InputManager.cs.meta
Normal file
3
Assets/Script/Core/Input/InputManager.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9d73a7b65ad4d43a9587b5060528a3c
|
||||
timeCreated: 1760403080
|
205
Assets/Script/Core/Input/PlayerInputActions.cs
Normal file
205
Assets/Script/Core/Input/PlayerInputActions.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator
|
||||
// version 1.11.2
|
||||
// from Assets/Settings/Input/PlayerInputActions.inputactions
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.Utilities;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
public partial class @PlayerInputActions: IInputActionCollection2, IDisposable
|
||||
{
|
||||
public InputActionAsset asset { get; }
|
||||
public @PlayerInputActions()
|
||||
{
|
||||
asset = InputActionAsset.FromJson(@"{
|
||||
""name"": ""PlayerInputActions"",
|
||||
""maps"": [
|
||||
{
|
||||
""name"": ""Player"",
|
||||
""id"": ""c76c22e9-2e4a-4535-bf00-ee32c0071ec4"",
|
||||
""actions"": [
|
||||
{
|
||||
""name"": ""Move"",
|
||||
""type"": ""Button"",
|
||||
""id"": ""cba876cd-5594-42ac-a4b0-2f2ed0f0e120"",
|
||||
""expectedControlType"": """",
|
||||
""processors"": """",
|
||||
""interactions"": """",
|
||||
""initialStateCheck"": false
|
||||
},
|
||||
{
|
||||
""name"": ""Look"",
|
||||
""type"": ""Value"",
|
||||
""id"": ""e288a319-23aa-463c-bd94-e6a00eec4e3b"",
|
||||
""expectedControlType"": ""Vector2"",
|
||||
""processors"": """",
|
||||
""interactions"": """",
|
||||
""initialStateCheck"": true
|
||||
}
|
||||
],
|
||||
""bindings"": [
|
||||
{
|
||||
""name"": """",
|
||||
""id"": ""dab4d8f6-9492-4327-8944-76f09907ba54"",
|
||||
""path"": ""<Keyboard>/w"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": """",
|
||||
""action"": ""Move"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": false
|
||||
},
|
||||
{
|
||||
""name"": """",
|
||||
""id"": ""d7c34f0b-b18e-4498-8d3e-1bdec4cb355d"",
|
||||
""path"": ""<Mouse>/position"",
|
||||
""interactions"": """",
|
||||
""processors"": """",
|
||||
""groups"": """",
|
||||
""action"": ""Look"",
|
||||
""isComposite"": false,
|
||||
""isPartOfComposite"": false
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
""controlSchemes"": []
|
||||
}");
|
||||
// Player
|
||||
m_Player = asset.FindActionMap("Player", throwIfNotFound: true);
|
||||
m_Player_Move = m_Player.FindAction("Move", throwIfNotFound: true);
|
||||
m_Player_Look = m_Player.FindAction("Look", throwIfNotFound: true);
|
||||
}
|
||||
|
||||
~@PlayerInputActions()
|
||||
{
|
||||
UnityEngine.Debug.Assert(!m_Player.enabled, "This will cause a leak and performance issues, PlayerInputActions.Player.Disable() has not been called.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UnityEngine.Object.Destroy(asset);
|
||||
}
|
||||
|
||||
public InputBinding? bindingMask
|
||||
{
|
||||
get => asset.bindingMask;
|
||||
set => asset.bindingMask = value;
|
||||
}
|
||||
|
||||
public ReadOnlyArray<InputDevice>? devices
|
||||
{
|
||||
get => asset.devices;
|
||||
set => asset.devices = value;
|
||||
}
|
||||
|
||||
public ReadOnlyArray<InputControlScheme> controlSchemes => asset.controlSchemes;
|
||||
|
||||
public bool Contains(InputAction action)
|
||||
{
|
||||
return asset.Contains(action);
|
||||
}
|
||||
|
||||
public IEnumerator<InputAction> GetEnumerator()
|
||||
{
|
||||
return asset.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Enable()
|
||||
{
|
||||
asset.Enable();
|
||||
}
|
||||
|
||||
public void Disable()
|
||||
{
|
||||
asset.Disable();
|
||||
}
|
||||
|
||||
public IEnumerable<InputBinding> bindings => asset.bindings;
|
||||
|
||||
public InputAction FindAction(string actionNameOrId, bool throwIfNotFound = false)
|
||||
{
|
||||
return asset.FindAction(actionNameOrId, throwIfNotFound);
|
||||
}
|
||||
|
||||
public int FindBinding(InputBinding bindingMask, out InputAction action)
|
||||
{
|
||||
return asset.FindBinding(bindingMask, out action);
|
||||
}
|
||||
|
||||
// Player
|
||||
private readonly InputActionMap m_Player;
|
||||
private List<IPlayerActions> m_PlayerActionsCallbackInterfaces = new List<IPlayerActions>();
|
||||
private readonly InputAction m_Player_Move;
|
||||
private readonly InputAction m_Player_Look;
|
||||
public struct PlayerActions
|
||||
{
|
||||
private @PlayerInputActions m_Wrapper;
|
||||
public PlayerActions(@PlayerInputActions wrapper) { m_Wrapper = wrapper; }
|
||||
public InputAction @Move => m_Wrapper.m_Player_Move;
|
||||
public InputAction @Look => m_Wrapper.m_Player_Look;
|
||||
public InputActionMap Get() { return m_Wrapper.m_Player; }
|
||||
public void Enable() { Get().Enable(); }
|
||||
public void Disable() { Get().Disable(); }
|
||||
public bool enabled => Get().enabled;
|
||||
public static implicit operator InputActionMap(PlayerActions set) { return set.Get(); }
|
||||
public void AddCallbacks(IPlayerActions instance)
|
||||
{
|
||||
if (instance == null || m_Wrapper.m_PlayerActionsCallbackInterfaces.Contains(instance)) return;
|
||||
m_Wrapper.m_PlayerActionsCallbackInterfaces.Add(instance);
|
||||
@Move.started += instance.OnMove;
|
||||
@Move.performed += instance.OnMove;
|
||||
@Move.canceled += instance.OnMove;
|
||||
@Look.started += instance.OnLook;
|
||||
@Look.performed += instance.OnLook;
|
||||
@Look.canceled += instance.OnLook;
|
||||
}
|
||||
|
||||
private void UnregisterCallbacks(IPlayerActions instance)
|
||||
{
|
||||
@Move.started -= instance.OnMove;
|
||||
@Move.performed -= instance.OnMove;
|
||||
@Move.canceled -= instance.OnMove;
|
||||
@Look.started -= instance.OnLook;
|
||||
@Look.performed -= instance.OnLook;
|
||||
@Look.canceled -= instance.OnLook;
|
||||
}
|
||||
|
||||
public void RemoveCallbacks(IPlayerActions instance)
|
||||
{
|
||||
if (m_Wrapper.m_PlayerActionsCallbackInterfaces.Remove(instance))
|
||||
UnregisterCallbacks(instance);
|
||||
}
|
||||
|
||||
public void SetCallbacks(IPlayerActions instance)
|
||||
{
|
||||
foreach (var item in m_Wrapper.m_PlayerActionsCallbackInterfaces)
|
||||
UnregisterCallbacks(item);
|
||||
m_Wrapper.m_PlayerActionsCallbackInterfaces.Clear();
|
||||
AddCallbacks(instance);
|
||||
}
|
||||
}
|
||||
public PlayerActions @Player => new PlayerActions(this);
|
||||
public interface IPlayerActions
|
||||
{
|
||||
void OnMove(InputAction.CallbackContext context);
|
||||
void OnLook(InputAction.CallbackContext context);
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Script/Core/Input/PlayerInputActions.cs.meta
Normal file
11
Assets/Script/Core/Input/PlayerInputActions.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff05c86378543b94488d735aea6436d1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
62
Assets/Script/Core/MonoSingleton.cs
Normal file
62
Assets/Script/Core/MonoSingleton.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
/// <summary>
|
||||
/// MonoBehavior抽象单例基类。确保继承类在场景中只有一个实例,并提供全局访问点。
|
||||
/// </summary>
|
||||
/// <typeparam name="T">必须是继承自MonoBehaviour的类型。</typeparam>
|
||||
public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
|
||||
{
|
||||
// 存储单例实例
|
||||
private static T instance;
|
||||
|
||||
/// <summary>
|
||||
/// 静态公共属性,用于获取单例实例(延迟加载)。
|
||||
/// </summary>
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
// 尝试在场景中查找现有实例
|
||||
instance = FindObjectOfType<T>();
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
// 如果没有,则创建新的游戏对象并附加该组件
|
||||
GameObject gameObject = new GameObject(typeof(T).Name);
|
||||
instance = gameObject.AddComponent<T>();
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
// 将当前对象设置为单例实例
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this as T;
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果场景中已存在其他实例,则销毁当前重复的对象
|
||||
if (instance != this as T)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
//清除静态实例,以防止在特定情况下出现“幽灵”实例。
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
if (instance == this)
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/Script/Core/MonoSingleton.cs.meta
Normal file
3
Assets/Script/Core/MonoSingleton.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6622c14f3a9d4861b696d1920dd54059
|
||||
timeCreated: 1760361568
|
84
Assets/Script/Core/ScenesManager.cs
Normal file
84
Assets/Script/Core/ScenesManager.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Collections;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
public class ScenesManager : MonoSingleton<ScenesManager>
|
||||
{
|
||||
public static ScenesManager Instance { get; private set; }
|
||||
|
||||
private string currentGameplayScene;
|
||||
|
||||
private bool IsLoadGameplayUI = false;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
//DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
public void LoadMainMenu()
|
||||
{
|
||||
if (IsLoadGameplayUI)
|
||||
{
|
||||
// 如果已经加载过游戏内UI场景,则卸载掉
|
||||
StartCoroutine(UnloadScene("UIScene"));
|
||||
IsLoadGameplayUI = false;
|
||||
}
|
||||
// 加载主菜单场景,需要卸载掉除了Core以外的所有场景
|
||||
StartCoroutine(SwitchGameplay("StartMenu"));
|
||||
}
|
||||
|
||||
|
||||
public void LoadGameplay(string sceneName)
|
||||
{
|
||||
StartCoroutine(SwitchGameplay(sceneName));
|
||||
if (!IsLoadGameplayUI)
|
||||
{
|
||||
// 只加载一次游戏内UI场景
|
||||
StartCoroutine(LoadSceneAdditive("UIScene"));
|
||||
IsLoadGameplayUI = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 切换游戏场景,卸载当前的游戏场景,加载新的游戏场景
|
||||
/// 适用于在游戏内切换场景
|
||||
/// </summary>
|
||||
/// <param name="newScene"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerator SwitchGameplay(string newScene)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(currentGameplayScene))
|
||||
{
|
||||
yield return UnloadScene(currentGameplayScene);
|
||||
}
|
||||
yield return LoadSceneAdditive(newScene);
|
||||
currentGameplayScene = newScene;
|
||||
}
|
||||
|
||||
private IEnumerator LoadSceneAdditive(string sceneName)
|
||||
{
|
||||
var async = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
|
||||
while (!async.isDone)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator UnloadScene(string sceneName)
|
||||
{
|
||||
var async = UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(sceneName);
|
||||
while (!async.isDone)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3
Assets/Script/Core/ScenesManager.cs.meta
Normal file
3
Assets/Script/Core/ScenesManager.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f0b4ab0cd384d91bfb99fd97c34ac5d
|
||||
timeCreated: 1760401308
|
105
Assets/Script/Core/ServiceLocator.cs
Normal file
105
Assets/Script/Core/ServiceLocator.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Locator 基类,支持父级查找机制。
|
||||
/// </summary>
|
||||
public abstract class ServiceLocatorBase
|
||||
{
|
||||
protected readonly Dictionary<Type, object> _services = new();
|
||||
|
||||
/// <summary>父 Locator,用于层级查找。</summary>
|
||||
public ServiceLocatorBase Parent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个服务实例。
|
||||
/// </summary>
|
||||
public virtual void Register<T>(T service)
|
||||
{
|
||||
_services[typeof(T)] = service;
|
||||
OnServiceRegistered?.Invoke(typeof(T), service);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注销一个服务。
|
||||
/// </summary>
|
||||
public virtual void Unregister<T>(T service)
|
||||
{
|
||||
if (_services.TryGetValue(typeof(T), out var instance) && instance.Equals(service))
|
||||
_services.Remove(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析服务(支持向父级递归查找)。
|
||||
/// </summary>
|
||||
public virtual T Resolve<T>()
|
||||
{
|
||||
if (_services.TryGetValue(typeof(T), out var instance))
|
||||
return (T)instance;
|
||||
|
||||
return Parent != null ? Parent.Resolve<T>() : default;
|
||||
}
|
||||
|
||||
/// <summary>清空所有服务。</summary>
|
||||
public virtual void Clear() => _services.Clear();
|
||||
|
||||
/// <summary>服务注册事件:当某个服务注册时触发。</summary>
|
||||
public event Action<Type, object> OnServiceRegistered;
|
||||
|
||||
/// <summary>
|
||||
/// 尝试立即获取服务。
|
||||
/// </summary>
|
||||
public virtual bool TryGet<T>(out T service)
|
||||
{
|
||||
if (_services.TryGetValue(typeof(T), out var instance))
|
||||
{
|
||||
service = (T)instance;
|
||||
return true;
|
||||
}
|
||||
else if (Parent != null)
|
||||
{
|
||||
return Parent.TryGet(out service);
|
||||
}
|
||||
|
||||
service = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试获取服务;若不存在,则等待其注册。
|
||||
/// </summary>
|
||||
public virtual bool TryGetWait<T>(Action<T> onGet)
|
||||
{
|
||||
if (TryGet<T>(out var service))
|
||||
{
|
||||
onGet?.Invoke(service);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Handler(Type type, object instance)
|
||||
{
|
||||
if (type == typeof(T))
|
||||
{
|
||||
onGet?.Invoke((T)instance);
|
||||
OnServiceRegistered -= Handler;
|
||||
}
|
||||
}
|
||||
|
||||
OnServiceRegistered += Handler;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 泛型单例版本的 Locator。
|
||||
/// </summary>
|
||||
public abstract class ServiceLocator<T> : ServiceLocatorBase where T : ServiceLocator<T>, new()
|
||||
{
|
||||
private static readonly Lazy<T> _instance = new(() => new T());
|
||||
|
||||
/// <summary>全局访问入口。</summary>
|
||||
public static T Instance => _instance.Value;
|
||||
}
|
||||
}
|
3
Assets/Script/Core/ServiceLocator.cs.meta
Normal file
3
Assets/Script/Core/ServiceLocator.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a04b02262c7d4e17b69925310da0ab5a
|
||||
timeCreated: 1760363734
|
3
Assets/Script/Core/UI.meta
Normal file
3
Assets/Script/Core/UI.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f2c59e63b24341b4802b1c395c29d815
|
||||
timeCreated: 1760404455
|
37
Assets/Script/Core/UI/UIBase.cs
Normal file
37
Assets/Script/Core/UI/UIBase.cs
Normal 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.
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Script/Core/UI/UIBase.cs.meta
Normal file
11
Assets/Script/Core/UI/UIBase.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68b45d1df56287c4ba5489104835f768
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
32
Assets/Script/Core/UI/UICamera.cs
Normal file
32
Assets/Script/Core/UI/UICamera.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
11
Assets/Script/Core/UI/UICamera.cs.meta
Normal file
11
Assets/Script/Core/UI/UICamera.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6653afda52a99784a9e0d5650ce2df10
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
18
Assets/Script/Core/UI/UILayerRoot.cs
Normal file
18
Assets/Script/Core/UI/UILayerRoot.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
11
Assets/Script/Core/UI/UILayerRoot.cs.meta
Normal file
11
Assets/Script/Core/UI/UILayerRoot.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a5cdded3cae71848bcb0e7ca91e4570
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
139
Assets/Script/Core/UI/UIManager.cs
Normal file
139
Assets/Script/Core/UI/UIManager.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
2
Assets/Script/Core/UI/UIManager.cs.meta
Normal file
2
Assets/Script/Core/UI/UIManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2e569d59b9a9da499d7a33a426c18f6
|
15
Assets/Script/Core/UIViewerControllerLocator.cs
Normal file
15
Assets/Script/Core/UIViewerControllerLocator.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 专用于视图控制器层的 Locator。
|
||||
/// 支持层级父Locator查找(例如:UI模块Locator → 游戏全局Locator)。
|
||||
/// </summary>
|
||||
public class UIViewerControllerLocator : ServiceLocator<UIViewerControllerLocator>
|
||||
{
|
||||
// 这里可以按需添加额外的特化功能。
|
||||
// 当前已经完整继承 Register、Resolve、TryGetWait 等功能。
|
||||
}
|
||||
}
|
3
Assets/Script/Core/UIViewerControllerLocator.cs.meta
Normal file
3
Assets/Script/Core/UIViewerControllerLocator.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db1ce8e01e77459d9eafe2da4a9b6c32
|
||||
timeCreated: 1760361806
|
Reference in New Issue
Block a user