技能的释放有两个阶段

1.请求使用技能 UseSkill()

使用技能是使用技能的第一个阶段:键入

比如说你想释放Q技能按下了Q建

就进入了使用技能阶段

这一段逻辑会判断是否满足所有先决条件进入释放技能阶段

如技能是否cd完毕

2.释放技能 ReleaseSkill()

当满足的所有了先决条件进入释放技能阶段

此时此刻是否切换到释放的技能状态

这个需要由状态机本身来决定

若是此时无法切换

则判定释放失败

此时就出现了行为树与状态机的通信的需求

具体行为树案例:

具体案例

这颗技能行为树需要得到释放技能节点的返回结果来决定技能是否进入cd

所以我们需要细致的定制这颗技能释放节点逻辑

实现

首当其冲的就是节点Action的整体逻辑

using MDDGameFramework;
using MDDGameFramework.Runtime;
using Sirenix.OdinInspector;
using System;
using Result = MDDGameFramework.Action.Result;
using Request = MDDGameFramework.Action.Request;

namespace MDDSkillEngine
{
    [Title("释放技能", TitleAlignment = TitleAlignments.Centered)]
    public class NP_ReleaseSkillAction : NP_ClassForAction
    {
        public int SkillId;

        public override Func GetFun3ToBeDone()
        {
            this.Func3 = ReleaseSkill;
            return this.Func3;
        }

        private Result ReleaseSkill(Request request)
        {
            Entity entity = owner as Entity;

            if (entity == null)
            {
                Log.Error("{0}技能未关联实体{1}", LogConst.Skill, SkillId);
            }

            ISkillSystem skillSystem = Game.Skill.GetSkillSystem(entity.Id);

            if (request == Request.START)
            {
                skillSystem.ReleaseSkill(SkillId);//请求释放技能
                return Result.PROGRESS;
            }
            else if (request == Request.UPDATE)
            {
                if (skillSystem.GetSkillReleaseResultType() == SkillReleaseResultType.STOP)//如果释放技能被停止则返回失败
                {
                    skillSystem.SetSkillReleaseResultType(SkillReleaseResultType.NONE);
                    return Result.FAILED;
                }

                if (skillSystem.GetSkillReleaseResultType() == SkillReleaseResultType.FAIL)//如果释放技能失败则返回失败
                {
                    skillSystem.SetSkillReleaseResultType(SkillReleaseResultType.NONE);
                    return Result.FAILED;
                }

                if (skillSystem.GetSkillReleaseResultType() == SkillReleaseResultType.PROGRESS)//如果为释放过程中则继续等待结果
                {
                    return Result.PROGRESS;
                }

                if (skillSystem.GetSkillReleaseResultType() == SkillReleaseResultType.SUCCSE)//如果释放成功则返回成功
                {
                    skillSystem.SetSkillReleaseResultType(SkillReleaseResultType.NONE);
                    return Result.SUCCESS;
                }
            }
            else if (request == Request.CANCEL)//取消释放则返回失败
            {
                skillSystem.SetSkillReleaseResultType(SkillReleaseResultType.NONE);
                return Result.FAILED;
            }


            return Result.PROGRESS;
        }     
    }
}


在NPBehave中当我们使用Func对action进行行为定制

其内部会将其处理为一个持续行为

既向事件驱动时钟添加UpdateObserver

其中SetSkillReleaseResultType方法为

   /// 
        /// 设置当前技能释放结果
        /// 
        /// 
        public void SetSkillReleaseResultType(SkillReleaseResultType skillReleaseResultType)
        {
            Log.Info("{0}设置技能释放状态,{1}", LogConst.Skill, skillReleaseResultType);
            CurrentSkillState = skillReleaseResultType;
        }

这样我们就可以通过此action在获得返回结果的行为过程中持续监听技能释放状态

我暂时的做法是已一个技能系统中的普通变量CurrentSkillState来保存当前技能的释放状态作为一个简单的桥梁

接下来我们需要在改变技能释放状态的关键点位加上对CurrentSkillState的控制

目前在MDDSkillEngine中有以下几种情况

1.释放技能无绑定状态

2.技能状态跳转观察函数

3.Slate CD轨道(走过cdclip意味着技能进入cd 释放成功)

        /// 
        /// 释放技能
        /// 
        /// 
        public virtual void ReleaseSkill(int id)
        {
            IFsm fsm = Game.Fsm.GetFsm(m_Owner.Id.ToString());
            IDataTable dtSkill = Game.DataTable.GetDataTable();
            DRSkill drSkill = dtSkill.GetDataRow(id);

            //通过技能id对应到关联的state实例
            //如果没有关联则直接进入释放状态
            if (drSkill.IsState)
            {
                fsm.SetData(drSkill.StateName, true);
            }
            else
            {
                SetSkillReleaseResultType(SkillReleaseResultType.PROGRESS);
                Log.Error("{0}Skill{1}未关联state", LogConst.Skill, id);
            }

            Game.Event.Fire(this, ReleaseSkillEventArgs.Create(this, id));
        }
        
        /// 
        /// 状态跳转
        /// 基于黑板的观察函数
        /// 
        /// 
        /// 
        protected override void Observing(Blackboard.Type type, Variable newValue)
        {
            VarBoolean varBoolean = (VarBoolean)newValue;

            if (varBoolean.Value == false)
                return;

            if (Fsm.GetCurrStateName() == "AkiIdleState")
            {
                ISkillSystem skillSystem = Game.Skill.GetSkillSystem(Fsm.Owner.Id);
                skillSystem.SetSkillReleaseResultType(SkillReleaseResultType.PROGRESS);
                ChangeState(Fsm,GetType());
            }
            else
            {
                ISkillSystem skillSystem = Game.Skill.GetSkillSystem(Fsm.Owner.Id);
                skillSystem.SetSkillReleaseResultType(SkillReleaseResultType.FAIL);
            }
        }


       //CDClip
       public override void Enter()
        {
            //将技能状态设置为成功 
            ISkillSystem skillSystem = Game.Skill.GetSkillSystem(actor.Id);
            skillSystem.SetSkillReleaseResultType(SkillReleaseResultType.SUCCSE);
       }

这样我们就实现了行为树与状态机的通信


记录历程,整理思路,共享知识,分享思维。