【Unity】动作游戏开发实战详细分析-06-技能系统设计

【Unity】动作游戏开发实战详细分析-06-技能系统设计


基本思想

不同的技能可以设计为技能模版,当角色释放技能时,会通过模版ID将它进行实例化,这个实例技能类可以是一个挂载的MonoBehaviour组件或者通过上下文传入的独立对象

其次,考虑技能是否共用的问题,如果一个技能,玩家和敌人都可以使用,是否需要设计更为严谨的上下文接口?在动作游戏中,真正需要共用技能的情况相对比价多见。

系统设计

我们首先设计技能基本接口

它包含了:

  • 属性-技能ID
  • 方法-序列化恢复
  • 方法-执行
public interface ISkillBase
{
  int ID { get; }//技能ID
  void SerializeRecovery(SkillContext skillContext);//序列化恢复
  void Execute(Action onFinished);//技能执行
}

其中序列化恢复需要传入技能上下文,它是一个结构体,定义如下,它存储了该技能的技能释放者以及使用按键

public struct SkillContext
{
  public GameObject GameObject { get; set; }//技能释放者引用
  public KeyCode TriggerKey { get; set; }
}

然后,我们继续设计技能接口

新增接口包含:

  • 模版注册回调函数
  • 模版注销回调函数
  • 技能实例化
public interface ISkill : ISkillBase
{
  void OnTemplateRegist();//模板注册回调
  void OnTemplateUnregist();//模板反注册回调
  ISkillBase Instantiate(SkillContext skillContext);//技能对象实例化
}

最后,定义我们的抽象类技能模版

它包含基本属性和字段

属性:ID

这里有一点需要注意,Skill实现了ISkill接口,它就会有一个公开的ID,这个ID和现在我们定义的ID不是一个东西。其次,模版类中的东西均是不可访问,对技能的访问只能通过ISkill接口进行访问,这里使用了类似的技术使的所有东西被封闭封装

using System;

namespace ACTBook
{
    public abstract class Skill : ISkill, ICloneable
    {
        protected abstract int ID { get; }

        protected SkillContext mSkillContext;


        protected abstract void Execute(Action onFinished);

        protected virtual void OnTemplateRegist()
        {

        }

        protected virtual void OnTemplateUnregist()
        {

        }

        protected virtual void OnInstantiate()
        {

        }

        protected virtual void SerializeRecovery(SkillContext skillContext)
        {

        }

        protected virtual ISkillBase SkillInstantiate(SkillContext skillContext)
        {
            var instantiateSkill = Clone() as Skill;
            instantiateSkill.mSkillContext = skillContext;

            instantiateSkill.OnInstantiate();

            return instantiateSkill;
        }

        #region --- ISkillBase Members ---

        int ISkillBase.ID { get { return this.ID; } }

        void ISkillBase.Execute(Action onFinished)
        {
            this.Execute(onFinished);
        }

        ISkillBase ISkill.Instantiate(SkillContext skillContext)
        {
            return SkillInstantiate(skillContext);
        }

        void ISkill.OnTemplateRegist()
        {
            OnTemplateRegist();
        }

        void ISkill.OnTemplateUnregist()
        {
            OnTemplateUnregist();
        }

        void ISkillBase.SerializeRecovery(SkillContext skillContext)
        {
            SerializeRecovery(skillContext);
        }

        #endregion

        #region --- ICloneable Members ---

        public object Clone()
        {
            return base.MemberwiseClone();
        }

        #endregion
    }
}

仅仅这些,我们还无法实例化技能,我们还需要一个技能管理类

它是一个单例,并且作为一个可用组件

通过技能模版字典存储所有的基本技能模版,通过公共接口对公共技能进行访问获取,这样的设计使的所有的技能都只需要实例化一次,因为它是共用的。

public class SkillManager : MonoBehaviour
{
  static bool mIsDestroying;
  static SkillManager mInstance;//单例对象
  public static SkillManager Instance
  {
    get
    {
      if (mIsDestroying) return null;
      if (mInstance == null)
      {
        mInstance = new GameObject("[SkillManager]").AddComponent<SkillManager>();
        DontDestroyOnLoad(mInstance.gameObject);
      }
      return mInstance;
    }
  }
  Dictionary<int, ISkill> mSkillTemplateDict;//技能模板字典

  void Awake()
  {
    mSkillTemplateDict = new Dictionary<int, ISkill>(10);
  }
  void OnDestroy()
  {
    mIsDestroying = true;//单例处理
  }
  public void RegistToTemplate(ISkill skill)//模板注册
  {
    mSkillTemplateDict.Add(skill.ID, skill);
  }
  public bool UnregistFromTemplate(int skillID)//模板反注册
  {
    return mSkillTemplateDict.Remove(skillID);
  }
  public ISkillBase InstantiateSkill(int skillID, SkillContext skillContext)//技能实例化
  {
    return mSkillTemplateDict[skillID].Instantiate(skillContext);
  }
}

然后我们最后只需要定义一个技能初始化的类,来对我们自己个性化的技能进行模版注册即可

并使用[DefaultExecutionOrder(-100)]来让它的执行顺序优先,防止出现空异常

[DefaultExecutionOrder(-100)]
public class SkillManagerInitialization : MonoBehaviour
{
  void Awake()
  {
    SkillManager.Instance.RegistToTemplate(new PlayerSkill_Roll());
    SkillManager.Instance.RegistToTemplate(new PlayerSkill_SkillAttack1());
    SkillManager.Instance.RegistToTemplate(new PlayerSkill_SkillAttack2());
  }
}

猜你喜欢

转载自blog.csdn.net/qq_52324195/article/details/125745094