FSM状态机

---------框架
先声明两个枚举类型 一个存状态 一个存转换条件,
声明一个状态的类 里面创建一个字典进行储存条件和相应的状态 
这个类里面有添加方法,删除方法,还有有进入和出去的虚方法,return状态的方法,还有是否转换状态
的抽象方法

声明一个管理状态机的类 里面实例化一下状态类,和状态,里面有添加状态和删除状态的方法,
还有执行过度的方法,传入的条件进行判断,如果状态类里面有条件的话,返回状态,然后进行遍历,
当在状态类找到状态的时候,执行进入前的方法,然后把当前状态切换为传入条件返回的那个状态,再执行



/*
如何使用:  
1.  为有限状态系统的转换和状态加上标签  
在相应的枚举中。  
 
2.  编写从FSMState继承的新类,并用对(transition-state)填充每个类。  
当FSMSystem处于S1, a状态时,这些对表示状态S2  
跃迁T被触发,状态S1从它跃迁到S2。 记住这是一个确定性的FSM。  
一个转变不可能导致两个不同的状态。  
 
方法Reason用于确定应该触发哪个转换。  
您可以编写代码在另一个地方触发转换,如果您  
感觉更适合你的项目。  
 
Method Act拥有执行NPC在此状态下应该执行的动作的代码。  
您可以在另一个地方为操作编写代码,如果您  
感觉更适合你的项目。  
 
3. 创建一个FSMSystem类的实例,并向它添加状态。  
 
4. 调用理由和行动(或任何你用来触发转换和制作npc的方法)  
从你的Update或FixedUpdate方法。  
 
从Unity引擎的异步转换,如OnTriggerEnter, SendMessage,也可以使用,  
当事件发生时。从你的FSMSystem实例中调用方法PerformTransition就可以了  
  
  
①  
/// 这个类表示有限状态系统中的状态。  
/// 每个状态都有一个显示成对(过渡状态)的Dictionary  
/// 如果在此状态下触发一个过渡,则FSM应该处于哪个状态就是当前状态。  
/// 方法Reason用于确定应该触发哪个转换。  
/// Method Act有执行NPC在这个状态下应该执行的动作的代码。


②
/// FSMSystem类表示有限状态机类。  
/// 它有一个带有NPC的状态的列表,以及添加、删除状态和改变机器当前状态的方法。  
/// 这是唯一的方法进行FSM的状态转换
/// 不要直接改变 CurrentState  

③
/// 这个方法在FSM中放置新的状态,
/// 或者如果状态已经在List中,则打印一个ERROR消息。
/// 第一个加入的状态也是初始状态。


④
/// 该方法尝试根据当前状态和通过的过渡来改变FSM所处的状态。
/// 如果当前状态没有传递的转换目标状态,则打印ERROR消息。
*/




using System.Collections.Generic;
using UnityEngine;


// 在这个枚举中放置过渡的标签。
public enum Transition
{
    NullTransition = 0, // 使用此转换表示系统中不存在的转换  

    //看见玩家
    SawPlayer,

    //丢失玩家
    LostPlayer,
}


// 在这个枚举中放置状态的标签。
public enum StateId
{
    NullStateId = 0, // 此ID表示不存在的状态   

    //追玩家
    Chasing,

    //自动寻路
    AutoPath,
}


// ①这个类表示有限状态系统中的状态  
public abstract class FSMState
{
    //设置一个成对的字典
    protected Dictionary<Transition, StateId> map;
    protected StateId stateID;

    protected FSMState()
    {
        map = new Dictionary<Transition, StateId>();
    }

    //状态
    public StateId ID
    {
        get { return stateID; }
    }

    //添加过度
    public void AddTransition(Transition trans, StateId id)
    {
        // 检查是否有无效参数
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition 在实际转换中是不允许的");
            return;
        }

        if (id == StateId.NullStateId)
        {
            Debug.LogError("FSMState ERROR: NullStateID 不允许被真实的ID使用");
            return;
        }


        // 检测当前的过度是否已经存在在字典中
        if (map.ContainsKey(trans))
        {
            Debug.LogError("FSMState ERROR: 状态 " + stateID.ToString() + " 已经有 "
                           + trans.ToString() + "不能分配给另一个状态");
            return;
        }

        //如果不存在就添加到字典中
        map.Add(trans, id);
    }

    // 删除过度
    public void DeleteTransition(Transition trans)
    {
        // 检查参数是否无效
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSMState ERROR: NullTransition 不允许删除");
            return;
        }

        // 在删除之前检测是否存在在字典中
        if (map.ContainsKey(trans))
        {
            map.Remove(trans);
            return;
        }

        Debug.LogError("FSMState ERROR: 过度 " + trans.ToString() + " 交给 " + stateID.ToString() +
                       " 不在状态过度联系的名单上");
    }


    // 该方法返回FSM应该是的新状态  
    public StateId GetOutputState(Transition trans)
    {
        // 检查是否有这个过度
        if (map.ContainsKey(trans))
        {
            return map[trans];
        }

        return StateId.NullStateId;
    }


    ///进入状态
    public virtual void DoBeforeEntering()
    {
    }


    // 离开状态  
    public virtual void DoBeforeLeaving()
    {
    }


    // 该方法决定是否将状态转移到列表中的另一个状态,NPC是这个类控制的对象的引用
    public abstract void Reason(GameObject player, GameObject npc);


    // 这个方法控制游戏世界中NPC的行为,NPC所做的每一个动作、移动或交流都应该放在这里
    public abstract void Act(GameObject player, GameObject npc);
}


// 有限状态机类。  
public class FsmSystem
{
    private List<FSMState> states;

    private StateId currentStateID;
    private FSMState currentState;

    public StateId CurrentStateID
    {
        get { return currentStateID; }
    }

    public FSMState CurrentState
    {
        get { return currentState; }
    }

    public FsmSystem()
    {
        states = new List<FSMState>();
    }


    // ③添加状态
    public void AddState(FSMState s)
    {
        // 添加前检查空引用
        if (s == null)
        {
            Debug.LogError("FSM ERROR: 不允许空引用");
        }

        // 初始状态
        if (states.Count == 0)
        {
            states.Add(s);
            currentState = s;
            currentStateID = s.ID;
            return;
        }

        // 如果状态不在列表中,则将其添加到列表中
        foreach (FSMState state in states)
        {
            if (s != null && state.ID == s.ID)
            {
                Debug.LogError("FSM ERROR: 不可能添加状态 " + s.ID.ToString() +
                               " 因为这个状态已经添加");
                return;
            }
        }

        states.Add(s);
    }


    // 删除状态
    public void DeleteState(StateId id)
    {
        // 检查状态是否为空
        if (id == StateId.NullStateId)
        {
            Debug.LogError("FSM ERROR: NullStateID 不允许用于真实状态");
            return;
        }

        // 如果它在里面,搜索列表并删除状态
        foreach (FSMState state in states)
        {
            if (state.ID == id)
            {
                states.Remove(state);
                return;
            }
        }

        Debug.LogError("FSM ERROR: 不可能删除状态 " + id.ToString() +
                       "不在状态列表里");
    }


    // ④执行过度  
    public void PerformTransition(Transition trans)
    {
        // 检查当前过度是否被允许
        if (trans == Transition.NullTransition)
        {
            Debug.LogError("FSM ERROR: NullTransition 不允许进行过渡");
            return;
        }

        // 检查当前状态是否将转换作为参数传递
        StateId id = currentState.GetOutputState(trans);
        if (id == StateId.NullStateId)
        {
            Debug.LogError("FSM ERROR: 状态 " + currentStateID.ToString() +
                           " 没有转换的目标状态 " + trans.ToString());
            return;
        }

        // 更新当前状态信息 
        currentStateID = id;
        foreach (FSMState state in states)
        {
            if (state.ID == currentStateID)
            {
                // 设置新状态前做处理
                currentState.DoBeforeLeaving();

                currentState = state;

                // 设置新状态后做处理
                currentState.DoBeforeEntering();
                break;
            }
        }
    }
--例子
using UnityEngine;


[RequireComponent(typeof(Rigidbody))]
public class NPCControl : MonoBehaviour
{
    public GameObject player;
    public Transform[] path;
    public FsmSystem fsm;


    public void Start()
    {
        MakeFSM();
    }

    public void FixedUpdate()
    {
        fsm.CurrentState.Reason(player, gameObject);
        fsm.CurrentState.Act(player, gameObject);
    }

    //NPC有两种状态:FollowPath和ChasePlayer  
    //如果它在第一个状态,并且SawPlayer转换被触发,它会变成ChasePlayer  
    //如果它在ChasePlayerState和LostPlayer转换被触发,它返回到FollowPath  
    private void MakeFSM()
    {
        ChasePlayerState chase = new ChasePlayerState();
        chase.AddTransition(Transition.LostPlayer, StateId.AutoPath);
        FollowPathState follow = new FollowPathState(path);
        follow.AddTransition(Transition.SawPlayer, StateId.Chasing);


        fsm = new FsmSystem();
        fsm.AddState(chase);
        fsm.AddState(follow);
    }
}

//巡逻
public class FollowPathState : FSMState
{
    private int currentWayPoint;
    private Transform[] waypoints;

    public FollowPathState(Transform[] wp)
    {
        waypoints = wp;
        currentWayPoint = 0;
        stateID = StateId.AutoPath;
    }

    public override void DoBeforeEntering()
    {
        Debug.Log("进入前做了一些事情...");
    }

    public override void DoBeforeLeaving()
    {
        Debug.Log("离开前做了一些事情...");
    }

    public override void Reason(GameObject player, GameObject npc)
    {
        // 如果玩家从NPC面前经过时距离不到15米
        RaycastHit hit;
        if (Physics.Raycast(npc.transform.position, npc.transform.forward, out hit, 15F))
        {
            if (hit.transform.gameObject.tag == "Player")
                npc.GetComponent<NPCControl>().fsm.PerformTransition(Transition.SawPlayer);
        }
    }

    public override void Act(GameObject player, GameObject npc)
    {
        Debug.Log("12");
        // 沿着路标的路径走
        // 找出当前航路点的方向
        Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
        Vector3 moveDir = waypoints[currentWayPoint].position - npc.transform.position;

        if (moveDir.magnitude < 1)
        {
            currentWayPoint++;
            if (currentWayPoint >= waypoints.Length)
            {
                currentWayPoint = 0;
            }
        }
        else
        {
            vel = moveDir.normalized * 10;

            // 向目标点旋转
            npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
                Quaternion.LookRotation(moveDir),
                5 * Time.deltaTime);
            npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);
        }

        // 应用速度
        npc.GetComponent<Rigidbody>().velocity = vel / 5;
    }
}

/// <summary>
/// 追
/// </summary>
public class ChasePlayerState : FSMState
{
    public ChasePlayerState()
    {
        stateID = StateId.Chasing;
    }


    public override void Reason(GameObject player, GameObject npc)
    {
        // 如果玩家已经离开NPC 30米远,发射LostPlayer过渡
        if (Vector3.Distance(npc.transform.position, player.transform.position) >= 2)
            npc.GetComponent<NPCControl>().fsm.PerformTransition(Transition.LostPlayer);
    }

    public override void Act(GameObject player, GameObject npc)
    {
        // 沿着路标的路径走
        // 找到玩家的方向        
        Vector3 vel = npc.GetComponent<Rigidbody>().velocity;
        Vector3 moveDir = player.transform.position - npc.transform.position;

        // 向航路点旋转
        npc.transform.rotation = Quaternion.Slerp(npc.transform.rotation,
            Quaternion.LookRotation(moveDir),
            5 * Time.deltaTime);
        npc.transform.eulerAngles = new Vector3(0, npc.transform.eulerAngles.y, 0);

        vel = moveDir.normalized * 10;

        // 应用新的速度
        npc.GetComponent<Rigidbody>().velocity = vel;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的FSM状态机示例代码(使用Python编写): ```python class State: def __init__(self, name): self.name = name def on_enter(self): pass def on_exit(self): pass def handle_event(self, event): pass class StateMachine: def __init__(self, initial_state): self.current_state = initial_state def transition_to(self, new_state): self.current_state.on_exit() self.current_state = new_state self.current_state.on_enter() def handle_event(self, event): self.current_state.handle_event(event) # 示例状态 class IdleState(State): def on_enter(self): print("进入空闲状态") def on_exit(self): print("退出空闲状态") def handle_event(self, event): if event == "start": print("开始任务") return "running" return self.name class RunningState(State): def on_enter(self): print("进入运行状态") def on_exit(self): print("退出运行状态") def handle_event(self, event): if event == "stop": print("停止任务") return "idle" return self.name # 创建状态机并设置初始状态为IdleState state_machine = StateMachine(IdleState()) # 处理事件 state_machine.handle_event("start") # 输出: 进入空闲状态 # 开始任务 state_machine.transition_to(RunningState()) # 输出: 退出空闲状态 # 进入运行状态 state_machine.handle_event("stop") # 输出: 停止任务 # 退出运行状态 # 进入空闲状态 ``` 以上示例代码展示了一个简单的状态机,其中包括两个状态,即"空闲"和"运行"。状态之间的切换由事件触发,例如"start"事件将从"空闲"状态切换到"运行"状态,"stop"事件将从"运行"状态切换回"空闲"状态。每个状态都有进入(on_enter)和退出(on_exit)方法,在切换到新状态时会调用这些方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值