IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> FSM状态机 -> 正文阅读

[游戏开发]FSM状态机

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

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



/*
如何使用:  
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;
    }

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-10-17 12:17:25  更:2021-10-17 12:18:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 0:54:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码