网站内容设置,灯塔网站制作公司,wordpress的代码逻辑,免费网站制作平台下载文章目录 前言预输入Animancer的InputBuffer#xff1a;在UnityHFSM中实现InputBuffer#xff1a; 打断机制 前言
参考Animancer在状态机中的InputBuffer#xff0c;在UnityHFSM中实现类似的InputBuffer机制#xff0c;同时扩展一个状态打断机制
插件介绍#xff1a;
A… 文章目录 前言预输入Animancer的InputBuffer在UnityHFSM中实现InputBuffer 打断机制 前言
参考Animancer在状态机中的InputBuffer在UnityHFSM中实现类似的InputBuffer机制同时扩展一个状态打断机制
插件介绍
AnimancerUnity的动画插件易于修改和扩展。
UnityHFSMGithub上的一个开源分层状态机项目 基于继承的方法使用UnityHFSM
UnityHFSM有非常方便的使用方法可以在不创建新类的情况下创建状态和状态机定义转换条件等但如果状态逻辑比较复杂并且有统一的父级行为则建议使用类继承的方式定义状态和状态机类。
本文是笔者在做一个自己的ARPG小项目时做出来的思路仅供参考。 预输入
Animancer的InputBuffer
Animancer的InputBuffer主要处理流程如下
在构造函数中绑定一个状态机 外部调用Buffer函数传入期望的目标状态和超时时长 InputBuffer在Update函数中轮询是否能够转换到目标状态一旦转换成功即立刻结束轮询 超时后也会结束轮询
注意点
Animancer接收的是状态机接口而不是具体的状态机类。但由于需要继承来扩展UntiyHFSM的状态机和状态行为的需要我在实现时会传入的是类而非接口。 Animancer的InputBuffer并未继承自Monobehavior并且没有使用任何UnityEngine的东西即Update需要在其他Mono类中调用Time.deltaTime也要靠外部传入方便起见我实现时直接使用Time.deltaTime。
在UnityHFSM中实现InputBuffer
Animancer的状态机有TryResetState函数并且会返回是否成功UntiyHFSM中无此函数故使用StateMachine类的StateChanged来触发事件并与期望的目标状态进行比对一致则终止轮询。
Animancer直接使用TryResetState函数来改变状态状态是否允许进入的条件也写在状态中UnityHFSM依赖于Transition定义两个指定状态之间的转换虽然也有RequestStateChange来强制转换到某个状态但这样做并不优雅并且可能出错。由于这个操作的多样性和复杂性由外界传入Action来决定轮询时该进行的操作。大多数时候使用UnityHFSM的StateMachine的Trigger函数来转换状态
以下是代码CharacterStateMachineBase是我扩展的角色状态机基类ECharacterState是角色状态的枚举值
public class InputBuffer
{public bool IsActive Action ! null;public float TimeOut;public Action Action;public CharacterStateMachineBase StateMachine;public ECharacterState TargetState;public InputBuffer(CharacterStateMachineBase stateMachine){StateMachine stateMachine;StateMachine.StateChanged OnStateChanged;}~InputBuffer(){StateMachine.StateChanged - OnStateChanged;}public void Buffer(Action action, ECharacterState targetState, float timeOut){Action action;TargetState targetState;TimeOut timeOut;}public void Update(){if (IsActive){Action();TimeOut - Time.deltaTime;if (TimeOut 0)Clear();}}public virtual void Clear(){Action null;TimeOut default;}public void OnStateChanged(UnityHFSM.StateBaseECharacterState state){if (IsActive state.name TargetState){Clear();}}
}在外部调用时如下
//CharacterBrain.cs//控制的玩家
public Character ControlledCharacter;//攻击缓冲
public float AttackTimeout 1f;
InputBuffer AttackBuffer;//绑定操作
void Start()
{GameInput.Instance.onAttack () {AttackBuffer.Buffer(() ControlledCharacter.Attack(), ECharacterState.Attack, AttackTimeout);};
}//轮询
void Update()
{AttackBuffer.Update();
}Character的Attack函数实际上就是在调用角色状态机的Trigger函数如下
//Character.cspublic void Attack()
{if (Equipment.CurrentWeapon null)return;StateMachine.Trigger(Attack);
}打断机制
我把打断机制写在了状态机中作为一种扩展行为在UnityHFSM中一个State有一个needsExitTime字段该字段为true时除非强制转换否则无法退出本状态配合Animancer的动画事件OnEnd可以很好的让动画来控制状态的转换我这样做是希望角色的各种动作更加真实让逻辑和表现更容易统一。
为此我们只需要新增一个叫做CanExitBeforeEnd的bool变量即可表示本状态是否可以被打断。于此同时实际上完全没有必要为每个状态新建一个CanExitBeforeEnd变量将该变量放在状态机类中供每个状态变更供外界访问即可。
现在我们只使用CanExitBeforeEnd表示状态希望被改变但没有直接改变的逻辑假设我们当前使用Trigger的方法进行状态变更则我们只需要在每次Trigger前查询一次当前状态是否需要变更需要变更且状态的needsExitTime为true则将其设置为false再执行Trigger操作即可。
StateMachine的Trigger操作并不是虚函数只需要在源码中为其添加virtual关键字即可然后在扩展的CharacterStateMachineBase中重写Trigger逻辑代码如下
public class CharacterStateMachineData
{public bool CanExitBeforeEnd;
}public class CharacterStateMachineBase : StateMachineECharacterState
{public Character Owner;public CharacterStateMachineData Data;public CharacterStateMachineBase(Character owner, CharacterStateMachineData data null) : base(){Owner owner;if(data ! null )Data data;elseData new CharacterStateMachineData();}//分层状态机递归获取具体状态public CharacterStateBase CurrentState{get{if (ActiveState is CharacterStateMachineBase)return (ActiveState as CharacterStateMachineBase).CurrentState;return ActiveState as CharacterStateBase;}}public override void Trigger(string trigger){//允许玩家通过一些动作和输入打断当前状态if(ActiveState.needsExitTime Data.CanExitBeforeEnd)ActiveState.needsExitTime false;base.Trigger(trigger);}
}public class CharacterStateBase : StateECharacterState
{public Character Owner;new public CharacterStateMachineBase fsm base.fsm as CharacterStateMachineBase;public virtual Vector3 DeltaMotion Owner.Animancer.Animator.deltaPosition;public CharacterStateBase(Character owner) : base(){Owner owner;}public override void OnEnter(){base.OnEnter();fsm.Data.CanExitBeforeEnd false;}
}以上是在使用Trigger时打断的操作假设我们使用的是普通的Transition进行状态变更那么是无法打断的比如如下的移动操作完全没有使用Trigger
MovementStateMachineBase GroundFSM new MovementStateMachineBase(owner);
GroundFSM.AddState(EMovementState.Idle, new IdleState(owner));
GroundFSM.AddState(EMovementState.Move, new MoveState(owner));
GroundFSM.AddTwoWayTransition(EMovementState.Idle, EMovementState.Move,t Owner.Parameters.MovementDirection.magnitude 0);为此我们可以在期望被打断的状态机中特别指出何时可以被打断我当前只在攻击状态时可以被打断不打断会放完攻击动画打断会立刻进入其他状态并播放动画
特别的代码如下该代码会在AttackState的Update函数中被轮询
Parameters是Character的字段用于与直接的GameInput解耦
private void UpdateInterrupt()
{if(!fsm.Data.CanExitBeforeEnd)return;//移动打断if (Owner.Parameters.MovementDirection.magnitude 0){needsExitTime false;}
}