有限状态机的实现

有限状态机的实现1.概述         在游戏中存在各种的“状态”,不仅是角色的行为状态,还有例如场景的开始、加载、游戏中等状态,此次采用一个小案例实现有限状态机——一个NPC一个角色player,当角色player不在NPC视野内时NPC在固定的四个点之间巡逻,当player在NPC视野内时,NPC会追逐player,丢失跟踪后继续巡逻,player的移动采用在scene中手动移动。2.FSMState…

大家好,欢迎来到IT知识分享网。

1. 概述  

        在游戏中存在各种的“状态”,不仅是角色的行为状态,还有例如场景的开始、加载、游戏中等状态,此次采用一个小案例实现有限状态机——一个NPC一个角色player,当角色player不在NPC视野内时NPC在固定的四个点之间巡逻,当player在NPC视野内时,NPC会追逐player,丢失跟踪后继续巡逻,player的移动采用在scene中手动移动。

2. FSMState.cs

        FSMState为定义状态的基类,但也将状态转换条件和状态ID枚举定义放在了此类中,Transition中存了所有的状态转换条件,StateID中定义了所有的状态。状态定了一些状态的基本信息,有状态ID以及储存转换条件和状态ID的字典,持有一个状态管理的对象……….

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

/// <summary>
/// 状态转换的条件
/// </summary>
public enum Transition
{
    NullTransition=0,
    SawPlayer,//看到主角
    LostPlayer//看不到主角
}

/// <summary>
/// 状态ID,每个状态唯一表示,不重复,用于区分不同状态
/// </summary>
public enum StateID
{
    NullStateID=0,
    Patrol,//巡逻
    Chase//追逐主角
}

public abstract class FSMState{
    protected StateID stateID;
    public StateID ID
    {
        get { return stateID; }
    }

    protected Dictionary<Transition, StateID> map = new Dictionary<Transition, StateID>();

    public FSMSystem fsm;

    public void AddTransition(Transition tran,StateID id)
    {
        if(tran==Transition.NullTransition||id==StateID.NullStateID)
        {
            Debug.LogError("添加的状态转换条件或状态Id不能为空!");
            return;
        }
        if(map.ContainsKey(tran))
        {
            Debug.LogError("状态转换条件 " + id + " 已经存在" + tran);
            return;
        }

        map.Add(tran, id);
    }

    public void DeleteTransition(Transition tran)
    {
        if(map.ContainsKey(tran)==false)
        {
            Debug.LogWarning("想要删除的状态转换条件 " + tran + " 不存在!");
            return;
        }

        map.Remove(tran);
    }

    /// <summary>
    /// 根据传过来的转换条件,判断是否可以转换,然后返回状态ID
    /// </summary>
    /// <param name="tran"></param>
    /// <returns></returns>
    public StateID GetOutputState(Transition tran)
    {
        if(map.ContainsKey(tran))
        {
            return map[tran];
        }

        return StateID.NullStateID;
    }

    //进入当前状态前需做的
    public virtual void DoBeforeEntering() { }
    public virtual void DoBeforeLeaving() {  }

    public abstract void DoUpdate();//在当前状态机出于当前状态的时候,会一直调用
} 

3. ChaseState.cs

        定义的追逐状态。

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

public class ChaseState : FSMState {
    private GameObject npc;
    private GameObject player;

    private Rigidbody npcRig;

    public ChaseState(GameObject npc,GameObject player)
    {
        stateID = StateID.Chase;

        this.player = player;
        this.npc = npc;

        npcRig = npc.GetComponent<Rigidbody>();
    }

    public override void DoBeforeEntering()
    {
        Debug.Log("进入 "+ID+" 状态");
    }

    public override void DoUpdate()
    {
        ChaseMove();
        CheckTransition();
    }

    public void CheckTransition()
    {
        if(Vector3.Distance(npc.transform.position,player.transform.position)>2)
        {
            fsm.PerformTransition(Transition.LostPlayer);
        }
    }

    public void ChaseMove()
    {
        npcRig.velocity = npc.transform.forward * 3;

        Transform targetTrans = player.transform;

        Vector3 targetPosition = targetTrans.position;

        targetPosition.y = npc.transform.position.y;

        npc.transform.LookAt(targetPosition);
    }
}

4. PatrolState.cs

        定义的巡逻状态。

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

public class PatrolState : FSMState {

    private int targetWaypoint;
    private Transform[] wayPoints;//巡逻的路径点
    private GameObject npc;

    private Rigidbody npcRig;

    private GameObject player;
    public PatrolState(Transform[] wp,GameObject npc,GameObject player)
    {
        stateID = StateID.Patrol;

        wayPoints = wp;

        this.npc = npc;
        targetWaypoint = 0;
        npcRig = npc.GetComponent<Rigidbody>();

        this.player = player;
    }

    public override void DoBeforeEntering()
    {
        Debug.Log("进入 " + ID + " 状态");
    }

    public override void DoUpdate()
    {
        PatrolMove();

        CheckTransition();
    }

    private void PatrolMove()
    {
        npcRig.velocity = npc.transform.forward * 3;

        Transform targetTrans = wayPoints[targetWaypoint];
        Vector3 targetPosition = targetTrans.position;

        targetPosition.y = npc.transform.position.y;

        npc.transform.LookAt(targetPosition);

        if (Vector3.Distance(npc.transform.position, targetPosition) < 1)
        {
            targetWaypoint++;
            targetWaypoint = targetWaypoint % wayPoints.Length;//使targetWaypoint循环
        }
    }

    private void CheckTransition()
    {
        if(Vector3.Distance(player.transform.position,npc.transform.position)<2)
        {
            fsm.PerformTransition(Transition.SawPlayer);
        }
    }
} 

5. FSMSystem.cs

        对所有状态的管理,用字典储存了所有的状态,持有一个记录当前状态的变量。

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


/// <summary>
/// 有限状态机管理类,有限状态机系统类
/// </summary>
public class FSMSystem {

    //当前状态机下面所有状态
    private Dictionary<StateID, FSMState> statesDict;

    //状态机当前所处的状态
    private FSMState currentState;
    public FSMState CurrentState
    {
        get { return currentState; }
    }

    public FSMSystem()
    {
        statesDict = new Dictionary<StateID, FSMState>();
    }

    /// <summary>
    /// 向状态机中添加状态
    /// </summary>
    /// <param name="state"></param>
    public void AddState(FSMState state)
    {
        if(state==null)
        {
            Debug.LogError("要添加的状态为空!");
            return;
        }
        if(statesDict.ContainsKey(state.ID))
        {
            Debug.LogError("要添加的状态已经存在!StateID: "+state.ID);
            return;
        }

        state.fsm = this;
        statesDict.Add(state.ID, state);
    }

    /// <summary>
    /// 从状态机中删除状态
    /// </summary>
    /// <param name="state"></param>
    public void DeleteState(FSMState state)
    {
        if (state == null)
        {
            Debug.LogError("要删除的状态为空!");
            return;
        }
        if (statesDict.ContainsKey(state.ID)==false)
        {
            Debug.LogError("要删除的 "+state+" 状态不存在!");
            return;
        }

        statesDict.Remove(state.ID);
    }

    /// <summary>
    /// 根据所传的转换条件,控制状态之间的转换
    /// </summary>
    /// <param name="tran"></param>
    public void PerformTransition(Transition tran)
    {
        if(tran==Transition.NullTransition)
        {
            Debug.LogError("空的转换条件不能转换!");
            return;
        }

        StateID id = currentState.GetOutputState(tran);
        if(id==StateID.NullStateID)
        {
            Debug.Log("没有符合条件的转换!");
            return;
        }

        FSMState state;
        statesDict.TryGetValue(id,out state);
        currentState.DoBeforeLeaving();
        currentState = state;
        currentState.DoBeforeEntering();
    }

    /// <summary>
    /// 设置默认状态,启动状态机
    /// </summary>
    /// <param name="id"></param>
    public void Start(StateID id)
    {
        FSMState state;
        bool isGet = statesDict.TryGetValue(id, out state);

        if(isGet)
        {
            state.DoBeforeEntering();
            currentState = state;
        }
        else
        {
            Debug.LogError("状态 " + id + " 不存在");
        }
    }
}

6. NPCController.cs

        挂载于NPC上的脚本,且需要刚体组件,并将player的标签设置为Player,用于查找到player;且赋值四个路标位置。

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

public class NPCController : MonoBehaviour {

    private FSMSystem fsmSystem;

    private GameObject player;

    public Transform[] wayPoints;
	void Start () {
        player = GameObject.FindGameObjectWithTag("Player");
        InitFSM();

        
	}
	
	/// <summary>
    /// 初始化状态机
    /// </summary>
	void InitFSM () {
        fsmSystem = new FSMSystem();

        PatrolState patrolState = new PatrolState(wayPoints,this.gameObject,player);
        patrolState.AddTransition(Transition.SawPlayer, StateID.Chase);

        ChaseState chaseState = new ChaseState(this.gameObject,player);
        chaseState.AddTransition(Transition.LostPlayer, StateID.Patrol);

        fsmSystem.AddState(patrolState);
        fsmSystem.AddState(chaseState);

        fsmSystem.Start(StateID.Patrol);
	}

    private void Update()
    {
        fsmSystem.CurrentState.DoUpdate();
    }
}

有限状态机的实现

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/14735.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信