Unity FSM有限状态机

FSM介绍

FSM 即有限状态机,它是一个状态管理系统,表示一个对象的几种状态在指定条件下转移行为,即随着条件的不断改变内部状态不断地切换。

用途

通常使用FSM去实现一些简单的AI逻辑,对于游戏中的每个对象都可以在其生命周期中分出一些状态,比如一个怪物,他可能在休息,或者巡逻,当玩家出现时,他的状态可能切换为追逐敌人或者攻击敌人,当某些条件成立时,状态机从当前状态转移到下一状态,在不同状态下有不同的任务,所以要使用有限状态机去实现。

优点

使整个状态切换逻辑比较清晰,增强代码的可扩展性,也便于后期维护

具体代码示例

创建脚本命名:StateID.cs 。使用枚举类型 管理所有的状态

public enum StateID  {
    NullStateID=0,//空(系统默认状态)
    IdleState,//空闲状态
    PatrolState,//巡逻状态
    ChaseState	//追逐状态
}

创建脚本命名:Transiton.cs 。使用枚举类型管理所有转换条件

public enum Transiton  {
    NullTransition=0,//空(系统默认条件)
    NoSeePlayer,//没有发现玩家时
    SeePlayer,//发现玩家时
    GamePause	//与玩家距离超出追逐范围时
}

创建脚本命名:FSMState.cs 。状态基类,负责处理一个状态的周期,状态的进入前,状态中,离开状态等。以及状态切换条件的增删。


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public abstract class FSMState {

    protected FSMSystem fsm;

    protected Dictionary<Transiton, StateID> map = new Dictionary<Transiton, StateID>();//存储所有的转换条件和状态

    protected StateID stateID;

    public StateID ID { get { return stateID; } }

    public FSMState(FSMSystem fsm) {
        this.fsm = fsm;
    }
    /// <summary>
    /// 添加转换条件
    /// </summary>
    /// <param name="trans"></param>
    /// <param name="id"></param>
    public void AddTransition(Transiton trans,StateID id) {
        if (trans==Transiton.NullTransition) {
            Debug.Log("错误:不能添加空的转换条件");
            return;
        }
        if (map.ContainsKey(trans)) {
            Debug.Log("错误:转换条件已存在");
            return;
        }
        if (id==StateID.NullStateID) {
            Debug.Log("错误:不能添加空的状态");
            return;
        }
        map.Add(trans,id);
    }
    /// <summary>
    /// 删除转换条件
    /// </summary>
    /// <param name="trans"></param>
    public void DeleteTransition(Transiton trans) {
        if (trans==Transiton.NullTransition) {
            Debug.Log("错误:禁止删除空的转换条件");
            return;
        }
        if (!map.ContainsKey(trans)) {
            Debug.Log("错误:不存在该转换条件"+trans);
            return;
        }
        map.Remove(trans);

    }
    /// <summary>
    /// 通过转换条件获取状态
    /// </summary>
    /// <param name="trans"></param>
    /// <returns></returns>
    public StateID GetOutputState(Transiton trans) {
        if (map.ContainsKey(trans)) {
            return map[trans];
        }
        return StateID.NullStateID;
    }

    /// <summary>
    ///  进入到状态之前 需要做的事情
    /// </summary>
    public virtual void DoBeforeEntering() { }
    /// <summary>
    /// 离开状态之前 需要做的事情
    /// </summary>
    public virtual void DoBeforeLeaving() { }
    /// <summary>
    /// 当前状态时需要处理的事情 
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public abstract void Action(GameObject player, GameObject npc);

    /// <summary>
    /// 检测是否切换状态
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public abstract void Reason(GameObject player,GameObject npc);


}

创建脚本命名:FSMSystem.cs。用来管理所有的状态(状态的添加,删除,切换,更新等)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FSMSystem  {

    private List<FSMState> states;//存储所有的状态
    private StateID currentStateID;//当前的状态标识
    public StateID CurrentStateID { get { return currentStateID; } }//提供外界获取当前状态标识的接口

    private FSMState currentState;//当前状态
    public FSMState CurrentState { get { return currentState; } }//提供外界获取当前状态的接口
    /// <summary>
    /// 构造函数 初始化FSMSystem
    /// </summary>
    public FSMSystem() {
        states = new List<FSMState>();
    }
    /// <summary>
    /// 执行当前状态的处理的事情和检测是否切换状态
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public void Update(GameObject player,GameObject npc) {
        currentState.Action(player,npc);
        currentState.Reason(player,npc);
    }
    /// <summary>
    /// 添加状态
    /// </summary>
    /// <param name="s"></param>
    public void AddState(FSMState s) {

        if (s==null)
        {
            Debug.Log(s+"状态不存在");
            return;
        }
        if (states.Count==0) {
            states.Add(s);
            currentState = s;
            currentStateID = s.ID;
            return;
        }
        foreach (FSMState state in states)
        {
            if (state.ID==s.ID) {
                Debug.Log("错误:已存在状态"+state.ID);
                return;
            }
        }
        states.Add(s);
    }
    /// <summary>
    /// 删除状态
    /// </summary>
    /// <param name="id"></param>
    public void DeleteState(StateID id) {
        if (id==StateID.NullStateID)
        {
            Debug.Log("NullStateID不可以删除");
        }
        foreach (FSMState  state in states)
        {
            if (state.ID==id) {
                states.Remove(state);
                return;
            }
        }
        Debug.Log(id+"该状态不存在");
    }
    /// <summary>
    /// 切换状态
    /// </summary>
    /// <param name="trans"></param>
    public void PerformTransition(Transiton trans) {
        if (trans==Transiton.NullTransition)
        {
            Debug.Log("不能转为NullTransition");
            return;
        }
        StateID id = currentState.GetOutputState(trans);
       
        if (id==StateID.NullStateID)
        {
            Debug.LogError(currentStateID.ToString()+"不存在状态"+trans.ToString());
            return;
        }
        currentStateID = id;
        foreach (FSMState state in states)
        {         
            if (state.ID==currentStateID) {              
                currentState.DoBeforeLeaving();
                currentState = state;           
                currentState.DoBeforeEntering();
                break;
            }
        }
    }
}

创建脚本:IdleState.cs。怪物空闲状态 时的行为和转换条件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class IdleState : FSMState{
    /// <summary>
    /// 空闲状态
    /// </summary>
    /// <param name="fsm"></param>
    public IdleState(FSMSystem fsm):base(fsm) {

        stateID = StateID.IdleState;
    }

    public override void Action(GameObject player, GameObject npc)
    {
      
    }
    /// <summary>
    /// 转换条件:2秒后转为巡逻状态
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public override void Reason(GameObject player, GameObject npc)
    {      
        if (Time.time>2)
        {          
            fsm.PerformTransition(Transiton.NoSeePlayer);
        }
    }
    public override void DoBeforeLeaving()
    {


    }

}

创建脚本:PatrolState.cs。怪物巡逻状态 时行为和装换条件


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PatrolState : FSMState
{
    GameObject[] path;//巡逻路径点
    int index = 0;//路径点编号

    /// <summary>
    /// 巡逻模式
    /// </summary>
    /// <param name="fsm"></param>
    public PatrolState(FSMSystem fsm):base(fsm) {
        stateID = StateID.PatrolState;
        path = GameObject.FindGameObjectsWithTag("Path");
       
    }

    /// <summary>
    /// 沿着路径点进行循环移动 巡逻
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public override void Action(GameObject player, GameObject npc)
    {
        if (Vector3.Distance(npc.transform.position,path[index].transform.position)<1) {
            index++;
            index %= path.Length;
        }
        npc.transform.LookAt(path[index].transform);
        npc.transform.Translate(Vector3.forward*3*Time.deltaTime);
    }
    /// <summary>
    /// 切换条件;当玩家与NPC 距离小于3米时,转换为追逐状态
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public override void Reason(GameObject player, GameObject npc)
    {
        if (Vector3.Distance(player.transform.position,npc.transform.position)<3) {
            fsm.PerformTransition(Transiton.SeePlayer);
        }
    }
}

创建脚本:ChaseState.cs 。怪物追逐状态 时的行为和转换条件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ChaseState :FSMState {

    /// <summary>
    /// 追逐状态
    /// </summary>
    /// <param name="fsm"></param>
    public ChaseState(FSMSystem fsm):base(fsm) {
        stateID = StateID.ChaseState;
    }
    /// <summary>
    /// 追逐状态时 npc 面向玩家进行移动
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public override void Action(GameObject player, GameObject npc)
    {
        npc.transform.LookAt(player.transform);
        npc.transform.Translate(Vector3.forward*2*Time.deltaTime);
    }
    /// <summary>
    /// 转换条件为: 当玩家和NPC距离超出5米时,切换为巡逻模式
    /// </summary>
    /// <param name="player"></param>
    /// <param name="npc"></param>
    public override void Reason(GameObject player, GameObject npc)
    {
        if (Vector3.Distance(npc.transform.position,player.transform.position)>=5)
        {
            Debug.Log(Vector3.Distance(npc.transform.position, player.transform.position));
            fsm.PerformTransition(Transiton.GamePause);
        }
    }

   
}

创建脚本:Enemy.cs 怪物实现类,怪物有限状态机初始化和逻辑控制 (脚本挂载 怪物物体上)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour {

    FSMState state;
    FSMSystem fsm;


	// Use this for initialization
	void Start () {
        //添加状态转换条件
        fsm = new FSMSystem();
        state = new IdleState(fsm);
        state.AddTransition(Transiton.NoSeePlayer,StateID.PatrolState);
        fsm.AddState(state);
        state = new PatrolState(fsm);
        state.AddTransition(Transiton.SeePlayer,StateID.ChaseState);
        fsm.AddState(state);
        state = new ChaseState(fsm);
        state.AddTransition(Transiton.GamePause,StateID.IdleState);
        fsm.AddState(state);
	}
	
	// Update is called once per frame
	void Update () {
        print("当前的状态是:" + fsm.CurrentState);
        
        fsm.Update(GameController.player,this.gameObject);//使FSM系统运行起来
	}
}

创建脚本:GameController.cs。游戏管理类 (脚本挂载摄像机上)


#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameController : MonoBehaviour {

    /// <summary>
    /// 游戏玩家
    /// </summary>

    public static GameObject player;
    private GameController() { }
    /// <summary>
    /// 单例模式
    /// </summary>
    private static GameController gameController;

    public static GameController GetGameController { get { return gameController; } }

    private void Awake() {
        gameController = this;
        player = GameObject.Find("Player");
    }
}

创建脚本:Player.cs 控制玩家移动 (脚本挂载到玩家物体(命名为:Player)上)

#endregion
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 键盘控制玩家 
/// </summary>
public class Player : MonoBehaviour {


    Rigidbody rb;
	// Use this for initialization
	void Start () {
        rb= gameObject.GetComponent<Rigidbody>(); 
        if(rb==null){
            gameObject.AddComponent<Rigidbody>();
            rb = gameObject.GetComponent<Rigidbody>();

        }//获取rigidbody组件
    }
	
	// Update is called once per frame
	void Update () {
        
        float BallX = Input.GetAxis("Horizontal");
        float BallY = Input.GetAxis("Vertical");
        rb.AddForce(new Vector3(BallX, 0, BallY) * 5);      //根据键盘的输入,来给物体加入各个方向的力,实现移动

    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值