feat(enemy): EnemyAbilityBase 加 ResolveConfig<T> 类型化配置解析(不匹配显式报错) + EditMode 测试
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6af97638caa36fc469e07c93d25f2031
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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<T>() where T : EnemyAbilitySO => ResolveConfig<T>();
|
||||
}
|
||||
|
||||
private ProbeAbility Make() => new GameObject("probe").AddComponent<ProbeAbility>();
|
||||
|
||||
[Test]
|
||||
public void ResolveConfig_CorrectType_ReturnsInstance()
|
||||
{
|
||||
var probe = Make();
|
||||
var cfg = ScriptableObject.CreateInstance<ProbeConfigSO>();
|
||||
probe.SetConfig(cfg);
|
||||
Assert.AreSame(cfg, probe.Probe<ProbeConfigSO>());
|
||||
Object.DestroyImmediate(cfg);
|
||||
Object.DestroyImmediate(probe.gameObject);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ResolveConfig_WrongType_LogsError_ReturnsNull()
|
||||
{
|
||||
var probe = Make();
|
||||
probe.SetConfig(ScriptableObject.CreateInstance<OtherConfigSO>());
|
||||
LogAssert.Expect(LogType.Error, new Regex("_config 需为"));
|
||||
Assert.IsNull(probe.Probe<ProbeConfigSO>());
|
||||
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<ProbeConfigSO>());
|
||||
Object.DestroyImmediate(probe.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02af4081300f9164f95b2b22339e5911
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -155,6 +155,15 @@ namespace BaseGames.Enemies.Abilities
|
||||
if (target == null || _enemy == null) return;
|
||||
_enemy.FaceTarget(target.position);
|
||||
}
|
||||
|
||||
/// <summary>把 _config 解析为期望的子类型;类型不符/为空时显式报错(不回退)。</summary>
|
||||
protected T ResolveConfig<T>() 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>WaitForSeconds 池(架构 §10 GC 优化)。能力协程统一通过此获取等待指令。</summary>
|
||||
|
||||
Reference in New Issue
Block a user