From ca36974ec32bbf9e9124e73eae2ed8acb88ee4ad Mon Sep 17 00:00:00 2001 From: Joywayer Date: Tue, 21 Jul 2026 11:14:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(enemy):=20EnemyAbilityBase=20=E5=8A=A0=20R?= =?UTF-8?q?esolveConfig=20=E7=B1=BB=E5=9E=8B=E5=8C=96=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E8=A7=A3=E6=9E=90(=E4=B8=8D=E5=8C=B9=E9=85=8D=E6=98=BE?= =?UTF-8?q?=E5=BC=8F=E6=8A=A5=E9=94=99)=20+=20EditMode=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Tests/EditMode/Abilities.meta | 8 +++ .../Abilities/AbilityConfigResolveTests.cs | 58 +++++++++++++++++++ .../AbilityConfigResolveTests.cs.meta | 11 ++++ .../Enemies/Abilities/EnemyAbilityBase.cs | 9 +++ 4 files changed, 86 insertions(+) create mode 100644 Assets/Tests/EditMode/Abilities.meta create mode 100644 Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs create mode 100644 Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs.meta diff --git a/Assets/Tests/EditMode/Abilities.meta b/Assets/Tests/EditMode/Abilities.meta new file mode 100644 index 00000000..bca771c0 --- /dev/null +++ b/Assets/Tests/EditMode/Abilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6af97638caa36fc469e07c93d25f2031 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs b/Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs new file mode 100644 index 00000000..4ca68ff4 --- /dev/null +++ b/Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs @@ -0,0 +1,58 @@ +using System.Collections; +using System.Text.RegularExpressions; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; +using BaseGames.Enemies.Abilities; + +namespace BaseGames.Tests.EditMode.Abilities +{ + public class AbilityConfigResolveTests + { + // 测试专用 SO 子类型(不依赖真实 8 个子类,保持本 Task 自包含) + private sealed class ProbeConfigSO : EnemyAbilitySO { } + private sealed class OtherConfigSO : EnemyAbilitySO { } + + // 测试探针:跳过基类 Awake 依赖装配,暴露 protected 成员 + private sealed class ProbeAbility : EnemyAbilityBase + { + protected override void Awake() { /* 跳过依赖装配,避免测试噪声 */ } + protected override IEnumerator ExecuteCoroutine() { yield break; } + public void SetConfig(EnemyAbilitySO c) => _config = c; + public T Probe() where T : EnemyAbilitySO => ResolveConfig(); + } + + private ProbeAbility Make() => new GameObject("probe").AddComponent(); + + [Test] + public void ResolveConfig_CorrectType_ReturnsInstance() + { + var probe = Make(); + var cfg = ScriptableObject.CreateInstance(); + probe.SetConfig(cfg); + Assert.AreSame(cfg, probe.Probe()); + Object.DestroyImmediate(cfg); + Object.DestroyImmediate(probe.gameObject); + } + + [Test] + public void ResolveConfig_WrongType_LogsError_ReturnsNull() + { + var probe = Make(); + probe.SetConfig(ScriptableObject.CreateInstance()); + LogAssert.Expect(LogType.Error, new Regex("_config 需为")); + Assert.IsNull(probe.Probe()); + Object.DestroyImmediate(probe.gameObject); + } + + [Test] + public void ResolveConfig_Null_LogsError_ReturnsNull() + { + var probe = Make(); + probe.SetConfig(null); + LogAssert.Expect(LogType.Error, new Regex("_config 需为")); + Assert.IsNull(probe.Probe()); + Object.DestroyImmediate(probe.gameObject); + } + } +} diff --git a/Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs.meta b/Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs.meta new file mode 100644 index 00000000..63c9b888 --- /dev/null +++ b/Assets/Tests/EditMode/Abilities/AbilityConfigResolveTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 02af4081300f9164f95b2b22339e5911 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs b/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs index 037b0d4c..5751efd6 100644 --- a/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs +++ b/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs @@ -155,6 +155,15 @@ namespace BaseGames.Enemies.Abilities if (target == null || _enemy == null) return; _enemy.FaceTarget(target.position); } + + /// 把 _config 解析为期望的子类型;类型不符/为空时显式报错(不回退)。 + protected T ResolveConfig() where T : EnemyAbilitySO + { + if (_config is T typed) return typed; + Debug.LogError($"[{GetType().Name}] _config 需为 {typeof(T).Name}," + + $"实际 = {(_config != null ? _config.GetType().Name : "null")}。请重建为正确的子类 SO。", this); + return null; + } } /// WaitForSeconds 池(架构 §10 GC 优化)。能力协程统一通过此获取等待指令。