基于继承实现回合制战术游戏脚本化技能的最优方案
回合制战术游戏技能系统实现方案
针对你遇到的Unity技能序列化与类型识别问题,这里有几个实用的解决思路:
方案一:用ScriptableObject实现技能(推荐)
ScriptableObject天生适合做可复用的技能模板,而且支持[SerializeReference]特性,能完美保留子类类型信息。
- 定义技能基类(继承ScriptableObject)
using UnityEngine; public abstract class AbilitySO : ScriptableObject { // 技能名称、图标等公共属性 public string abilityName; public Sprite icon; // 抽象激活方法,传入施法单位、目标等上下文 public abstract void ActivateAbility(Unit caster, GameObject target = null); }
- 实现具体技能子类
[CreateAssetMenu(fileName = "Fireball", menuName = "Abilities/Fireball")] public class FireballAbility : AbilitySO { public int damage = 20; public float radius = 3f; public override void ActivateAbility(Unit caster, GameObject target) { // 实现火球技能逻辑:AOE伤害、特效播放等 Collider[] hitTargets = Physics.OverlapSphere(target.transform.position, radius); foreach (Collider col in hitTargets) { if (col.TryGetComponent<Health>(out Health health)) { health.TakeDamage(damage); } } } }
- 单位脚本中配置技能列表
using UnityEngine; using System.Collections.Generic; public class Unit : MonoBehaviour { // 用[SerializeReference]让Unity序列化时保留子类类型 [SerializeReference] public List<AbilitySO> abilities = new List<AbilitySO>(); // 绑定UI按钮的方法,通过索引触发技能 public void CastAbility(int abilityIndex) { if (abilityIndex >= 0 && abilityIndex < abilities.Count) { // 这里需要传入施法单位和目标(可以根据游戏逻辑获取选中目标) abilities[abilityIndex].ActivateAbility(this, GetSelectedTarget()); } } private GameObject GetSelectedTarget() { // 实现获取当前选中目标的逻辑 return null; } }
在编辑器里,你可以右键创建Fireball等技能资产,直接拖入Unit组件的abilities列表,Unity会正确显示技能的子类类型和专属参数。
方案二:MonoBehaviour技能+主动收集子类组件
如果必须用MonoBehaviour挂载技能,可以放弃序列化列表,改为在初始化时主动收集所有技能组件,利用多态执行逻辑。
- 技能基类(MonoBehaviour)
using UnityEngine; public abstract class AbilityBase : MonoBehaviour { public string abilityName; public Sprite icon; public abstract void ActivateAbility(Unit caster, GameObject target = null); }
- 具体技能子类
public class HealAbility : AbilityBase { public int healAmount = 15; public override void ActivateAbility(Unit caster, GameObject target) { if (target.TryGetComponent<Health>(out Health health)) { health.Heal(healAmount); } } }
- 单位脚本收集技能
using UnityEngine; using System.Collections.Generic; public class Unit : MonoBehaviour { private List<AbilityBase> abilities = new List<AbilityBase>(); private void Awake() { // 收集所有子物体上的AbilityBase子类组件 abilities.AddRange(GetComponentsInChildren<AbilityBase>(true)); } // UI按钮触发技能 public void CastAbility(int abilityIndex) { if (abilityIndex >= 0 && abilityIndex < abilities.Count) { abilities[abilityIndex].ActivateAbility(this, GetSelectedTarget()); } } private GameObject GetSelectedTarget() { // 实现获取目标逻辑 return null; } }
把每个技能脚本挂在Unit的子GameObject上,Awake时会自动收集所有技能,调用ActivateAbility时会自动执行子类的重写逻辑,不需要关心类型转换。
方案三:自定义编辑器扩展(可选)
如果需要在编辑器里直接给Unit添加指定类型的技能,可以写个自定义编辑器:
using UnityEditor; using UnityEngine; [CustomEditor(typeof(Unit))] public class UnitEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); Unit unit = (Unit)target; if (GUILayout.Button("Add Fireball Ability")) { GameObject child = new GameObject("Fireball Ability"); child.transform.parent = unit.transform; child.AddComponent<FireballAbility>(); } if (GUILayout.Button("Add Heal Ability")) { GameObject child = new GameObject("Heal Ability"); child.transform.parent = unit.transform; child.AddComponent<HealAbility>(); } } }
这样在编辑器里就能一键给单位添加指定技能,避免手动创建子物体的麻烦。
内容的提问来源于stack exchange,提问作者novawhelk
相关产品推荐
相关产品推荐

