前言

本文介绍unity常用框架工具,可以类似于GF(GameFrame)给的工具集。

GameFramework解析:开篇 - 知乎

FSM

概念

  • FSM是一种数据结构,它由以下几个部分组成:
  1. 内在的所有状态(必须是有限个)
  2. 输入条件
  3. 状态之间起到连接性作用的转换函数
  • FSM的描述性定义:
    一个有限状态机是一个设备,或是一个模型,具有有限数量的状态。它可以在任何给定时间根据输入进行操作,使得系统从一个状态转换到另一个状态,或者是使一个输出或者一种行为的发生,一个有限状态机在任何瞬间只能处于一种状态。

State 状态基类,定义了基本的Enter,Update,Exit三种状态行为,通常在这三种状态行为的方法里会写一些逻辑。每个State都会有StateID(状态id,可以是枚举等),FSMControl(控制该状态的状态控制器的引用),Check方法(用来进行状态判断,并返回StateID,通过FSMControl驱动)

实现

有限状态机的实现我们可以把他分成3部分,上图中从上到下每一行就是一部分,分别是状态部分(FsmState),状态机部分(FsmBase、IFsm、Fsm)以及状态机管理器部分(IFsmManager、FsmManager)。

状态类FsmState

  • FsmState为有限状态机状态基类,所有用于有限状态机的状态都需要继承自此类,泛型参数T需要传入状态持有者类型。
  • OnInit、OnEnter、OnUpdate、OnLeave、OnDestroy为状态的生命周期方法,其中OnInit和OnDestroy分别在状态创建和销毁时调用,只会调用一次,而OnEnter、OnLeave分别在进入状态和离开状态时调用,可能会调用多次,而OnUpdate则是在进入该状态后每帧调用。
  • ChangeState用于切换到下一状态。ChangeState实际是用该方法传入的FSM对象调用FSM类里的ChangeState方法,正式执行状态切换逻辑。

状态机类Fsm

  • Fsm对象通过Create方法创建,需要传入状态机拥有者类型、状态机名字、状态列表3个参数,Create方法为静态方法,由FsmManager调用。参数状态列表将会保存在字段m_States中,并调用所有状态的OnInit方法。
  • 状态机通过Start方法启动,传入初始状态类型作为参数,方法内部会调用该状态的OnEnter。
  • Update方法会每帧调用当前状态的Update方法,且会计算当前状态机进行了的累计时间,可通过CurrentStateTime获取。
  • GetAllState和GetState方法可以获取注册进这个状态机的状态对象。
  • 状态机内通常不同状态之间是需要有数据交互的,GetData,SetData,HasData,RemoveData这四个接口则提供了不同状态间数据交互的功能,分别对应获取数据、设置数据、是否有数据、移除数据,数据以key-value形式存在于字典m_Datas中。
  • Shutdown方法会回收FSM对象,此方法由FsmManager的DestroyFsm方法调用。

状态机管理器FsmManager

  • 外部创建新的状态机统一通过FsmManager的CreateFsm接口创建,参数同FSM类中的静态方法Create,此方法会调用Fsm类的Create创建Fsm对象,然后以key-value的形式储存在字段m_Fsms中,注意m_Fsms是Dictionary类型,以TypeNamePair为Key,TypeNamePair对象是结合状态机持有者类型和状态机名字字符串类型参数组成,为了保证Key的唯一性,对于同样类型的而不同实例的持有者,应该传入不同的状态机名字。
  • GetFsm、GetAllFsm、HasFsm,向外部提供某个状态机的查询、获取,需要传入持有者类型和状态机名字两个参数。
  • DestroyFsm可销毁特定状态机,会调用对应Fsm对象的Shutdown方法,并在FsmManager的m_Fsms字段中移除该状态机。

ObjPool

1. 什么是对象池

  • 对象池是预创建对象集合的技术方案
  • 核心机制:
    • 获取对象时优先从池中获取
    • 无可用对象时创建新对象
    • 销毁时标记为未激活状态并回收
  • 优势:避免大量重复的对象创建/销毁开销

2. 对象池解决的问题

2.1 核心问题

  • 减少高频创建/销毁对象的性能损耗
  • 典型案例:
    • 线程池(线程创建成本高)
    • 网络连接池(TCP三次握手开销)
    • 游戏对象池(高频生成子弹/特效)

2.2 适用场景

  • 对象初始化成本高的资源
  • 需要快速响应的实时系统
  • 高频创建/销毁对象的场景

3. 对象池的优缺点

3.1 优点 ✅

  1. 性能提升
    • 缩短对象获取响应时间
    • 减少系统初始化开销
  2. 资源优化
    • 降低GC压力(尤其对GC敏感语言)
    • 减少内存碎片

3.2 缺点 ❌

  1. 脏对象问题
    • 残留数据导致内存泄漏
    • 影响后续使用(需清理机制)
  2. 资源占用
    • 长期维持闲置对象
    • 内存空间换时间取舍
  3. 复杂度增加
    • 需要管理对象生命周期
    • 线程安全问题(多线程环境)

4. 对象池核心特征

一般来说,对象池有下面几个特征:
(1)对象池中有一定数量已经创建好的对象
(2)对象池向用户提供获取对象的接口,当用户需要新的对象时,便可通过调用此接口获取新的对象。如果对象池中有事先创建好的对象时,就直接返回给用 户;如果没有了,对象池还可以创建新的对象加入其中,然后返回给用户
(3)对象池向用户提供归还对象的接口,当用户不再使用某对象时,便可通过此接口把该对象归还给对象池

5. 池的大小选择

通常情况下,我们需要控制对象池的大小如果对象池没有限制,可能导致对象池持有过多的闲置对象,增加内存的占用。如果对象池闲置过小,没有可用的对象时,会造成之前对象池无可用的对象时,再次请求出现的问题。

对象池的大小选取应该结合具体的使用场景,结合数据(触发池中无可用对象的频率)分析来确定。现在Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计,并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍;由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈;很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高。
空间换时间的折中,本质上,对象池属于空间换时间的折中。它通过缓存初始化好的对象来提升调用者请求对象的响应速度。除此之外,折中(tradeoff)是软件开发中的一个重要的概念,会贯穿整个软件开发过程中。

Behavir Tree