利用设计模式开发游戏之状态模式中的有限状态机

此次的有限状态机是设置已知的条件,根据条件转换状态

状态条件转换图

利用设计模式开发游戏之状态模式中的有限状态机_第1张图片

一、设计战士状态机基类

1、设计战士状态转换条件枚举以及对应的战士状态

通过上面关系图可以得到以下几个战士状态转换条件

/// 
/// 战士转换条件
/// 
public enum SoldierTransition
{
    NullTransition,
    SeeEnemy,
    NoEnemy,
    CanAttack
}

对应的战士状态

/// 
/// 战士状态ID
/// 
public enum SoldierStateID
{
    None,
    Idle,
    Chase,
    Attack 
}

2、设计战士状态及系统类

/// 
/// 战士状态机系统类
/// 
public class SoldierFSMSystem
{
}

3、设计战士状态机基类

/// 
/// 战士状态基类
/// 
public abstract class ISoldierState
{
    /// 
    /// 转换条件与状态ID映射
    /// 
    protected Dictionary mTransitionAndStateIdDic = new Dictionary();

    protected SoldierStateID mStateId;
    /// 
    /// 当前状态ID
    /// 
    public SoldierStateID StateId
    {
        get { return mStateId; }
    }

    public ISoldierState(SoldierFSMSystem fsm, ICharacter character)
    {
        mFSM = fsm;
        mCharacter = character;
    }
    /// 
    /// 角色基类(战士类与敌人类的共有的基类)
    /// 
    protected ICharacter mCharacter;
    protected SoldierFSMSystem mFSM;
    /// 
    /// 添加转换条件
    /// 
    /// 
    /// 
    public void AddTransition(SoldierTransition trans, SoldierStateID id)
    {
        if (trans == SoldierTransition.NullTransition || id == SoldierStateID.None)
        {
            Debug.LogErrorFormat("SoldierState Error:trans或者id不能为null");
            return;
        }

        if (mTransitionAndStateIdDic.ContainsKey(trans))
        {
            Debug.LogErrorFormat("Soldier State Error:{0}已经添加了", trans);
            return;
        }
        mTransitionAndStateIdDic.Add(trans, id);
    }
    /// 
    /// 删除转换条件
    /// 
    /// 
    public void DeleteTransition(SoldierTransition trans)
    {
        if (!mTransitionAndStateIdDic.ContainsKey(trans))
        {
            Debug.LogErrorFormat("SoldierState Error:trans不能为null");
            return;
        }

        mTransitionAndStateIdDic.Remove(trans);
    }
    /// 
    /// 获取当前状态ID根据转换条件
    /// 
    /// 
    /// 
    public SoldierStateID GetOutPutState(SoldierTransition trans)
    {
        if (!mTransitionAndStateIdDic.ContainsKey(trans))
        {
            Debug.LogErrorFormat("SoldierState Error:trans不存在");
            return SoldierStateID.None;
        }
        return mTransitionAndStateIdDic[trans];
    }
    //进入状态
    public virtual void DoBeforeEnter() { }
    //离开状态
    public virtual void DoBeforeLeave() { }

    //状态更新函数,传入当前目标集合
    public abstract void Reason(List targets);
    public abstract void Action(List targets);
}

二、设计战士状态机系统类

此类负责战士状态的添加、删除、和转换条件

using System.Collections.Generic;
using UnityEngine;
/// 
/// 战士状态机系统类
/// 
public class SoldierFSMSystem
{
    /// 
    /// 战士状态列表
    /// 
    private List mStates = new List();
    private ISoldierState mCurState;
    /// 
    /// 当前状态
    /// 
    public ISoldierState CurState
    {
        get { return mCurState; }
    }
    /// 
    /// 添加多个状态
    /// 
    /// 
    public void AddStates(params ISoldierState[] states)
    {
        foreach (ISoldierState state in states)
        {
            AddState(state);
        }
    }
    /// 
    /// 添加状态
    /// 
    /// 
    public void AddState(ISoldierState state)
    {
        if (state == null)
        {
            Debug.LogError("SoldierFSMSystem Error:状态为null");
            return;
        }

        if (mStates.Count == 0)
        {
            mStates.Add(state);
            mCurState = state;
            return;
        }

        for (int i = 0; i < mStates.Count; i++)
        {
            if (mStates[i].StateId == state.StateId)
            {
                Debug.LogError("SoldierFSMSystem Error:状态已存在:" + state.StateId);
                return;
            }
        }
        mStates.Add(state);
    }
    /// 
    /// 删除状态
    /// 
    /// 
    public void DeleteState(SoldierStateID stateId)
    {
        if (stateId == SoldierStateID.None)
        {
            Debug.LogError("SoldierFSMSystem DeleteState Error:状态为null");
            return;
        }
        for (int i = 0; i < mStates.Count; i++)
        {
            if (mStates[i].StateId == stateId)
            {
                mStates.Remove(mStates[i]);
                return;
            }
        }
        Debug.LogError("SoldierFSMSystem DeleteState Error:StateId不存在");
    }
    /// 
    /// 转换状态根据转换条件
    /// 
    /// 
    public void PerformTransition(SoldierTransition trans)
    {
        if (trans == SoldierTransition.NullTransition)
        {
            Debug.LogError("SoldierFSMSystem PerformTransition Error:转换条件为null");
            return;
        }
        //根据转换条件获取当前对应的状态ID
        SoldierStateID nextStateId = mCurState.GetOutPutState(trans);
        if (nextStateId == SoldierStateID.None)
        {
            Debug.LogError("SoldierFSMSystem PerformTransition Error:获取下一个转换条件为null");
            return;
        }

        foreach (ISoldierState state in mStates)
        {
            if (state.StateId == nextStateId)//如果存在这样的状态
            {
                mCurState.DoBeforeLeave();//当前状态执行离开操作
                mCurState = state;//将新的状态赋值给当前状态
                state.DoBeforeEnter();//新的状态执行进入状态操作
                return;
            }

        }
    }
}

三、添加战士具体状态类

1、添加战士待机状态

继承战士状态基类,在构造中设置当前状态ID为Idle,循环判断当有敌人时,切换设置状态条件为SeeEnemy,即切换到追击敌人Chase状态

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// 
/// 战士待机状态类
/// 
public class SoldierIdleState : ISoldierState {
    public SoldierIdleState(SoldierFSMSystem fsm, ICharacter character) : base(fsm,character)
    {
        mStateId = SoldierStateID.Idle;
    }
    public override void Reason(List targets)
    {
        if (targets != null && targets.Count > 0)
        {
            mFSM.PerformTransition(SoldierTransition.SeeEnemy);//如果存在敌人则执行切换到看到敌人条件,即追击敌人状态
        }
    }

    public override void Action(List targets)
    {
       
    }
    /// 
    /// 状态进入时操作
    /// 
    public override void DoBeforeEnter()
    {
        mCharacter.PlayAnim("stand");//执行待机动画
    }
}

2、添加追击敌人状态

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// 
/// 战士追击敌人状态
/// 
public class SoldierChaseState : ISoldierState
{
    public SoldierChaseState(SoldierFSMSystem fsm, ICharacter character) : base(fsm,character)
    {
        mStateId = SoldierStateID.Chase;
    }
    public override void Reason(List targets)
    {
        if (targets == null || targets.Count <= 0)//没有敌人时
        {
            mFSM.PerformTransition(SoldierTransition.NoEnemy);
            return;
        }

        float distance = Vector3.Distance(targets[0].Position, mCharacter.Position);
        if (distance < mCharacter.AttackRange)//如果在攻击范围内
        {
            mFSM.PerformTransition(SoldierTransition.CanAttack);//开始攻击
        }
    }

    public override void Action(List targets)
    {
        if (targets != null && targets.Count > 0)//有敌人时
        {
            mCharacter.SetNavMoveTarget(targets[0].Position);//寻路到敌人的位置
        }
    }
}

上面代码其中mCharacter.SetNavMoveTarget(targets[0].Position);内容如下

    /// 
    /// 设置寻路目标
    /// 
    /// 
    public void SetNavMoveTarget(Vector3 targetPos)
    {
        mNav.SetDestination(targetPos);//mNav为角色身上的NavMeshAgent组件
    }

3、添加战士攻击敌人状态类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// 
/// 战士攻击敌人类
/// 
public class SoldierAttackState : ISoldierState
{
    private float mAttackTime = 1;
    private float mAttackTimer = 0;
    public SoldierAttackState(SoldierFSMSystem fsm, ICharacter character) : base(fsm,character)
    {
        mStateId = SoldierStateID.Attack;
    }

    /// 
    /// 状态更新类,传入当前角色
    /// 
    /// 
    public override void Reason(List targets)
    {
        if (targets == null || targets.Count <= 0)//没有敌人时
        {
            mFSM.PerformTransition(SoldierTransition.NoEnemy);
        }

        float distance = Vector3.Distance(mCharacter.Position, targets[0].Position);
        if (distance > mCharacter.AttackRange)//没有在攻击范围内
        {
            mFSM.PerformTransition(SoldierTransition.SeeEnemy);//追击敌人
        }
    }
    /// 
    /// 状态更新类,传入当前角色
    /// 
    /// 
    public override void Action(List targets)
    {
        if (targets == null || targets.Count <= 0) return;//没有敌人时
        //有敌人时
        mAttackTimer += Time.deltaTime;
        if (mAttackTimer >= mAttackTime)//每隔一秒攻击一次
        {
            mCharacter.Attack(targets[0]);//执行战士攻击动作,传入当前攻击目标
            mAttackTimer = 0;
        }
    }
}

四、战士基类中初始化状态机

战士基类继承自角色基类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// 
/// 战士基类
/// 
public class ISoldier : ICharacter
{
    /// 
    /// 战士状态机
    /// 
    protected SoldierFSMSystem mFSMSystem;

    public ISoldier() : base()
    {
        InitFSM();
    }
    /// 
    /// 循环更新状态机
    /// 
    /// 
    public void UpdateFSMAI(List targets)
    {
        mFSMSystem.CurState.Reason(targets);
        mFSMSystem.CurState.Action(targets);
    }
    /// 
    /// 初始化战士状态机
    /// 
    public void InitFSM()
    {
        mFSMSystem = new SoldierFSMSystem();
        SoldierIdleState idleState = new SoldierIdleState(mFSMSystem, this);
        idleState.AddTransition(SoldierTransition.SeeEnemy, SoldierStateID.Chase);

        SoldierChaseState chaseState = new SoldierChaseState(mFSMSystem, this);
        chaseState.AddTransition(SoldierTransition.NoEnemy, SoldierStateID.Idle);
        chaseState.AddTransition(SoldierTransition.CanAttack, SoldierStateID.Attack);

        SoldierAttackState attackState = new SoldierAttackState(mFSMSystem, this);
        attackState.AddTransition(SoldierTransition.NoEnemy, SoldierStateID.Idle);
        attackState.AddTransition(SoldierTransition.SeeEnemy, SoldierStateID.Chase);
        mFSMSystem.AddStates(idleState,chaseState,attackState);
    }
}

 

你可能感兴趣的:(Unity游戏开发常用技术,unity3d,有限状态机,c#)