通用的FSM有限状态机(状态管理,可实现NPC简单AI)

在角色状态较多,状态之间切换关系复杂时,简单的使用if...else...和switch...case...已经满足不了需求了,不仅代码结构很乱,难以扩展,别人看起来也会很费劲,这时候就需要FSM有限状态机,清晰方便的实现多状态管理

参考:

unity3D FSM有限状态机(状态设计模式)

一个通用的有限状态机(FSM)框架

状态机结构示意图:

 首先要拆分角色状态,列成枚举

public enum FSMStateID
{
    Idle,
    Move,
    Attack,
    Damage,
    Death,
}

 状态基类

//主要是构造函数,进入、更新、退出状态接口

public abstract class FSMStateTemplate
{
    public FSMStateID stateID { get; protected set; }
    public FSMHandlerBase fsmHandler { get; protected set; }

    public FSMStateTemplate(FSMStateID fsmStateID, FSMHandlerBase handler)
    {
        stateID = fsmStateID;
        fsmHandler = handler;
    }

    /// 进入动作
    public abstract void OnEnterAct(params object[] args);
    /// 离开动作
    public abstract void OnExitAct(params object[] args);
    /// 输入动作
    public abstract void OnUpdateAct(params object[] args);
}

// 增加泛型,用以区分不同角色(比如区分开玩家和小怪、Boss)

public abstract class FSMStateBase<T> : FSMStateTemplate where T: RoleBase
{
    public T targetRole { get; protected set; }

    public FSMStateBase(FSMStateID fsmStateID, FSMHandlerBase handler, T role) : base(fsmStateID, handler)
    {
        targetRole = role;
    }
}

状态管理器基类

//管理当前角色的所有状态,主要有增加、移除、切换状态函数

//增加FSMAction枚举,比如小怪碰撞到玩家,近战小怪会攻击,远程小怪会逃跑,这时候只需要增加"TouchPlayer"枚举,近战小怪的状态管理器切换到攻击状态,远程小怪的状态管理器切换到逃跑状态。


public enum FSMAction
{
    None,
    DropOut,
    EndGame,
}

public abstract class FSMHandlerBase
{
    public FSMStateID curStateID { get; protected set; }
    public FSMStateID lastStateID { get; protected set; }

    protected Dictionary<FSMStateID, FSMStateTemplate> stateDic;

    //protected FSMStateTemplate lastState;
    protected FSMStateTemplate curState;

    public FSMHandlerBase()
    {
        stateDic = new Dictionary<FSMStateID, FSMStateTemplate>();

        curStateID = FSMStateID.Idle;
        lastStateID = FSMStateID.Idle;
        curState = null;
    }

    public void AddState(FSMStateTemplate state)
    {
        if (state == null)
        {
            Debug.LogError(state + "为空");
            return;
        }

        if (stateDic.ContainsValue(state))
        {
            Debug.LogError(state + "已经存在");
        }
        else
        {
            stateDic.Add(state.stateID, state);
        }
    }

    public bool RemoveState(FSMStateID stateID)
    {
        if (stateDic.ContainsKey(stateID))
        {
            stateDic.Remove(stateID);
            return true;
        }
        else
        {
            Debug.LogError(stateID + "不存在");
            return false;
        }
    }

    public virtual void TransitionState(FSMAction action, params object[] args)
    {
        switch (action)
        {
            case FSMAction.None:
                TransitionState(FSMStateID.Idle, args);
                break;
            case FSMAction.EndGame:
                TransitionState(FSMStateID.Idle, args);
                break;
        }
    }

    public void TransitionState(FSMStateID stateID, params object[] args)
    {
        if(stateDic.ContainsKey(stateID))
        {
            if (curState != null)
                curState.OnExitAct(args);

            if (lastStateID != curStateID)
                lastStateID = curStateID;

            curStateID = stateID;
            curState = stateDic[stateID];
            curState.OnEnterAct(args);
        }
    }

    public void UpdateFSM(params object[] args)
    {
        if (curState != null)
        {
            curState.OnUpdateAct(args);
        }
    }

}

每个类型的角色继承状态管理器基类

//主要重写"TransitionState(FSMAction action, params object[] args)"函数,不同小怪对相同事件可以发出不同的反应

public class PlayerFSMHandler : FSMHandlerBase
{
    public override void TransitionState(FSMAction action, params object[] args)
    {
        base.TransitionState(action, args);
    }
}
public class EnemyFSMHandler : FSMHandlerBase
{
    public override void TransitionState(FSMAction action, params object[] args)
    {
        base.TransitionState(action, args);
    }
}

因为现在的项目只区分敌人和玩家,而且两者有一部分状态相同或类似,我增加了一个角色状态基类"Role***State",继承了状态基类"StateBase",然后再有"Player***State"和"Enemy***State",(也可以"Player***State"和"Enemy***State"直接继承"StateBase")

 比如:

//RoleAttackState 继承 FSMStateBase

public class RoleAttackState<T> : FSMStateBase<T> where T : RoleBase
{

    public RoleAttackState(FSMStateID fsmStateID, FSMHandlerBase handler, T role) : base(fsmStateID, handler, role)
    {

    }

    public override void OnEnterAct(params object[] args)
    {
        targetRole.modelHandler.OnEnterState(FSMStateID.Attack);
    }

    public override void OnExitAct(params object[] args)
    {
        targetRole.modelHandler.OnExitState(FSMStateID.Attack);
    }

    public override void OnUpdateAct(params object[] args)
    {

    }

    protected void Attack()
    {
        if (targetRole.attackTarget == null)
            fsmHandler.TransitionState(FSMStateID.Idle);
        else
            targetRole.launchHandler.Fire();
    }

}

RoleAttackState 继承 RoleAttackState

public class RoleAttackState<T> : FSMStateBase<T> where T : RoleBase
{

    public RoleAttackState(FSMStateID fsmStateID, FSMHandlerBase handler, T role) : base(fsmStateID, handler, role)
    {

    }

    public override void OnEnterAct(params object[] args)
    {
        targetRole.modelHandler.OnEnterState(FSMStateID.Attack);
    }

    public override void OnExitAct(params object[] args)
    {
        targetRole.modelHandler.OnExitState(FSMStateID.Attack);
    }

    public override void OnUpdateAct(params object[] args)
    {

    }

    protected void Attack()
    {
        if (targetRole.attackTarget == null)
            fsmHandler.TransitionState(FSMStateID.Idle);
        else
            targetRole.launchHandler.Fire();
    }

}

EnemyAttackState 继承 RoleAttackState


public class EnemyAttackState : RoleAttackState<EnemyBase>
{
    float curAttackCount;
    float lastAttackTime;

    float aimStartTime;
    float aimDuration;

    public EnemyAttackState(FSMStateID fsmStateID, FSMHandlerBase handler, EnemyBase role) : base(fsmStateID, handler, role)
    {

    }

    public override void OnEnterAct(params object[] args)
    {
        base.OnEnterAct(args);

        curAttackCount = 0;
        lastAttackTime = 0;

        aimStartTime = Time.time;
        aimDuration = 0;
        if (targetRole.roleConfig.aimTime > 0)
        {
            targetRole.launchHandler.AimAt();
            targetRole.modelHandler.LockDir(true);
        }
    }

    public override void OnExitAct(params object[] args)
    {
        base.OnExitAct(args);

        targetRole.modelHandler.LockDir(false);
    }

    public override void OnUpdateAct(params object[] args)
    {
        base.OnUpdateAct(args);

        if (aimDuration >= targetRole.roleConfig.aimTime)
        {
            if (Time.time - lastAttackTime >= targetRole.roleConfig.attackFrequency)
            {
                lastAttackTime = Time.time;
                if (curAttackCount < targetRole.roleConfig.attackCount)
                {
                    Attack();
                    curAttackCount++;
                }
                else
                {
                    targetRole.StopAttack();
                }
            }
        }
        else
        {
            if (Time.time - aimStartTime >= targetRole.roleConfig.aimTime)
            {
                aimDuration = targetRole.roleConfig.aimTime + 1;
                targetRole.launchHandler.AimHide();
            }
        }
    }

}

把所有的角色状态类实现之后, 在角色类中创建状态管理器,并加入该角色的所有状态类即可

public class Player : RoleBase
{
    PlayerFSMHandler playerFSMHandler;

    protected void Start()
    {
        playerFSMHandler = new PlayerFSMHandler();

        PlayerIdleState playerIdleState = new PlayerIdleState(FSMStateID.Idle, playerFSMHandler, this);
        PlayerMoveState playerMoveState = new PlayerMoveState(FSMStateID.Move, playerFSMHandler, this);
        PlayerAttackState playerAttackState = new PlayerAttackState(FSMStateID.Attack, playerFSMHandler, this);
        //PlayerDamageState playerDamageState = new PlayerDamageState(FSMStateID.Damage, playerFSMHandler, this);
        PlayerDeathState playerDeathState = new PlayerDeathState(FSMStateID.Death, playerFSMHandler, this);

        playerFSMHandler.AddState(playerIdleState);
        playerFSMHandler.AddState(playerMoveState);
        playerFSMHandler.AddState(playerAttackState);
        //playerFSMHandler.AddState(playerDamageState);
        playerFSMHandler.AddState(playerDeathState);
    }
}
发布了104 篇原创文章 · 获赞 74 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_39108767/article/details/103051266