chore: initial commit
This commit is contained in:
8
Assets/Scripts/Core/Assets.meta
Normal file
8
Assets/Scripts/Core/Assets.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f475eab1c9fa38649bf17b8b68d06d68
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
84
Assets/Scripts/Core/Assets/AddressKeyRegistry.cs
Normal file
84
Assets/Scripts/Core/Assets/AddressKeyRegistry.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Assets
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行时 Addressable Key 注册层(架构 13_AssetPoolModule §9)。
|
||||
/// 供 DLC / 扩展包在运行时动态注册额外地址键,不修改编译期常量类 <see cref="AddressKeys"/>。
|
||||
///
|
||||
/// 用法:
|
||||
/// // 注册(DLC 模块 Awake 时)
|
||||
/// AddressKeyRegistry.TryRegister("DLC_WeaponScythe", "DLC/WPN_Scythe");
|
||||
///
|
||||
/// // 查询(GlobalObjectPool.SpawnInternal 内部调用)
|
||||
/// if (AddressKeyRegistry.TryResolve(key, out var addr)) { ... }
|
||||
/// </summary>
|
||||
public static class AddressKeyRegistry
|
||||
{
|
||||
private static readonly Dictionary<string, string> _registry = new();
|
||||
|
||||
/// <summary>
|
||||
/// 注册一个运行时 key → Addressable 地址映射。
|
||||
/// 若 key 已存在则跳过,返回 false。
|
||||
/// </summary>
|
||||
public static bool TryRegister(string key, string address)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(address))
|
||||
{
|
||||
Debug.LogWarning($"[AddressKeyRegistry] TryRegister: key 或 address 不能为空。key={key}, address={address}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_registry.ContainsKey(key))
|
||||
{
|
||||
Debug.LogWarning($"[AddressKeyRegistry] key 已存在,跳过注册:{key}");
|
||||
return false;
|
||||
}
|
||||
|
||||
_registry[key] = address;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制覆盖注册(用于测试 / 热更新覆盖)。
|
||||
/// </summary>
|
||||
public static void ForceRegister(string key, string address)
|
||||
{
|
||||
_registry[key] = address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析 key,返回对应的 Addressable 地址字符串。
|
||||
/// 若 key 未注册则返回原 key(兼容直接使用静态常量的调用方)。
|
||||
/// </summary>
|
||||
public static string Resolve(string key)
|
||||
{
|
||||
return _registry.TryGetValue(key, out var address) ? address : key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试解析 key,成功时 out address 为注册的地址字符串,返回 true。
|
||||
/// 若 key 未注册则返回 false,address 为 null。
|
||||
/// </summary>
|
||||
public static bool TryResolve(string key, out string address)
|
||||
{
|
||||
return _registry.TryGetValue(key, out address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除指定 key 的注册(场景卸载/DLC 卸载时调用)。
|
||||
/// </summary>
|
||||
public static void Unregister(string key) => _registry.Remove(key);
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有注册(仅用于测试)。
|
||||
/// </summary>
|
||||
public static void Clear() => _registry.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// 返回当前注册的所有 key 数量(调试用)。
|
||||
/// </summary>
|
||||
public static int Count => _registry.Count;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Assets/AddressKeyRegistry.cs.meta
Normal file
11
Assets/Scripts/Core/Assets/AddressKeyRegistry.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a64caba864b15a499c94f65e356e14e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
52
Assets/Scripts/Core/Assets/AddressKeys.cs
Normal file
52
Assets/Scripts/Core/Assets/AddressKeys.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
namespace BaseGames.Core.Assets
|
||||
{
|
||||
/// <summary>
|
||||
/// 所有 Addressable 地址字符串的静态常量类。
|
||||
/// 禁止在代码中直接使用字面字符串,统一引用此处的 const。
|
||||
/// 与 Assets 的 Addressable 标签分配保持同步(AddressKeyValidator 工具验证)。
|
||||
/// </summary>
|
||||
public static class AddressKeys
|
||||
{
|
||||
// ── Scenes ──────────────────────────────────────────────────────
|
||||
public const string ScenePersistent = "Scene_Persistent";
|
||||
public const string SceneMainMenu = "Scene_MainMenu";
|
||||
|
||||
// ── Player ──────────────────────────────────────────────────────
|
||||
public const string PrefabPlayer = "PLY_Player";
|
||||
|
||||
// ── Enemies ─────────────────────────────────────────────────────
|
||||
public const string PrefabEnemyGrunt = "ENM_GruntWarrior";
|
||||
public const string PrefabEnemySkullArch = "ENM_SkullArcher";
|
||||
|
||||
// ── Projectiles ─────────────────────────────────────────────────
|
||||
public const string PrefabProjArrow = "PROJ_Arrow";
|
||||
public const string PrefabProjFireball = "PROJ_Fireball";
|
||||
public const string PrefabProjSoulBall = "PROJ_SoulBall";
|
||||
|
||||
// ── VFX ─────────────────────────────────────────────────────────
|
||||
public const string PrefabVFXHitSpark = "VFX_HitSpark";
|
||||
public const string PrefabVFXBloodSplat = "VFX_BloodSplat";
|
||||
public const string PrefabVFXExplosion = "VFX_Explosion";
|
||||
|
||||
// ── UI ───────────────────────────────────────────────────────────
|
||||
public const string PrefabUIFloatingDmgText = "UI_FloatingDamageText";
|
||||
|
||||
// ── Collectibles ─────────────────────────────────────────────────
|
||||
public const string PrefabCollectibleGeo = "COL_Geo";
|
||||
public const string PrefabCollectibleHPOrb = "COL_HPOrb";
|
||||
|
||||
// ── Weapons ──────────────────────────────────────────────────────
|
||||
public const string PrefabWeaponSkyBlade = "WPN_SkyBlade";
|
||||
public const string PrefabWeaponEarthClaw = "WPN_EarthClaw";
|
||||
public const string PrefabWeaponSoulStaff = "WPN_SoulStaff";
|
||||
|
||||
// ── Config ScriptableObjects ─────────────────────────────────────
|
||||
public const string DataFootstepCatalog = "Config/FootstepCatalog";
|
||||
|
||||
// ── Labels(批量加载用)──────────────────────────────────────────
|
||||
public const string LabelEnemy = "Enemy";
|
||||
public const string LabelPoolable = "Poolable";
|
||||
public const string LabelBGM = "BGM";
|
||||
public const string LabelCharms = "Charms";
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Assets/AddressKeys.cs.meta
Normal file
11
Assets/Scripts/Core/Assets/AddressKeys.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: faaab8ae1b4f5584688d2c294fcffa1a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
53
Assets/Scripts/Core/Assets/AssetLoader.cs
Normal file
53
Assets/Scripts/Core/Assets/AssetLoader.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace BaseGames.Core.Assets
|
||||
{
|
||||
/// <summary>
|
||||
/// Addressables 运行时加载工具(薄封装)。
|
||||
/// 场景卸载时配合 <see cref="AssetReleaseTracker"/> 批量 Release。
|
||||
/// </summary>
|
||||
public static class AssetLoader
|
||||
{
|
||||
/// <summary>异步加载资产,返回 handle 供 Release 使用。</summary>
|
||||
public static async Task<(T asset, AsyncOperationHandle<T> handle)> LoadAsync<T>(string addressKey)
|
||||
{
|
||||
var handle = Addressables.LoadAssetAsync<T>(addressKey);
|
||||
var result = await handle.Task;
|
||||
return (result, handle);
|
||||
}
|
||||
|
||||
/// <summary>释放一个已加载的 handle(引用计数 -1)。</summary>
|
||||
public static void Release<T>(AsyncOperationHandle<T> handle)
|
||||
{
|
||||
if (handle.IsValid()) Addressables.Release(handle);
|
||||
}
|
||||
|
||||
/// <summary>释放一个 GameObject 实例(Addressables.ReleaseInstance)。</summary>
|
||||
public static bool ReleaseInstance(GameObject go)
|
||||
=> Addressables.ReleaseInstance(go);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 场景卸载时批量释放由 Addressables 加载的资产句柄。
|
||||
/// 挂在场景的根 GameObject 上;OnDestroy 时自动 Release 注册的所有 handle。
|
||||
/// </summary>
|
||||
public class AssetReleaseTracker : MonoBehaviour
|
||||
{
|
||||
private readonly List<AsyncOperationHandle> _handles = new();
|
||||
|
||||
public void Track<T>(AsyncOperationHandle<T> handle)
|
||||
=> _handles.Add(handle);
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
foreach (var h in _handles)
|
||||
if (h.IsValid()) Addressables.Release(h);
|
||||
_handles.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Assets/AssetLoader.cs.meta
Normal file
11
Assets/Scripts/Core/Assets/AssetLoader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b53704ca12a83b479fc3967704c642b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
46
Assets/Scripts/Core/Assets/AssetReleaseTracker.cs
Normal file
46
Assets/Scripts/Core/Assets/AssetReleaseTracker.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using UnityEngine;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Core.Assets;
|
||||
using BaseGames.Core.Pool;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 资产释放跟踪器。
|
||||
/// 事件驱动:监听 EVT_SceneLoadRequest,在新场景加载前清理旧场景的对象池缓存。
|
||||
/// ⚠️ 不使用显式注册 API;GlobalObjectPool.ClearPool 在场景切换时批量清理。
|
||||
/// </summary>
|
||||
public class AssetReleaseTracker : MonoBehaviour
|
||||
{
|
||||
[Header("Event Channels")]
|
||||
[SerializeField] private SceneLoadRequestEventChannelSO _onSceneLoadRequest;
|
||||
|
||||
private string _lastLoadedScene;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_onSceneLoadRequest != null)
|
||||
_onSceneLoadRequest.OnEventRaised += OnSceneLoadRequested;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_onSceneLoadRequest != null)
|
||||
_onSceneLoadRequest.OnEventRaised -= OnSceneLoadRequested;
|
||||
}
|
||||
|
||||
private void OnSceneLoadRequested(SceneLoadRequest req)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_lastLoadedScene)) { _lastLoadedScene = req.SceneName; return; }
|
||||
|
||||
// 清除旧场景的敌人对象池缓存(按需扩展)
|
||||
if (GlobalObjectPool.Instance != null)
|
||||
{
|
||||
GlobalObjectPool.Instance.ClearPool(AddressKeys.PrefabEnemyGrunt);
|
||||
GlobalObjectPool.Instance.ClearPool(AddressKeys.PrefabEnemySkullArch);
|
||||
}
|
||||
|
||||
_lastLoadedScene = req.SceneName;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Assets/AssetReleaseTracker.cs.meta
Normal file
11
Assets/Scripts/Core/Assets/AssetReleaseTracker.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba91049904d9daa43a59032bc1481cd1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Assets/Scripts/Core/BaseGames.Core.asmdef
Normal file
19
Assets/Scripts/Core/BaseGames.Core.asmdef
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"precompiledReferences": [],
|
||||
"name": "BaseGames.Core",
|
||||
"defineConstraints": [],
|
||||
"noEngineReferences": false,
|
||||
"versionDefines": [],
|
||||
"rootNamespace": "BaseGames.Core",
|
||||
"references": [
|
||||
"BaseGames.Core.Events",
|
||||
"BaseGames.Core.Save",
|
||||
"Unity.Addressables",
|
||||
"Unity.ResourceManager"
|
||||
],
|
||||
"autoReferenced": true,
|
||||
"overrideReferences": false,
|
||||
"includePlatforms": []
|
||||
}
|
||||
7
Assets/Scripts/Core/BaseGames.Core.asmdef.meta
Normal file
7
Assets/Scripts/Core/BaseGames.Core.asmdef.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e41e18c796a92334c8eb801039fc7440
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
141
Assets/Scripts/Core/BuiltinGameStates.cs
Normal file
141
Assets/Scripts/Core/BuiltinGameStates.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
using System.Collections.Generic;
|
||||
using BaseGames.Core;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Core.States
|
||||
{
|
||||
/// <summary>初始化状态(应用启动时的第一个状态)。</summary>
|
||||
public class InitializingState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.Initializing;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId> { GameStates.MainMenu };
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>主菜单状态。</summary>
|
||||
public class MainMenuState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.MainMenu;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId> { GameStates.LoadingScene };
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>场景加载中状态。</summary>
|
||||
public class LoadingSceneState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.LoadingScene;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId> { GameStates.MainMenu, GameStates.Gameplay };
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>正常游玩状态。</summary>
|
||||
public class GameplayState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.Gameplay;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId>
|
||||
{
|
||||
GameStates.LoadingScene,
|
||||
GameStates.BossFight,
|
||||
GameStates.Paused,
|
||||
GameStates.Dead,
|
||||
GameStates.Cutscene,
|
||||
};
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>Boss 战状态。</summary>
|
||||
public class BossFightState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.BossFight;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId>
|
||||
{
|
||||
GameStates.LoadingScene,
|
||||
GameStates.Gameplay,
|
||||
GameStates.Paused,
|
||||
GameStates.Dead,
|
||||
};
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>暂停状态。</summary>
|
||||
public class PausedState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.Paused;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId>
|
||||
{
|
||||
GameStates.Gameplay,
|
||||
GameStates.BossFight,
|
||||
GameStates.MainMenu,
|
||||
};
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>死亡状态。</summary>
|
||||
public class DeadState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.Dead;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId> { GameStates.LoadingScene, GameStates.GameOver };
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>过场动画状态。</summary>
|
||||
public class CutsceneState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.Cutscene;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId> { GameStates.Gameplay };
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
|
||||
/// <summary>Game Over 状态(SteelSoul 清档用)。</summary>
|
||||
public class GameOverState : IGameState
|
||||
{
|
||||
public GameStateId Id => GameStates.GameOver;
|
||||
|
||||
public IReadOnlyCollection<GameStateId> ValidNextStates { get; } =
|
||||
new HashSet<GameStateId> { GameStates.MainMenu };
|
||||
|
||||
public void OnEnter(GameStateId prev) { }
|
||||
public void OnExit(GameStateId next) { }
|
||||
public void Tick(float dt) { }
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/BuiltinGameStates.cs.meta
Normal file
11
Assets/Scripts/Core/BuiltinGameStates.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9bcf1a51cdbfa94a8e3d63c25626d34
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
78
Assets/Scripts/Core/DeathRespawnService.cs
Normal file
78
Assets/Scripts/Core/DeathRespawnService.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 死亡/复活流程服务接口。
|
||||
/// </summary>
|
||||
public interface IDeathRespawnService
|
||||
{
|
||||
/// <summary>玩家死亡时由 GameManager 调用,启动死亡演出流程。</summary>
|
||||
IEnumerator StartDeathSequenceCoroutine();
|
||||
|
||||
/// <summary>DeathScreen 确认按钮点击后调用,执行复活流程。</summary>
|
||||
IEnumerator StartRespawnCoroutine();
|
||||
|
||||
/// <summary>SteelSoul 模式:HP 归零后直接清档并返回主菜单。</summary>
|
||||
IEnumerator StartGameOverCoroutine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 死亡/复活流程独立服务(Phase 0 骨架,Phase 1 完整实现)。
|
||||
/// </summary>
|
||||
public class DeathRespawnService : MonoBehaviour, IDeathRespawnService
|
||||
{
|
||||
[Header("Config")]
|
||||
[SerializeField] private float _deathAnimDuration = 1.2f;
|
||||
[SerializeField] private float _deathScreenDelay = 0.5f;
|
||||
[SerializeField] private float _respawnFadeDuration = 0.4f;
|
||||
|
||||
[Header("Event Channels - Raise")]
|
||||
[SerializeField] private VoidEventChannelSO _onRespawnStarted;
|
||||
[SerializeField] private VoidEventChannelSO _onRespawnCompleted;
|
||||
|
||||
[Header("Event Channels - Listen")]
|
||||
[SerializeField] private VoidEventChannelSO _onDeathScreenConfirmed;
|
||||
|
||||
private bool _deathConfirmed;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_onDeathScreenConfirmed != null)
|
||||
_onDeathScreenConfirmed.OnEventRaised += HandleDeathScreenConfirmed;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_onDeathScreenConfirmed != null)
|
||||
_onDeathScreenConfirmed.OnEventRaised -= HandleDeathScreenConfirmed;
|
||||
}
|
||||
|
||||
private void HandleDeathScreenConfirmed() => _deathConfirmed = true;
|
||||
|
||||
public IEnumerator StartDeathSequenceCoroutine()
|
||||
{
|
||||
yield return new WaitForSeconds(_deathAnimDuration);
|
||||
yield return new WaitForSeconds(_deathScreenDelay);
|
||||
_deathConfirmed = false;
|
||||
yield return new WaitUntil(() => _deathConfirmed);
|
||||
}
|
||||
|
||||
public IEnumerator StartRespawnCoroutine()
|
||||
{
|
||||
_onRespawnStarted?.Raise();
|
||||
yield return new WaitForSeconds(_respawnFadeDuration);
|
||||
// Phase 1:加载存档场景(TODO)
|
||||
yield return new WaitForSeconds(_respawnFadeDuration);
|
||||
_onRespawnCompleted?.Raise();
|
||||
}
|
||||
|
||||
public IEnumerator StartGameOverCoroutine()
|
||||
{
|
||||
// Phase 1:SteelSoul 清档并返回主菜单(TODO)
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/DeathRespawnService.cs.meta
Normal file
11
Assets/Scripts/Core/DeathRespawnService.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 843f5718ab6dbb7418fa7a036a83efc9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/Core/Events.meta
Normal file
8
Assets/Scripts/Core/Events.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3771751cae7bcd04b96f7d9026a962aa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
0
Assets/Scripts/Core/Events/.gitkeep
Normal file
0
Assets/Scripts/Core/Events/.gitkeep
Normal file
61
Assets/Scripts/Core/Events/BaseEventChannelSO.cs
Normal file
61
Assets/Scripts/Core/Events/BaseEventChannelSO.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 泛型 SO 事件频道基类。T 为负载类型。
|
||||
/// </summary>
|
||||
public abstract class BaseEventChannelSO<T> : ScriptableObject
|
||||
{
|
||||
[Multiline] public string description;
|
||||
|
||||
public event Action<T> OnEventRaised;
|
||||
|
||||
public void Raise(T value)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EventBusMonitor.Record(name, value?.ToString() ?? "null",
|
||||
OnEventRaised?.GetInvocationList().Length ?? 0);
|
||||
#endif
|
||||
OnEventRaised?.Invoke(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订阅并返回可 Dispose 的订阅句柄,配合 CompositeDisposable 使用。
|
||||
/// </summary>
|
||||
public EventSubscription Subscribe(Action<T> callback)
|
||||
{
|
||||
OnEventRaised += callback;
|
||||
return new EventSubscription(() => OnEventRaised -= callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 无负载事件频道基类。
|
||||
/// </summary>
|
||||
public abstract class VoidBaseEventChannelSO : ScriptableObject
|
||||
{
|
||||
[Multiline] public string description;
|
||||
|
||||
public event Action OnEventRaised;
|
||||
|
||||
public void Raise()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
EventBusMonitor.Record(name, "<void>",
|
||||
OnEventRaised?.GetInvocationList().Length ?? 0);
|
||||
#endif
|
||||
OnEventRaised?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 订阅并返回可 Dispose 的订阅句柄。
|
||||
/// </summary>
|
||||
public EventSubscription Subscribe(Action callback)
|
||||
{
|
||||
OnEventRaised += callback;
|
||||
return new EventSubscription(() => OnEventRaised -= callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/BaseEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/BaseEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adcb05b71d0e3f94f8c4446dee4c253b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Scripts/Core/Events/BaseGames.Core.Events.asmdef
Normal file
14
Assets/Scripts/Core/Events/BaseGames.Core.Events.asmdef
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "BaseGames.Core.Events",
|
||||
"rootNamespace": "BaseGames.Core.Events",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf44e3f184fd4214eb09a80e6e04d7df
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
22
Assets/Scripts/Core/Events/BossEvents.cs
Normal file
22
Assets/Scripts/Core/Events/BossEvents.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Boss 技能事件负载。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct BossSkillEvent
|
||||
{
|
||||
public string BossId;
|
||||
public string SkillId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boss 阶段切换事件负载。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct BossPhaseEvent
|
||||
{
|
||||
public string BossId;
|
||||
public int Phase;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/BossEvents.cs.meta
Normal file
11
Assets/Scripts/Core/Events/BossEvents.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ac3dc174ca0e12544a31550b5f61e70b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/BossPhaseEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/BossPhaseEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/BossPhase")]
|
||||
public class BossPhaseEventChannelSO : BaseEventChannelSO<BossPhaseEvent> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/BossPhaseEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/BossPhaseEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 647b6596e515ba64483b7ff337c76699
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/BossSkillEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/BossSkillEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/BossSkill")]
|
||||
public class BossSkillEventChannelSO : BaseEventChannelSO<BossSkillEvent> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/BossSkillEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/BossSkillEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8230eab2acba8c24499b2d20df81adb7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Scripts/Core/Events/ColorblindMode.cs
Normal file
14
Assets/Scripts/Core/Events/ColorblindMode.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 色觉辅助模式枚举(16_SupportingModules §AccessibilityManager)。
|
||||
/// </summary>
|
||||
public enum ColorblindMode
|
||||
{
|
||||
None = 0,
|
||||
Protanopia = 1,
|
||||
Deuteranopia = 2,
|
||||
Tritanopia = 3,
|
||||
Achromatopsia = 4
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/ColorblindMode.cs.meta
Normal file
11
Assets/Scripts/Core/Events/ColorblindMode.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7f46e87b21f6a045bc324853efecf9c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/ColorblindMode")]
|
||||
public class ColorblindModeEventChannelSO : BaseEventChannelSO<ColorblindMode> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9227f70cba3e7949a837c0622653f33
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Scripts/Core/Events/DamageInfo.cs
Normal file
9
Assets/Scripts/Core/Events/DamageInfo.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
// 此文件已废弃。DamageInfo / DamageInfoEventChannelSO 已迁移至
|
||||
// Assets/Scripts/Combat/DamageInfo.cs (namespace BaseGames.Combat)
|
||||
// 程序集 BaseGames.Combat.asmdef
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
// 保留空命名空间,避免 .meta 文件冲突。
|
||||
// ReSharper disable once EmptyNamespace
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/DamageInfo.cs.meta
Normal file
11
Assets/Scripts/Core/Events/DamageInfo.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94ca97e41a3560141a991928af7c1beb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
12
Assets/Scripts/Core/Events/DifficultyChangedEventChannel.cs
Normal file
12
Assets/Scripts/Core/Events/DifficultyChangedEventChannel.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 难度变更事件频道。Phase 2 难度系统使用。
|
||||
/// 发布:DifficultyScalerSO / SettingsManager
|
||||
/// 订阅:所有需要感知当前难度的系统
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Events/DifficultyChanged")]
|
||||
public class DifficultyChangedEventChannel : BaseEventChannelSO<DifficultyLevel> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b3f48b426d66154797d7c9af9c6b02f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
13
Assets/Scripts/Core/Events/DifficultyLevel.cs
Normal file
13
Assets/Scripts/Core/Events/DifficultyLevel.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 难度等级枚举。
|
||||
/// </summary>
|
||||
public enum DifficultyLevel
|
||||
{
|
||||
Easy = 0,
|
||||
Normal = 1,
|
||||
Hard = 2,
|
||||
SteelSoul = 3 // 一命模式
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/DifficultyLevel.cs.meta
Normal file
11
Assets/Scripts/Core/Events/DifficultyLevel.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa2ae8b438a843b458548868d0cfef3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
39
Assets/Scripts/Core/Events/EventBusMonitor.cs
Normal file
39
Assets/Scripts/Core/Events/EventBusMonitor.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行时事件总线监控(仅 Editor 下记录调用信息)。
|
||||
/// </summary>
|
||||
public static class EventBusMonitor
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
public struct EventRecord
|
||||
{
|
||||
public string ChannelName;
|
||||
public string Payload;
|
||||
public int ListenerCount;
|
||||
public System.DateTime Timestamp;
|
||||
}
|
||||
|
||||
private static readonly System.Collections.Generic.Queue<EventRecord> _records
|
||||
= new System.Collections.Generic.Queue<EventRecord>(256);
|
||||
|
||||
public static System.Collections.Generic.IEnumerable<EventRecord> Records => _records;
|
||||
|
||||
public static void Record(string channelName, string payload, int listenerCount)
|
||||
{
|
||||
if (_records.Count >= 256) _records.Dequeue();
|
||||
_records.Enqueue(new EventRecord
|
||||
{
|
||||
ChannelName = channelName,
|
||||
Payload = payload,
|
||||
ListenerCount = listenerCount,
|
||||
Timestamp = System.DateTime.Now
|
||||
});
|
||||
}
|
||||
|
||||
public static void Clear() => _records.Clear();
|
||||
#else
|
||||
public static void Record(string channelName, string payload, int listenerCount) { }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/EventBusMonitor.cs.meta
Normal file
11
Assets/Scripts/Core/Events/EventBusMonitor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d580e5431c172b34d8fc0252ff72774a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
54
Assets/Scripts/Core/Events/EventChannelRegistry.cs
Normal file
54
Assets/Scripts/Core/Events/EventChannelRegistry.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行时事件频道注册表。
|
||||
/// 在 Persistent 场景 Awake 时由 EventChannelRegistrar 注册所有频道 SO,
|
||||
/// 供不能持有 [SerializeField] 的动态对象(CharmEffect SO 等)按类型名查找频道。
|
||||
/// </summary>
|
||||
public class EventChannelRegistry : MonoBehaviour, IEventChannelRegistry
|
||||
{
|
||||
public static EventChannelRegistry Instance { get; private set; }
|
||||
|
||||
private readonly Dictionary<string, ScriptableObject> _channels = new();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
/// <summary>由 EventChannelRegistrar 在场景初始化时批量注册频道 SO。</summary>
|
||||
public void Register(string key, ScriptableObject channel)
|
||||
=> _channels[key] = channel;
|
||||
|
||||
/// <summary>
|
||||
/// 按 key 查找频道。key = SO 资产文件名(不含扩展名),如 "EVT_HitConfirmed"。
|
||||
/// </summary>
|
||||
public T Get<T>(string key) where T : ScriptableObject
|
||||
{
|
||||
if (_channels.TryGetValue(key, out var ch) && ch is T typed) return typed;
|
||||
Debug.LogError($"[EventChannelRegistry] Key '{key}' not found or wrong type ({typeof(T).Name}).");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>尝试获取频道,不报错。</summary>
|
||||
public bool TryGet<T>(string key, out T channel) where T : ScriptableObject
|
||||
{
|
||||
if (_channels.TryGetValue(key, out var ch) && ch is T typed)
|
||||
{
|
||||
channel = typed;
|
||||
return true;
|
||||
}
|
||||
channel = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/EventChannelRegistry.cs.meta
Normal file
11
Assets/Scripts/Core/Events/EventChannelRegistry.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 661043851605d4849bef40ea15c556b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
65
Assets/Scripts/Core/Events/EventSubscription.cs
Normal file
65
Assets/Scripts/Core/Events/EventSubscription.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 单条订阅的 Disposable 句柄。Dispose() 自动取消注册。
|
||||
/// </summary>
|
||||
public readonly struct EventSubscription : IDisposable
|
||||
{
|
||||
private readonly Action _unsubscribe;
|
||||
|
||||
public EventSubscription(Action unsubscribe)
|
||||
=> _unsubscribe = unsubscribe;
|
||||
|
||||
public void Dispose() => _unsubscribe?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量管理多条订阅,统一在 Dispose / Clear 时取消所有注册。
|
||||
/// 用法:OnEnable 调用 Subscribe,OnDisable 调用 Clear。
|
||||
/// </summary>
|
||||
public sealed class CompositeDisposable : IDisposable
|
||||
{
|
||||
private readonly List<IDisposable> _items = new();
|
||||
|
||||
public void Add(IDisposable item) => _items.Add(item);
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
foreach (var item in _items) item.Dispose();
|
||||
_items.Clear();
|
||||
}
|
||||
|
||||
public void Dispose() => Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="EventSubscription"/> 扩展方法(架构 02 §8)。
|
||||
/// </summary>
|
||||
public static class EventSubscriptionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 将订阅句柄添加到集合中,统一生命周期管理。
|
||||
/// 用法:channel.Subscribe(Handler).AddTo(_subscriptions);
|
||||
/// </summary>
|
||||
public static EventSubscription AddTo(this EventSubscription subscription,
|
||||
ICollection<IDisposable> collection)
|
||||
{
|
||||
collection.Add(subscription);
|
||||
return subscription;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将订阅句柄添加到 <see cref="CompositeDisposable"/> 中。
|
||||
/// </summary>
|
||||
public static EventSubscription AddTo(this EventSubscription subscription,
|
||||
CompositeDisposable compositeDisposable)
|
||||
{
|
||||
compositeDisposable.Add(subscription);
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/EventSubscription.cs.meta
Normal file
11
Assets/Scripts/Core/Events/EventSubscription.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15f50c6ab5d503b47bbcc8f73b32d3e1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/FloatEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/FloatEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/Float")]
|
||||
public class FloatEventChannelSO : BaseEventChannelSO<float> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/FloatEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/FloatEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62f2d5b0575789048b6badd7d1608db3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/GameEventChannels.cs
Normal file
7
Assets/Scripts/Core/Events/GameEventChannels.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/GameState")]
|
||||
public class GameStateEventChannelSO : BaseEventChannelSO<GameStateId> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/GameEventChannels.cs.meta
Normal file
11
Assets/Scripts/Core/Events/GameEventChannels.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c060c20fe37837c408cc5a628f6d8863
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
21
Assets/Scripts/Core/Events/GameStateId.cs
Normal file
21
Assets/Scripts/Core/Events/GameStateId.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏状态 ID(值类型,替代旧版 GameState 枚举)。
|
||||
/// 具体实例由 GameStates 静态工厂创建。
|
||||
/// </summary>
|
||||
public readonly struct GameStateId : System.IEquatable<GameStateId>
|
||||
{
|
||||
public readonly string Id;
|
||||
|
||||
public GameStateId(string id) => Id = id;
|
||||
|
||||
public bool Equals(GameStateId other) => Id == other.Id;
|
||||
public override bool Equals(object obj) => obj is GameStateId g && Equals(g);
|
||||
public override int GetHashCode() => Id?.GetHashCode() ?? 0;
|
||||
public override string ToString() => Id ?? "<null>";
|
||||
|
||||
public static bool operator ==(GameStateId a, GameStateId b) => a.Equals(b);
|
||||
public static bool operator !=(GameStateId a, GameStateId b) => !a.Equals(b);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/GameStateId.cs.meta
Normal file
11
Assets/Scripts/Core/Events/GameStateId.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: af0ccb15630f6ff419a4c526e96459f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
9
Assets/Scripts/Core/Events/HitInfo.cs
Normal file
9
Assets/Scripts/Core/Events/HitInfo.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
// 此文件已废弃。HitInfo / HitConfirmedEventChannelSO 已迁移至
|
||||
// Assets/Scripts/Combat/HitInfo.cs (namespace BaseGames.Combat)
|
||||
// 程序集 BaseGames.Combat.asmdef
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
// 保留空命名空间,避免 .meta 文件冲突。
|
||||
// ReSharper disable once EmptyNamespace
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/HitInfo.cs.meta
Normal file
11
Assets/Scripts/Core/Events/HitInfo.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a04b32343a49fe4c9a273b4d0d51b60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Assets/Scripts/Core/Events/IEventChannelRegistry.cs
Normal file
19
Assets/Scripts/Core/Events/IEventChannelRegistry.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// SO 事件频道注册表接口。
|
||||
/// 允许 Core 层通过接口访问动态频道查找,而无需直接依赖 EventChannelRegistry 实现。
|
||||
/// </summary>
|
||||
public interface IEventChannelRegistry
|
||||
{
|
||||
/// <summary>注册一个事件频道 SO。key 通常为 SO 资产名(如 "EVT_HitConfirmed")。</summary>
|
||||
void Register(string key, ScriptableObject channel);
|
||||
|
||||
/// <summary>
|
||||
/// 按 key 查找事件频道。未找到或类型不匹配时返回 null 并输出错误日志。
|
||||
/// </summary>
|
||||
T Get<T>(string key) where T : ScriptableObject;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/IEventChannelRegistry.cs.meta
Normal file
11
Assets/Scripts/Core/Events/IEventChannelRegistry.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7c6211f04a78764c978bbdb3c5fc708
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/IntEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/IntEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/Int")]
|
||||
public class IntEventChannelSO : BaseEventChannelSO<int> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/IntEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/IntEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce6e33f4353535944a0d2573df0a08d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/Scripts/Core/Events/LiquidEvent.cs
Normal file
29
Assets/Scripts/Core/Events/LiquidEvent.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 液体区域事件负载。用于玩家进入/离开液体区域时传递区域信息。
|
||||
/// </summary>
|
||||
public readonly struct LiquidEvent
|
||||
{
|
||||
/// <summary>液体区域标识符(对应 LiquidZone SO 的 zoneId)。</summary>
|
||||
public readonly string ZoneId;
|
||||
/// <summary>液体类型(如 "Water" / "Acid" / "Lava")。</summary>
|
||||
public readonly string LiquidType;
|
||||
|
||||
public LiquidEvent(string zoneId, string liquidType)
|
||||
{
|
||||
ZoneId = zoneId;
|
||||
LiquidType = liquidType;
|
||||
}
|
||||
|
||||
public override string ToString() => $"LiquidEvent(Zone={ZoneId}, Type={LiquidType})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 液体进出事件频道(EVT_LiquidEntered / EVT_LiquidExited)。
|
||||
/// 发布:LiquidZone(OnTriggerEnter2D / OnTriggerExit2D)
|
||||
/// 订阅:PlayerController(切换游泳状态)、DrownSystem、AudioManager
|
||||
/// </summary>
|
||||
[UnityEngine.CreateAssetMenu(menuName = "Events/LiquidEvent")]
|
||||
public class LiquidEventChannelSO : BaseEventChannelSO<LiquidEvent> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/LiquidEvent.cs.meta
Normal file
11
Assets/Scripts/Core/Events/LiquidEvent.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9cf4419a1f1358d4583f21618396ff9d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/PrimitiveEventChannels.cs
Normal file
7
Assets/Scripts/Core/Events/PrimitiveEventChannels.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/Bool")]
|
||||
public class BoolEventChannelSO : BaseEventChannelSO<bool> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/PrimitiveEventChannels.cs.meta
Normal file
11
Assets/Scripts/Core/Events/PrimitiveEventChannels.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3e424c1787e5be4fa918201b1830192
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
36
Assets/Scripts/Core/Events/QuestEvents.cs
Normal file
36
Assets/Scripts/Core/Events/QuestEvents.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 任务状态枚举(22_QuestChallengeModule §QuestSO)。
|
||||
/// </summary>
|
||||
public enum QuestState
|
||||
{
|
||||
Unavailable = 0,
|
||||
Available = 1,
|
||||
Active = 2,
|
||||
Completed = 3,
|
||||
Failed = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务状态变更事件负载。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct QuestStateChangedEvent
|
||||
{
|
||||
public string QuestId;
|
||||
public QuestState State;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务目标进度事件负载。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct QuestObjectiveEvent
|
||||
{
|
||||
public string QuestId;
|
||||
public string ObjectiveId;
|
||||
public int Progress;
|
||||
public int Required;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/QuestEvents.cs.meta
Normal file
11
Assets/Scripts/Core/Events/QuestEvents.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a4badb3a03cabf941b030e5c0c45a442
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/QuestObjective")]
|
||||
public class QuestObjectiveEventChannelSO : BaseEventChannelSO<QuestObjectiveEvent> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a46bd0c0af9fcd4dae69a00c6565be5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/QuestStateChanged")]
|
||||
public class QuestStateChangedEventChannel : BaseEventChannelSO<QuestStateChangedEvent> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9eeac19c9884b3144b390577d1c0c99f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/Scripts/Core/Events/SceneLoadRequest.cs
Normal file
20
Assets/Scripts/Core/Events/SceneLoadRequest.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 场景加载请求。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct SceneLoadRequest
|
||||
{
|
||||
/// <summary>目标场景名称(Addressable Key)</summary>
|
||||
public string SceneName;
|
||||
/// <summary>进入点 ID(供 PlayerController 决定出生位置)</summary>
|
||||
public string EntryId;
|
||||
/// <summary>玩家出生点 Transition ID(具体过渡门 ID,可为 null)</summary>
|
||||
public string EntryTransitionId;
|
||||
/// <summary>是否显示加载画面</summary>
|
||||
public bool ShowLoadingScreen;
|
||||
/// <summary>死亡复活时为 true,不执行正常过渡动画</summary>
|
||||
public bool IsRespawn;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/SceneLoadRequest.cs.meta
Normal file
11
Assets/Scripts/Core/Events/SceneLoadRequest.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05c1eca4b1ee3d24e80a4f42e4a19276
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/SceneLoadRequest")]
|
||||
public class SceneLoadRequestEventChannelSO : BaseEventChannelSO<SceneLoadRequest> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b5bc8e9a164cd54ea37027bc06c37ff
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/Scripts/Core/Events/ShopPurchaseEvent.cs
Normal file
14
Assets/Scripts/Core/Events/ShopPurchaseEvent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 商店购买事件负载(架构 15_MapShopModule §2.3)。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct ShopPurchaseEvent
|
||||
{
|
||||
/// <summary>购买的商品 ID</summary>
|
||||
public string ItemId;
|
||||
/// <summary>支付价格(Geo)</summary>
|
||||
public int Price;
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/ShopPurchaseEvent.cs.meta
Normal file
11
Assets/Scripts/Core/Events/ShopPurchaseEvent.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fa35acf32b4f9047af74c1514117951
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/ShopPurchaseEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/ShopPurchaseEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/ShopPurchase")]
|
||||
public class ShopPurchaseEventChannelSO : BaseEventChannelSO<ShopPurchaseEvent> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3d88263c925f8347bf1e19b7fa71036
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/StatusEffectEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/StatusEffectEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/StatusEffect")]
|
||||
public class StatusEffectEventChannelSO : BaseEventChannelSO<StatusEffectType> { }
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 933f8f3ce17ee54409502057bd1a8138
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
17
Assets/Scripts/Core/Events/StatusEffectType.cs
Normal file
17
Assets/Scripts/Core/Events/StatusEffectType.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 状态效果类型枚举(06_CombatModule §11)。
|
||||
/// </summary>
|
||||
public enum StatusEffectType
|
||||
{
|
||||
None = 0,
|
||||
Poison = 1,
|
||||
Burn = 2,
|
||||
Freeze = 3,
|
||||
Stun = 4,
|
||||
Stagger = 5,
|
||||
Bleed = 6,
|
||||
Wet = 7 // 液体相关(用于液体+火=蒸汽等组合效果)
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/StatusEffectType.cs.meta
Normal file
11
Assets/Scripts/Core/Events/StatusEffectType.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c5ad21970f85f04494b553fc483546a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/StringEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/StringEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/String")]
|
||||
public class StringEventChannelSO : BaseEventChannelSO<string> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/StringEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/StringEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23dad55c2f7bcc54a92ed61cc6f27c5b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
18
Assets/Scripts/Core/Events/ToolEvents.cs
Normal file
18
Assets/Scripts/Core/Events/ToolEvents.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 工具使用事件负载(架构 09_ProgressionModule §7.5)。
|
||||
/// Tool 用地址键字符串标识,避免 Core.Events 依赖高层程序集。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public struct ToolUsedPayload
|
||||
{
|
||||
/// <summary>工具槽索引(0 = 主槽,1 = 副槽)。</summary>
|
||||
public int SlotIndex;
|
||||
/// <summary>工具的 Addressable 地址键(对应 AddressKeys 常量)。</summary>
|
||||
public string ToolId;
|
||||
}
|
||||
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/ToolEvents.cs.meta
Normal file
11
Assets/Scripts/Core/Events/ToolEvents.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a5d53ae78d3ec74c9ec1981117953e8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/ToolUsedEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/ToolUsedEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/Progression/ToolUsed")]
|
||||
public class ToolUsedEventChannelSO : BaseEventChannelSO<ToolUsedPayload> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/ToolUsedEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/ToolUsedEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ab7a70182c725f4fad23afda850433e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/TransformEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/TransformEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/Transform")]
|
||||
public class TransformEventChannelSO : BaseEventChannelSO<Transform> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/TransformEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/TransformEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19580b3794783e647be1e1c0771814d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/Vector2EventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/Vector2EventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/Vector2")]
|
||||
public class Vector2EventChannelSO : BaseEventChannelSO<Vector2> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/Vector2EventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/Vector2EventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d28df1b130e12348a582259a89fa4e2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
Assets/Scripts/Core/Events/VoidEventChannelSO.cs
Normal file
7
Assets/Scripts/Core/Events/VoidEventChannelSO.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
[CreateAssetMenu(menuName = "Events/Void")]
|
||||
public class VoidEventChannelSO : VoidBaseEventChannelSO { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/VoidEventChannelSO.cs.meta
Normal file
11
Assets/Scripts/Core/Events/VoidEventChannelSO.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 07c5881d0d5ca3c42949a79f40939c3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
31
Assets/Scripts/Core/Events/WorldMarkerEvent.cs
Normal file
31
Assets/Scripts/Core/Events/WorldMarkerEvent.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// 世界导航标记事件负载。用于激活/失活地图标记点。
|
||||
/// </summary>
|
||||
public readonly struct WorldMarkerEvent
|
||||
{
|
||||
/// <summary>标记点唯一标识符。</summary>
|
||||
public readonly string MarkerId;
|
||||
/// <summary>标记点世界坐标。</summary>
|
||||
public readonly Vector3 Position;
|
||||
|
||||
public WorldMarkerEvent(string markerId, Vector3 position)
|
||||
{
|
||||
MarkerId = markerId;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
public override string ToString() => $"WorldMarkerEvent(Id={MarkerId}, Pos={Position})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导航标记激活/失活事件频道(EVT_WorldMarkerActivated / EVT_WorldMarkerDeactivated)。
|
||||
/// 发布:WorldMarker(OnTriggerEnter2D)、EventChainNode
|
||||
/// 订阅:MapController(更新迷雾地图标记)
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Events/WorldMarker")]
|
||||
public class WorldMarkerEventChannelSO : BaseEventChannelSO<WorldMarkerEvent> { }
|
||||
}
|
||||
11
Assets/Scripts/Core/Events/WorldMarkerEvent.cs.meta
Normal file
11
Assets/Scripts/Core/Events/WorldMarkerEvent.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3359e2b097190e49a4203dd215225f5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
144
Assets/Scripts/Core/GameManager.cs
Normal file
144
Assets/Scripts/Core/GameManager.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using BaseGames.Core.Events;
|
||||
using BaseGames.Core.States;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 全局游戏管理器。
|
||||
/// 生命周期:Persistent → 第一个执行(DefaultExecutionOrder(-1000))。
|
||||
/// 持有 <see cref="GameStateMachine"/> 并协调所有顶层服务。
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-1000)]
|
||||
public class GameManager : MonoBehaviour
|
||||
{
|
||||
// ── 单例 ──────────────────────────────────────────────────────────
|
||||
public static GameManager Instance { get; private set; }
|
||||
|
||||
// ── Inspector 引用 ────────────────────────────────────────────────
|
||||
[Header("Managers")]
|
||||
[SerializeField] private SettingsManager _settingsManager;
|
||||
[SerializeField] private DeathRespawnService _deathRespawnService;
|
||||
[SerializeField] private SceneService _sceneService;
|
||||
|
||||
[Header("Event Channels - Listen")]
|
||||
[SerializeField] private VoidEventChannelSO _onPlayerDied;
|
||||
[SerializeField] private VoidEventChannelSO _onPauseRequested;
|
||||
[SerializeField] private VoidEventChannelSO _onResumeRequested;
|
||||
[SerializeField] private StringEventChannelSO _onBossFightStarted;
|
||||
[SerializeField] private BoolEventChannelSO _onBossFightEnded;
|
||||
[SerializeField] private VoidEventChannelSO _onDeathScreenConfirmed;
|
||||
|
||||
[Header("Event Channels - Raise")]
|
||||
[SerializeField] private BaseEventChannelSO<GameStateId> _onGameStateChanged;
|
||||
[SerializeField] private VoidEventChannelSO _onPlayerRespawned;
|
||||
|
||||
// ── 状态机 ────────────────────────────────────────────────────────
|
||||
private readonly GameStateMachine _fsm = new GameStateMachine();
|
||||
public GameStateId CurrentState => _fsm.CurrentStateId;
|
||||
|
||||
// ──────────────────────────────────────────────────────────────────
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
|
||||
RegisterServices();
|
||||
RegisterStates();
|
||||
_settingsManager?.Initialize();
|
||||
|
||||
_fsm.TransitionTo(GameStates.Initializing, out _);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (_onPlayerDied) _onPlayerDied.OnEventRaised += HandlePlayerDied;
|
||||
if (_onPauseRequested) _onPauseRequested.OnEventRaised += HandlePauseRequested;
|
||||
if (_onResumeRequested) _onResumeRequested.OnEventRaised += HandleResumeRequested;
|
||||
if (_onBossFightStarted) _onBossFightStarted.OnEventRaised += HandleBossFightStarted;
|
||||
if (_onBossFightEnded) _onBossFightEnded.OnEventRaised += HandleBossFightEnded;
|
||||
if (_onDeathScreenConfirmed) _onDeathScreenConfirmed.OnEventRaised += HandleDeathScreenConfirmed;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_onPlayerDied) _onPlayerDied.OnEventRaised -= HandlePlayerDied;
|
||||
if (_onPauseRequested) _onPauseRequested.OnEventRaised -= HandlePauseRequested;
|
||||
if (_onResumeRequested) _onResumeRequested.OnEventRaised -= HandleResumeRequested;
|
||||
if (_onBossFightStarted) _onBossFightStarted.OnEventRaised -= HandleBossFightStarted;
|
||||
if (_onBossFightEnded) _onBossFightEnded.OnEventRaised -= HandleBossFightEnded;
|
||||
if (_onDeathScreenConfirmed) _onDeathScreenConfirmed.OnEventRaised -= HandleDeathScreenConfirmed;
|
||||
}
|
||||
|
||||
private void Update() => _fsm.Tick(Time.deltaTime);
|
||||
|
||||
// ── 初始化 ────────────────────────────────────────────────────────
|
||||
private void RegisterServices()
|
||||
{
|
||||
if (_deathRespawnService)
|
||||
ServiceLocator.Register<IDeathRespawnService>(_deathRespawnService);
|
||||
if (_sceneService)
|
||||
ServiceLocator.Register<ISceneService>(_sceneService);
|
||||
}
|
||||
|
||||
private void RegisterStates()
|
||||
{
|
||||
_fsm.Register(new InitializingState());
|
||||
_fsm.Register(new MainMenuState());
|
||||
_fsm.Register(new LoadingSceneState());
|
||||
_fsm.Register(new GameplayState());
|
||||
_fsm.Register(new BossFightState());
|
||||
_fsm.Register(new PausedState());
|
||||
_fsm.Register(new DeadState());
|
||||
_fsm.Register(new CutsceneState());
|
||||
_fsm.Register(new GameOverState());
|
||||
}
|
||||
|
||||
// ── 状态转换公共 API ──────────────────────────────────────────────
|
||||
public bool RequestTransition(GameStateId nextState)
|
||||
{
|
||||
if (_fsm.TransitionTo(nextState, out string error))
|
||||
{
|
||||
// 通知 UI 等监听者(GameStateId in Core.Events 是 string-based)
|
||||
_onGameStateChanged?.Raise(new Events.GameStateId(nextState.Id));
|
||||
return true;
|
||||
}
|
||||
Debug.LogWarning($"[GameManager] {error}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── 事件处理 ──────────────────────────────────────────────────────
|
||||
private void HandlePlayerDied() => StartCoroutine(DeathFlow());
|
||||
private void HandlePauseRequested() => RequestTransition(GameStates.Paused);
|
||||
private void HandleResumeRequested() => RequestTransition(GameStates.Gameplay);
|
||||
|
||||
private void HandleBossFightStarted(string bossId)
|
||||
=> RequestTransition(GameStates.BossFight);
|
||||
|
||||
private void HandleBossFightEnded(bool victory)
|
||||
{
|
||||
if (victory) RequestTransition(GameStates.Gameplay);
|
||||
else RequestTransition(GameStates.GameOver);
|
||||
}
|
||||
|
||||
private bool _deathScreenConfirmed;
|
||||
private void HandleDeathScreenConfirmed() => _deathScreenConfirmed = true;
|
||||
|
||||
private IEnumerator DeathFlow()
|
||||
{
|
||||
RequestTransition(GameStates.Dead);
|
||||
var deathService = ServiceLocator.Get<IDeathRespawnService>();
|
||||
yield return deathService.StartDeathSequenceCoroutine();
|
||||
|
||||
// 等待玩家在死亡画面点击重试
|
||||
_deathScreenConfirmed = false;
|
||||
yield return new WaitUntil(() => _deathScreenConfirmed);
|
||||
|
||||
yield return deathService.StartRespawnCoroutine();
|
||||
RequestTransition(GameStates.LoadingScene);
|
||||
_onPlayerRespawned?.Raise();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/GameManager.cs.meta
Normal file
11
Assets/Scripts/Core/GameManager.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45c1b813f057cfc4eb19a8c36643ead0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
30
Assets/Scripts/Core/GameServiceRegistrar.cs
Normal file
30
Assets/Scripts/Core/GameServiceRegistrar.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using UnityEngine;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 在 Awake 时(最早执行)向 ServiceLocator 注册所有服务。
|
||||
/// 挂载在 Persistent 场景的根 GameObject 上。
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-2000)]
|
||||
public class GameServiceRegistrar : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private DeathRespawnService _deathRespawnService;
|
||||
[SerializeField] private SceneService _sceneService;
|
||||
[SerializeField] private EventChannelRegistry _eventChannelRegistry;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// 注册 NullAudioService 作为兜底;Phase 2 Audio 模块 Awake 后会用真实实现覆盖
|
||||
ServiceLocator.RegisterIfAbsent<IAudioService>(new NullAudioService());
|
||||
|
||||
if (_deathRespawnService)
|
||||
ServiceLocator.Register<IDeathRespawnService>(_deathRespawnService);
|
||||
if (_sceneService)
|
||||
ServiceLocator.Register<ISceneService>(_sceneService);
|
||||
if (_eventChannelRegistry)
|
||||
ServiceLocator.Register<IEventChannelRegistry>(_eventChannelRegistry);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/GameServiceRegistrar.cs.meta
Normal file
11
Assets/Scripts/Core/GameServiceRegistrar.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d314e5c9b7077d8409f700a2f394bf29
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
18
Assets/Scripts/Core/GameStateId.cs
Normal file
18
Assets/Scripts/Core/GameStateId.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>内置游戏状态常量。</summary>
|
||||
public static class GameStates
|
||||
{
|
||||
public static readonly GameStateId Initializing = new GameStateId("Initializing");
|
||||
public static readonly GameStateId MainMenu = new GameStateId("MainMenu");
|
||||
public static readonly GameStateId LoadingScene = new GameStateId("LoadingScene");
|
||||
public static readonly GameStateId Gameplay = new GameStateId("Gameplay");
|
||||
public static readonly GameStateId BossFight = new GameStateId("BossFight");
|
||||
public static readonly GameStateId Paused = new GameStateId("Paused");
|
||||
public static readonly GameStateId Dead = new GameStateId("Dead");
|
||||
public static readonly GameStateId Cutscene = new GameStateId("Cutscene");
|
||||
public static readonly GameStateId GameOver = new GameStateId("GameOver");
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/GameStateId.cs.meta
Normal file
11
Assets/Scripts/Core/GameStateId.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d0652f4270c7de949822392d63415a39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
45
Assets/Scripts/Core/GameStateMachine.cs
Normal file
45
Assets/Scripts/Core/GameStateMachine.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 状态机核心,持有状态注册表与当前状态。
|
||||
/// GameManager 持有一个实例;状态对象在 Awake 注入。
|
||||
/// </summary>
|
||||
public class GameStateMachine
|
||||
{
|
||||
private readonly Dictionary<GameStateId, IGameState> _states = new();
|
||||
private IGameState _current;
|
||||
|
||||
public GameStateId CurrentStateId => _current?.Id ?? default;
|
||||
|
||||
/// <summary>注册状态实现(同 Id 注册多次以最后一次为准)。</summary>
|
||||
public void Register(IGameState state) => _states[state.Id] = state;
|
||||
|
||||
/// <summary>转换到目标状态。失败时返回 false 并填充 error 字符串。</summary>
|
||||
public bool TransitionTo(GameStateId nextId, out string error)
|
||||
{
|
||||
if (!_states.TryGetValue(nextId, out var next))
|
||||
{
|
||||
error = $"[GameStateMachine] 未知状态 '{nextId}'";
|
||||
return false;
|
||||
}
|
||||
if (_current != null && !_current.ValidNextStates.Contains(nextId))
|
||||
{
|
||||
error = $"[GameStateMachine] 非法转换 {_current.Id} → {nextId}";
|
||||
return false;
|
||||
}
|
||||
var prev = _current?.Id ?? default;
|
||||
_current?.OnExit(nextId);
|
||||
_current = next;
|
||||
_current.OnEnter(prev);
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Tick(float deltaTime) => _current?.Tick(deltaTime);
|
||||
}
|
||||
}
|
||||
11
Assets/Scripts/Core/GameStateMachine.cs.meta
Normal file
11
Assets/Scripts/Core/GameStateMachine.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b89b31d9f10c6d4f876f3e03bca72c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
57
Assets/Scripts/Core/GlobalSettingsSO.cs
Normal file
57
Assets/Scripts/Core/GlobalSettingsSO.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using UnityEngine;
|
||||
using BaseGames.Core.Events;
|
||||
|
||||
namespace BaseGames.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏全局设置数据(运行时值)。
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class GlobalSettingsData
|
||||
{
|
||||
public float MasterVolume = 1f;
|
||||
public float BGMVolume = 0.8f;
|
||||
public float SFXVolume = 1f;
|
||||
public float AmbientVolume = 0.8f;
|
||||
|
||||
public bool VSync = true;
|
||||
public int TargetFPS = 60;
|
||||
public bool FullScreen = true;
|
||||
|
||||
public string Language = "zh-CN";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局设置默认值 SO(资产:Assets/Data/Settings/SET_GlobalSettings.asset)。
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "Settings/GlobalSettings")]
|
||||
public class GlobalSettingsSO : ScriptableObject
|
||||
{
|
||||
[Header("Audio")]
|
||||
public float DefaultMasterVolume = 1f;
|
||||
public float DefaultBGMVolume = 0.8f;
|
||||
public float DefaultSFXVolume = 1f;
|
||||
public float DefaultAmbientVolume = 0.8f;
|
||||
|
||||
[Header("Display")]
|
||||
public bool DefaultVSync = true;
|
||||
public int DefaultTargetFPS = 60;
|
||||
public bool DefaultFullScreen = true;
|
||||
|
||||
[Header("Language")]
|
||||
public string DefaultLanguage = "zh-CN";
|
||||
|
||||
/// <summary>将 SO 默认值填入 GlobalSettingsData。</summary>
|
||||
public GlobalSettingsData CreateDefault() => new GlobalSettingsData
|
||||
{
|
||||
MasterVolume = DefaultMasterVolume,
|
||||
BGMVolume = DefaultBGMVolume,
|
||||
SFXVolume = DefaultSFXVolume,
|
||||
AmbientVolume = DefaultAmbientVolume,
|
||||
VSync = DefaultVSync,
|
||||
TargetFPS = DefaultTargetFPS,
|
||||
FullScreen = DefaultFullScreen,
|
||||
Language = DefaultLanguage,
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user