多轮审查和修复

This commit is contained in:
2026-05-12 15:34:08 +08:00
parent f55d2a57c3
commit ebbbb7332e
805 changed files with 838724 additions and 1905 deletions

View File

@@ -10,15 +10,37 @@ namespace BaseGames.Core.Events
{
[Multiline] public string description;
public event Action<T> OnEventRaised;
private event Action<T> _onEventRaisedBacking;
#if UNITY_EDITOR
private int _subscriberCount;
#endif
public event Action<T> OnEventRaised
{
add
{
_onEventRaisedBacking += value;
#if UNITY_EDITOR
_subscriberCount++;
#endif
}
remove
{
_onEventRaisedBacking -= value;
#if UNITY_EDITOR
_subscriberCount--;
#endif
}
}
public void Raise(T value)
{
#if UNITY_EDITOR
EventBusMonitor.Record(name, value?.ToString() ?? "null",
OnEventRaised?.GetInvocationList().Length ?? 0);
_subscriberCount,
Time.frameCount);
#endif
OnEventRaised?.Invoke(value);
_onEventRaisedBacking?.Invoke(value);
}
/// <summary>
@@ -38,15 +60,37 @@ namespace BaseGames.Core.Events
{
[Multiline] public string description;
public event Action OnEventRaised;
private event Action _onEventRaisedBacking;
#if UNITY_EDITOR
private int _subscriberCount;
#endif
public event Action OnEventRaised
{
add
{
_onEventRaisedBacking += value;
#if UNITY_EDITOR
_subscriberCount++;
#endif
}
remove
{
_onEventRaisedBacking -= value;
#if UNITY_EDITOR
_subscriberCount--;
#endif
}
}
public void Raise()
{
#if UNITY_EDITOR
EventBusMonitor.Record(name, "<void>",
OnEventRaised?.GetInvocationList().Length ?? 0);
_subscriberCount,
Time.frameCount);
#endif
OnEventRaised?.Invoke();
_onEventRaisedBacking?.Invoke();
}
/// <summary>

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: d3e424c1787e5be4fa918201b1830192
guid: d5c798758acf2c64097cf4ff3b088530
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,9 +1,2 @@
// 此文件已废弃。DamageInfo / DamageInfoEventChannelSO 已迁移至
// Assets/Scripts/Combat/DamageInfo.cs (namespace BaseGames.Combat)
// 程序集 BaseGames.Combat.asmdef
namespace BaseGames.Core.Events
{
// 保留空命名空间,避免 .meta 文件冲突。
// ReSharper disable once EmptyNamespace
}
// DamageInfo DamageInfoEventChannelSO 定义位于 Assets/Scripts/Combat/DamageInfo.cs。
namespace BaseGames.Core.Events { }

View File

@@ -3,7 +3,7 @@ using UnityEngine;
namespace BaseGames.Core.Events
{
/// <summary>
/// 难度变更事件频道。Phase 2 难度系统使用。
/// 难度变更事件频道。
/// 发布DifficultyScalerSO / SettingsManager
/// 订阅:所有需要感知当前难度的系统
/// </summary>

View File

@@ -11,29 +11,53 @@ namespace BaseGames.Core.Events
public string ChannelName;
public string Payload;
public int ListenerCount;
public int FrameCount;
public System.DateTime Timestamp;
}
private static readonly System.Collections.Generic.Queue<EventRecord> _records
= new System.Collections.Generic.Queue<EventRecord>(256);
private const int Capacity = 256;
public static System.Collections.Generic.IEnumerable<EventRecord> Records => _records;
// 固定大小环形缓冲区,避免 Queue.Enqueue/Dequeue 产生的 GC 分配
private static readonly EventRecord[] _buffer = new EventRecord[Capacity];
private static int _head = 0; // 下一条写入的位置
private static int _count = 0; // 当前有效记录数(≤ Capacity
public static void Record(string channelName, string payload, int listenerCount)
/// <summary>按时间顺序(最旧→最新)枚举所有已记录事件。</summary>
public static System.Collections.Generic.IEnumerable<EventRecord> Records
{
if (_records.Count >= 256) _records.Dequeue();
_records.Enqueue(new EventRecord
get
{
int start = _count < Capacity ? 0 : _head;
int total = System.Math.Min(_count, Capacity);
for (int i = 0; i < total; i++)
yield return _buffer[(start + i) % Capacity];
}
}
// frameCount 使用 int与 Time.frameCount 类型一致)。
// 在 @1000fps 持续运行约 24.8 天后发生有符号溢出C# 默认 unchecked环绕为负数
// 对于 Editor 调试工具此溢出无实际影响,此处不做处理。
public static void Record(string channelName, string payload, int listenerCount, int frameCount)
{
_buffer[_head] = new EventRecord
{
ChannelName = channelName,
Payload = payload,
ListenerCount = listenerCount,
FrameCount = frameCount,
Timestamp = System.DateTime.Now
});
};
_head = (_head + 1) % Capacity;
if (_count < Capacity) _count++;
}
public static void Clear() => _records.Clear();
public static void Clear()
{
_head = 0;
_count = 0;
}
#else
public static void Record(string channelName, string payload, int listenerCount) { }
public static void Record(string channelName, string payload, int listenerCount, int frameCount) { }
#endif
}
}

View File

@@ -10,19 +10,16 @@ namespace BaseGames.Core.Events
/// </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)
if (BaseGames.Core.ServiceLocator.GetOrDefault<IEventChannelRegistry>() != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
BaseGames.Core.ServiceLocator.Register<IEventChannelRegistry>(this);
}
/// <summary>由 EventChannelRegistrar 在场景初始化时批量注册频道 SO。</summary>

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 661043851605d4849bef40ea15c556b4
guid: 147bb5b987a0a244ba3a39c71852ca51
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,9 +1,2 @@
// 此文件已废弃。HitInfo / HitConfirmedEventChannelSO 已迁移至
// Assets/Scripts/Combat/HitInfo.cs (namespace BaseGames.Combat)
// 程序集 BaseGames.Combat.asmdef
namespace BaseGames.Core.Events
{
// 保留空命名空间,避免 .meta 文件冲突。
// ReSharper disable once EmptyNamespace
}
// HitInfo HitConfirmedEventChannelSO 定义位于 Assets/Scripts/Combat/HitInfo.cs。
namespace BaseGames.Core.Events { }

View File

@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace BaseGames.Core
{
/// <summary>
/// 轻量服务定位器。通过类型键注册/查找服务,支持接口类型注册(依赖倒置)。
/// <para><b>线程安全:</b>仅在 Unity 主线程调用。异步上下文Task/Thread不得访问此类。</para>
/// </summary>
public static class ServiceLocator
{
private static readonly Dictionary<Type, object> _services = new();
/// <summary>以接口类型 TInterface 注册实现 impl。</summary>
public static void Register<TInterface>(TInterface impl)
=> _services[typeof(TInterface)] = impl;
/// <summary>仅当尚未注册时才注册(防多场景重复注册同一服务)。</summary>
public static void RegisterIfAbsent<TInterface>(TInterface impl)
{
if (!_services.ContainsKey(typeof(TInterface)))
_services[typeof(TInterface)] = impl;
}
/// <summary>查找服务。未注册时抛出 InvalidOperationException。</summary>
public static TInterface Get<TInterface>()
{
if (_services.TryGetValue(typeof(TInterface), out var svc) && svc is TInterface typed)
return typed;
throw new InvalidOperationException(
$"[ServiceLocator] Service '{typeof(TInterface).Name}' is not registered. "
+ "Ensure GameServiceRegistrar.Awake() has run before this call.");
}
/// <summary>安全版 Get未注册时返回 fallback不报错适用于可选服务。</summary>
public static TInterface GetOrDefault<TInterface>(TInterface fallback = default)
=> _services.TryGetValue(typeof(TInterface), out var svc) && svc is TInterface typed
? typed : fallback;
/// <summary>
/// 注销服务。场景卸载或 Manager OnDestroy 时调用,防止持有已销毁对象的引用。
/// </summary>
public static void Unregister<TInterface>()
=> _services.Remove(typeof(TInterface));
/// <summary>
/// 安全版注销:仅当注册的实例与 <paramref name="impl"/> 相同时才移除,
/// 避免后注册的新实例被前一个实例的 OnDestroy 错误清除。
/// </summary>
public static void Unregister<TInterface>(TInterface impl)
{
if (_services.TryGetValue(typeof(TInterface), out var svc) && ReferenceEquals(svc, impl))
_services.Remove(typeof(TInterface));
}
#if UNITY_EDITOR
/// <summary>单元测试中替换服务实现。</summary>
public static void OverrideForTest<TInterface>(TInterface mock)
=> _services[typeof(TInterface)] = mock;
/// <summary>清空所有注册(测试用)。</summary>
public static void Reset() => _services.Clear();
#endif
}
}

View File

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