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 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity框架之写一个简单的FSM状态机 -> 正文阅读

[游戏开发]Unity框架之写一个简单的FSM状态机

有限状态机的定义

表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型 顾名思义 就是所有的状态都会在状态机的集合里,方便管理。在启动一个FSM时,首先必须将FSM置于“起始状态”,然后输入一系列操作,最终,FSM会到达“结束状态”

实现一个简单的状态机

///定义一个抽象的类,具体的函数在子类中实现,是状态机的基类
public abstruct class FSMState
{
  protected string name;//状态的名字
  public string Name{get=>name;}//name接口 对外界调用
  protected FSMSystem fsm;
  protracted Dictionary<Transaction , string>statemap=new Dictionary<Transaction , string>();//存放所有状态的字典 Transaction是转换条件 等一会实现 string 是名字
  ///添加转换条件 首先判断转换条件是不是空 如果为空不执行 在判断字典中是否有这个转换条件 如果没有再加入
  public void AddTransaction(Transaction transaction,string name)
  {
    if(transaction==Transaction.NULL||name=="") return;
    if(!map.ContainKey(transaction))map.add(transaction,name);
  }
  //删除转换条件 思路和插入一样
  public void DestoryTransaction(Transaction transaction)
    {
        if (transaction == Transaction.NULL) return;
        if (map.ContainsKey(transaction)) map.Remove(transaction);
    }
    ///获取当前状态 如果字典中有该状态就返回他的名字 否则返回空
    public string GetOutState(Transaction transaction)
    {
      if(map.ContainsKey(transaction) return statemap[transaction];
      else return"";
    }
    ///状态进入之前做
    public virtual void DoBeforEntering(){}
      ///状态离开之前做
    public virtual void DoBeforLeaving(){}
    ///检测状态转换
    public abstract void Reason();
    ///控制该状态的行为
    public abstract void Act();
}

补充 abstract和virtual的区别(面试经常问)

共同点: 两个都是为了让子类对付类重新定义 覆盖父类
区别:1 在父类中virtual修饰的方法必须有实现 而abstract修饰的方法不能有实现 必须用“;”结束
2 virtual在子类中可以被重写 abstruct必须被重写
3 如果类中有一个函数被abstract修饰那么类名也必须用abstract修饰
4 abstract类不能被实例化 必须依靠子类

言归正传 现在写管理状态的类

 ///使用一个List列表把状态全部管理
 public class FSMSystem 
{
  private List<FSMState> stateList ;
  private FSMState nowState;///当前的状态
  public FSMState NowState { get => nowState;   }
  public FSMSystem()
  {
    stateList=new List<FSMState>();
  }
  ///添加状态
  ///如果没有就跳出 如果有则判断list是不是为空 如果为空就把第一个进入的状态设为默认的状态
  ///如果list不为空那么就判断list里面有没有这个状态 如果没有则加入
  public void AddState(FSMState state)
  {
   if(state==null)return;
   else if(stateList.Count==0)
   {
     stateList.Add(state);
     nowstate=state;
     return;
   }
   else if(!stateList.Contains(state))
     stateList.Add(state);
  }
  ///删除状态
   public void DestoryState(string stateName)
    {
        if (stateName == "") return;
        foreach(FSMState state in stateList)
        {
            if(state.Name==stateName)
            {
                stateList.Remove(state);
                break;
            }
        }
    }
    ///转换状态
    public void DoTransaction (Transaction transaction)
    {
      if(transaction==Transaction.NULL)
      return;
      string newstateName=nowState.GetOutState(transaction);
      if(newstateName=="")return;
      else
      {
        foreach(FSMState state in stateList)
        {
          if(state.Name==newstateName)
          {
            nowState.DoBeforLeaving();
            nowstate=state;
            nowState.DoBeforEntering();
            retrun;
          }
        }
      }
    }
}

实现Transaction (状态)

public enum Transaction
{
  NULL,
  MOVE,
  IDEL
}

这样框架就完整实现了 现在我来写个小Demo来展示这个框架的用法

MoveState(移动状态)

 public class MoveState:FSMState()
 {
   Transform movetrans;
   public MoveState(Transform target,FSMSystem f)
   {
     this.name="Move";
     this.movetrans=target;
     this.fsm=f;
   }
   ///重写这个状态的要做的事情
   public override void Act()
   {
     Debug.Log("移动了!");
   }
   ///切换状态
    public override void Reason()
    {
        if (Input.GetKeyDown(KeyCode.W))
            fsm.DoTransaction(Transaction.TO_IDEL);
    }
 }

静止状态

public class idelState :FSMState
{
  public IdelState(FSMSystem f)
  {
    this.name="Idel";
    this.fsm=f;
  }
  public override void Act()
  {
    Debug.Log("静止");
  }
  public override void Reason()
  {
         if(Input.GetKeyDown(KeyCode.Space))
          fsm.DoTransaction(Transaction.TO_MOVE);
  }
}

玩家或敌人代码

public class SleepControl:MonoBehaviour
{
  private FSMSystem fsm;
  public Transform target;
  ///进行初始化操作
  private void MakeFSM()
  {
    fsm=new FSMSystem();
    IdelState idel=new IdelState(fsm);
    idel.AddTransaction(Transaction.MOVE,"Move");
    MoveState move=new MoveState(target,fsm);
     move.AddTransaction(Transaction.IDEL,"Idel");
     fsm.AddState(idel);
     fsm.AddState(move);
  }
  private void Awake()
  {
    MakeFSM();
  }
  private void Update()
  {
    fsm.nowState.Act();
    fsm.nowState.Reason();
  }
}

这样就可以按空格和W切换不同的状态了,如果要用自己想要在某个状态要做的事就可以直接在Act函数中调用你想要的操作的函数接口即可

  游戏开发 最新文章
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-08-21 15:47:34  更:2021-08-21 15:48:33 
 
开发: 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年5日历 -2024/5/4 6:46:48-

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