MMORPG学习笔记——简单战斗系统(一)有限状态机控制

战斗框架主要由有限状态机(FSM),AI,角色信息(RoleInfo),以及控制他们的角色控制器四个部分组成。
在这里插入图片描述

整个加载过程为:角色控制器绑定在角色或者角色模型的父物体上,然后每次登录场景之后,创建角色或者怪物,然后通过角色或者怪物身上的初始化函数,初始化FSM,AI,RoleInfo,即通过实例化脚本,通过构造函数将角色控制器传递给脚本,达到降低耦合的作用

首先是动画状态机的控制,如果选择新动画机,只需要将各种状态通过连线进行连接,通过条件判断执行动画,就可以达到动画切换的目的,但是,这其中会有一些问题。第一:一旦动画个数达到一定程度,再添加动画,新增的动画间的连线也会剧增,不方便管理。第二:在每次判定动画切换的时候,需要的条件前提也会多很多,会增加很多不必要的判定。

所以这里我们选择有限状态机,通过将每种动画播放分为切换动画,播放动画,结束动画三个部分,然后通过有限状态机对他们进行总体的控制。好处在于可以减少动画之间线的复杂程度,以及不必要的判定,以及代码的复用性。
同样需要连线,以及动画切换判定,但是连线方式有所不同;
在这里插入图片描述

每种动画的切换方式也需要稍微修改一下,以保证他们动画平滑的切换。
在这里插入图片描述

第二:因为每种状态都有切开始播放,播放动画,结束动画三种状态,所以抽象出一个角色状态抽象类RoleStateAbstract。

/// <summary>
/// 角色状态的抽象基类
/// </summary>
public abstract class RoleStateAbstract 
{
    /// <summary>
    /// 当前角色有限状态机管理器
    /// </summary>
    public RoleFSMMgr CurrRoleFSMMgr { get; private set; }

    public AnimatorStateInfo CurrRoleAnimatorStateInfo { get; set; }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="roleFSMMgr"></param>
    public RoleStateAbstract(RoleFSMMgr roleFSMMgr)
    {
        CurrRoleFSMMgr = roleFSMMgr;
    }

    /// <summary>
    /// 进入状态
    /// </summary>
    public virtual void OnEnter() { }

    /// <summary>
    /// 执行状态
    /// </summary>
    public virtual void OnUpdate() { }

    /// <summary>
    /// 离开状态
    /// </summary>
    public virtual void OnLeave() { }
}

然后其他的角色状态类继承抽象状态类,例如待机状态

/// <summary>
/// 待机状态
/// </summary>
public class RoleStateIdle : RoleStateAbstract
{

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="roleFSMMgr">有限状态机管理器</param>
    public RoleStateIdle(RoleFSMMgr roleFSMMgr) : base(roleFSMMgr)
    {

    }

    /// <summary>
    /// 实现基类 进入状态
    /// </summary>
    public override void OnEnter()
    {
        base.OnEnter();
        CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetBool(ToAnimatorCondition.ToIdleFight.ToString(), true);

    }

    /// <summary>
    /// 实现基类 执行状态
    /// </summary>
    public override void OnUpdate()
    {
        base.OnUpdate();
		//得到动画机第一层的角色当前状态信息
        CurrRoleAnimatorStateInfo = CurrRoleFSMMgr.CurrRoleCtrl.Animator.GetCurrentAnimatorStateInfo(0);
		//如果状态是Idle_Fight
        if (CurrRoleAnimatorStateInfo.IsName(RoleAnimatorName.Idle_Fight.ToString()))
        {
            CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetInteger(ToAnimatorCondition.CurrentState.ToString(), (int)RoleState.Idle);
        }
        else
        {
            CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetInteger(ToAnimatorCondition.CurrentState.ToString(), (int)RoleState.None);
            
        }
    }

    /// <summary>
    /// 实现基类 离开状态
    /// </summary>
    public override void OnLeave()
    {
        base.OnLeave();
        CurrRoleFSMMgr.CurrRoleCtrl.Animator.SetBool(ToAnimatorCondition.ToIdleFight.ToString(), false);
    }
}

其他状态因为不是循环动画,所以需要添加一个判断,在动画播放结束后,进入待机状态

		///如果动画执行一遍就切换待机
        if (CurrRoleAnimatorStateInfo.normalizedTime > 1)
        {
            CurrRoleFSMMgr.CurrRoleCtrl.ToIdle();
        }

通过角色有限状态机执行所有动画之间的切换,并将它们封装成一个方法,供角色控制器调用。

/// <summary>
/// 角色有限状态机管理器
/// </summary>
public class RoleFSMMgr
{
    /// <summary>
    /// 当前角色控制器
    /// </summary>
    public RoleCtrl CurrRoleCtrl { get; private set; }

    /// <summary>
    /// 当前角色状态枚举
    /// </summary>
    public RoleState CurrRoleStateEnum { get; private set; }

    /// <summary>
    /// 当前角色状态
    /// </summary>
    private RoleStateAbstract m_CurrRoleState = null;

    /// <summary>
    /// 关于角色状态枚举和当前角色状态的字典
    /// </summary>
    private Dictionary<RoleState, RoleStateAbstract> m_RoleStateDic;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="currRoleCtrl"></param>
	public RoleFSMMgr(RoleCtrl currRoleCtrl)
    {
        CurrRoleCtrl = currRoleCtrl;
        m_RoleStateDic = new Dictionary<RoleState, RoleStateAbstract>();

        m_RoleStateDic[RoleState.Idle] = new RoleStateIdle(this);
        m_RoleStateDic[RoleState.Run] = new RoleStateRun(this);
        m_RoleStateDic[RoleState.Attack] = new RoleStateAttack(this);
        m_RoleStateDic[RoleState.Hurt] = new RoleStateHurt(this);
        m_RoleStateDic[RoleState.Die] = new RoleStateDie(this);

        if(m_RoleStateDic.ContainsKey(CurrRoleStateEnum))
        {
            m_CurrRoleState = m_RoleStateDic[CurrRoleStateEnum];
        }
    }

    #region OnUpdate 每帧执行
    /// <summary>
    /// 每帧执行
    /// </summary>
    public void OnUpdate()
    {
        if(m_CurrRoleState!=null)
        {
            m_CurrRoleState.OnUpdate();
        }
    }
    #endregion

    /// <summary>
    /// 切换状态
    /// </summary>
    /// <param name="newState">新状态</param>
    public void ChangeState(RoleState newState)
    {
        if(CurrRoleStateEnum==newState)
        {
            return;
        }

        //调用以前状态的离开方法
        if(m_CurrRoleState!=null)
        {
            m_CurrRoleState.OnLeave();
        }

        //更改当前状态枚举
        CurrRoleStateEnum = newState;

        //更改当前状态
        m_CurrRoleState = m_RoleStateDic[newState];

        //调用新状态的进入方法
        m_CurrRoleState.OnEnter();
    }
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值