diff --git a/Assets/Tests/EditMode/AI/AbilityRefTests.cs b/Assets/Tests/EditMode/AI/AbilityRefTests.cs new file mode 100644 index 00000000..89d82380 --- /dev/null +++ b/Assets/Tests/EditMode/AI/AbilityRefTests.cs @@ -0,0 +1,46 @@ +using NUnit.Framework; +using UnityEngine; +using BaseGames.Enemies.Abilities; + +namespace BaseGames.Tests.EditMode.AI +{ + public class AbilityRefTests + { + [Test] + public void ImplicitFromString_ExposesLiteralId() + { + AbilityRef r = "e001_chase"; + Assert.AreEqual("e001_chase", r.Id); + Assert.IsFalse(r.IsEmpty); + } + + [Test] + public void FromAsset_ExposesAssetAbilityId() + { + var so = ScriptableObject.CreateInstance(); + so.abilityId = "from_asset"; + AbilityRef r = so; + Assert.AreEqual("from_asset", r.Id); + Object.DestroyImmediate(so); + } + + [Test] + public void Default_IsEmpty() + { + var r = default(AbilityRef); + Assert.IsTrue(r.IsEmpty); + Assert.IsNull(r.Id); + } + + [Test] + public void AssetWins_WhenBothSet() + { + var so = ScriptableObject.CreateInstance(); + so.abilityId = "asset_id"; + var r = new AbilityRef(so, "literal_id"); + Assert.AreEqual("asset_id", r.Id); + Assert.IsTrue(r.HasConflict); // 供校验器报警告 + Object.DestroyImmediate(so); + } + } +} diff --git a/Assets/Tests/EditMode/AI/AbilityRefTests.cs.meta b/Assets/Tests/EditMode/AI/AbilityRefTests.cs.meta new file mode 100644 index 00000000..1fc29b23 --- /dev/null +++ b/Assets/Tests/EditMode/AI/AbilityRefTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30c3cfb4f5ff62f4691343a1ab594f7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Game/Scripts/Enemies/Abilities/AbilityRef.cs b/Assets/_Game/Scripts/Enemies/Abilities/AbilityRef.cs new file mode 100644 index 00000000..aca18df0 --- /dev/null +++ b/Assets/_Game/Scripts/Enemies/Abilities/AbilityRef.cs @@ -0,0 +1,33 @@ +using System; +using UnityEngine; + +namespace BaseGames.Enemies.Abilities +{ + /// + /// 能力引用。配方资产侧引用 EnemyAbilitySO(Unity 引用系统保证拼不错、改名不断链); + /// 定制 AiScript 侧仍可直接写字符串 id。资产优先。 + /// + [Serializable] + public struct AbilityRef + { + [Tooltip("能力配置资产;配方路径用这个")] + [SerializeField] EnemyAbilitySO _asset; + + [NonSerialized] string _literal; // 定制脚本路径用;不参与序列化 + + public AbilityRef(EnemyAbilitySO asset) { _asset = asset; _literal = null; } + public AbilityRef(string id) { _asset = null; _literal = id; } + public AbilityRef(EnemyAbilitySO asset, string id) { _asset = asset; _literal = id; } + + /// 解析出的能力 id;资产优先,都没有则 null。 + public string Id => _asset != null ? _asset.abilityId : _literal; + + public bool IsEmpty => string.IsNullOrEmpty(Id); + + /// 资产与字符串同时非空——歧义配置,供校验器报警告。 + public bool HasConflict => _asset != null && !string.IsNullOrEmpty(_literal); + + public static implicit operator AbilityRef(string id) => new AbilityRef(id); + public static implicit operator AbilityRef(EnemyAbilitySO asset) => new AbilityRef(asset); + } +} diff --git a/Assets/_Game/Scripts/Enemies/Abilities/AbilityRef.cs.meta b/Assets/_Game/Scripts/Enemies/Abilities/AbilityRef.cs.meta new file mode 100644 index 00000000..e6445d07 --- /dev/null +++ b/Assets/_Game/Scripts/Enemies/Abilities/AbilityRef.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00a0a0d1f0e127a47ad01ea9423bbc0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: