怎么自己编写网站,网络维护工程师是做什么的,网站建站业务,网站建设需要哪些基础准备
Stateless是一个有限状态机扩展包。在c#项目中可以直接通过NuGet安装。
使用他需要先用枚举写好你所有可能的状态和子状态。 例如移动#xff0c;下蹲#xff0c;空闲#xff0c;跳跃#xff0c;游泳#xff0c;奔跑#xff0c;走路。 其中#xff0c;奔跑和走路…准备
Stateless是一个有限状态机扩展包。在c#项目中可以直接通过NuGet安装。
使用他需要先用枚举写好你所有可能的状态和子状态。 例如移动下蹲空闲跳跃游泳奔跑走路。 其中奔跑和走路是移动的子状态。
然后需要写触发器。所有状态转换必须要一个触发器。 所以你需要把所有的时机都精确描述并且哪怕只有一个地方用到也要描写。
class Player
{enum State { 移动, 下蹲, 空闲, 空中, 二段跳, 游泳, 奔跑, 走路 }enum Trigger { 上, 下, 左, 右, 松开下, 落地, 落水, 出水, 跌落 }private StateMachineState, Trigger stateMachine new StateMachineState, Trigger(State.空闲);
}配置状态机
对状态机调用Configure会启用对这个状态下的配置。 从这个配置上调用的配置方法全部都会再返回这个状态的配置。 需要以此法对所有的状态都进行配置。 public Player(){stateMachine.Configure(State.空闲).Permit(Trigger.下, State.下蹲).Permit(Trigger.右, State.走路).Permit(Trigger.左, State.走路);stateMachine.Configure(State.下蹲).Permit(Trigger.松开下, State.空闲);}触发器目标条件。
配置的方法有很多排列组合出来的版本例如
PermitPermitIfPermitDynamicPermitDynamicIf
一个基本的Permit方法接受两个参数第一个是触发器第二个是触发以后要变成谁。 例如空闲在触发了下的情况下会变成下蹲。 Permit(Trigger.下, State.下蹲)
Dynamic
如果有Dynamic那么第二个参数会是一个委托你可以动态的决定要变成谁。 PermitDynamic(Trigger.下, () Guid.NewGuid() Guid.NewGuid() ? State.下蹲 : State.空闲) 例如这句代码中有50%概率变成下蹲有50%概率变成空闲。
If
如果有If,那么还有第三个参数。也是一个委托表示条件需要你返回bool。 PermitIf(Trigger.下, State.下蹲, () Guid.NewGuid() Guid.NewGuid()) 只有条件满足的时候这个转化才能成功。
当一次触发时会判断所有的转化。如果有多个方案可以通过即便目标相同会报错。
有多个相同的方案一个方案存在的时候注册了他的If版本并且If通过。有多个If版本的方案种都通过了。
切换的目标
按照目标分组有以下分类
PermitPermitReentryInternalTransitionInitialTransition
Permit基础的转换自选目标。
PermitReentry重新进入自己意义是触发一次自己的退出和进入方法。 和直接Permit填自己是一样的。
InternalTransition不会发生切换也不会触发退出和进入。 取而代之的是给定一个动作委托触发你的动作。 使得看起来像因为切换状态发生了什么事情。 .InternalTransition(Trigger.上, () { Console.WriteLine(在蹲下的时候不能跳); })
InitialTransition指定一个子状态表示这种状态的默认情况。 指定了以后就不会直接停留在这个状态身上了一旦回来就会切换走。 例如走路和奔跑是移动的子状态。 为移动注册初始子状态为走路那么以任何方式切换到移动都会变为走路。 包括从走路切到移动。
忽略和未定义的触发
Ignore方法可以忽略这个触发器什么也不发生。 那如果声明忽略而调用这个转换会怎么样呢会报一个错表示这个转换没有注册。
你可以对状态机不是配置注册一个委托来表示有未注册的转换时什么也不做。 stateMachine.OnUnhandledTrigger((s, t) { });
有参数的转换
有时候你从外部获取了输入例如鼠标的位置。这种情况下不能用枚举来覆盖所有情况。 你需要对状态机调用SetTriggerParameters方法然后保存这个返回值之后要用到他。 var upTrigger stateMachine.SetTriggerParametersint(Trigger.上);
只能在有If的情况下使用这个东西进行配置。 因为如果你没有条件那么这个参数是没有意义的。 .PermitIf(upTrigger, State.下蹲, i i 4); 使用刚刚获得的东西作为触发器那么你的条件委托就能使用他的参数。
给状态机触发器
使用Fire方法传递给状态机触发器。 他会根据配置进行转换状态。
如果你使用注册了参数的触发器你还可以传递参数。
stateMachine.Fire(Trigger.出水);
stateMachine.Fire(upTrigger, 10086);注册进入和退出
OnEntry方法注册进入这个状态时会发生什么事情。 OnExit方法注册退出这个状态时会发生什么事情。
OnEntryFrom版本的方法。表示从xx触发器来使用注册了参数的触发器就能读取参数。
.OnEntry(() { Console.WriteLine(进入到下蹲); })
.OnExit(() { Console.WriteLine(从下蹲退出); })
.OnEntryFrom(Trigger.出水, () { })
.OnEntryFrom(upTrigger, i Console.WriteLine(携带的参数是 i));子级状态
一个状态可以声明为另一个状态的子状态只能声明一个直接父状态。 stateMachine.Configure(State.二段跳).SubstateOf(State.空中);stateMachine.Configure(State.奔跑).SubstateOf(State.移动);stateMachine.Configure(State.走路).SubstateOf(State.移动);如果只涉及子状态改变状态是同一个那么不会执行父状态的进入和退出。 例如说从走路切换到奔跑就移动而言是没有变化的。
如果从子状态越过父状态切换到了其他状态那么他们的退出都会执行。 例如从走路切换到空闲。那么此时也不能算作移动状态所以移动也会一起结束。
bool b1 stateMachine.IsInState(State.移动);
bool b2 stateMachine.State State.移动;可以使用IsInState方法带父子级进行状态检测。 例如如果当前是走路那么b1是true因为走路是移动。 b2是false他是直接比较的。
异步任务
带有Async的方法可以把注册的委托改为异步形式。
StateMachinestring, int st new StateMachinestring, int(1);
st.Configure(1).Permit(2, 2).Permit(3, 3);st.Configure(2).OnEntryFromAsync(2, async () {await Task.Delay(100);await Console.Out.WriteLineAsync(进入2的第一个异步);await Task.Delay(100);await Console.Out.WriteLineAsync(进入2的第二个异步);}).OnEntryFrom(3, () Console.WriteLine(进入2的同步)).Permit(1, 1).Permit(3, 3).OnExit(() Console.WriteLine(离开2));st.Configure(3).OnEntry(() Console.WriteLine(进入3)).Permit(1, 1).Permit(3, 2);var t st.FireAsync(2);
_ st.FireAsync(3);
_ st.FireAsync(1);
await t;
Console.WriteLine(st.State);会有以下几个影响
在异步期间所有的输入会排队直到异步完成再依次执行 如果不使用Async后缀的方法注册的委托也可以是异步的。状态机会认为他是普通方法而程序会在后台把异步继续下去。 如果转换的源状态的退出或是目标状态的进入有异步任务那么就要使用FireAsync 即便使用了OnEntryFromAsync 携带条件并且没有通过此条件也会报错。可以使用Fire的转换可以使用FireAsync而不会报错。 如果使用了任何通用转换的异步那么所有转换都要使用FireAsync OnTransitionCompletedAsyncOnTransitionedAsyncOnUnhandledTriggerAsync
先后顺序
从子级到父级依次经过所有Exit。执行转换间隙。从父级到子级依次执行Entry带参数的Entry进入下一级。完成切换后执行转换完成。
StateMachinestring, int st new StateMachinestring, int(1.1);st.Configure(1).OnExit(() Console.WriteLine(从1退出));st.Configure(1.1).SubstateOf(1).Permit(0, 2.1).OnExit(() Console.WriteLine(从1.1退出));st.Configure(2).OnEntry(() Console.WriteLine(进入2)).OnEntryFrom(0, () Console.WriteLine(通过0进入2));st.Configure(2.1).OnEntry(() Console.WriteLine(进入2.1)).OnEntryFrom(0, () Console.WriteLine(通过0进入2.2)).SubstateOf(2);st.OnTransitioned(st Console.WriteLine(完成所有退出));
st.OnTransitionCompleted(st Console.WriteLine(进入到目标状态));st.Fire(0);
/*
从1.1退出
从1退出
完成所有退出
进入2
通过0进入2
进入2.1
通过0进入2.2
进入到目标状态
*/示例
class Player
{enum State { 移动, 下蹲, 空闲, 空中, 二段跳, 游泳, 奔跑, 走路 }enum Trigger { 键盘输入, 落地, 落水, 出水, 跌落, 超时 }DateTime TimeOut;StateMachineState, Trigger stateMachine;StateMachineState, Trigger.TriggerWithParametersVector2 InputTrigger;public Player(){stateMachine new StateMachineState, Trigger(State.空闲);InputTrigger stateMachine.SetTriggerParametersVector2(Trigger.键盘输入);stateMachine.OnUnhandledTrigger((s, t) { });stateMachine.Configure(State.移动).PermitIf(InputTrigger, State.空闲, vec vec Vector2.Zero).PermitIf(InputTrigger, State.空中, vec vec Vector2.UnitY).PermitIf(InputTrigger, State.下蹲, vec vec -Vector2.UnitY).Permit(Trigger.跌落, State.空中).Permit(Trigger.落水, State.游泳);stateMachine.Configure(State.下蹲).PermitIf(InputTrigger, State.空闲, vec vec Vector2.Zero).PermitIf(InputTrigger, State.空中, vec vec Vector2.UnitY);stateMachine.Configure(State.空闲).PermitIf(InputTrigger, State.走路, vec vec.Y 0 vec.X ! 0).PermitIf(InputTrigger, State.空中, vec vec Vector2.UnitY).PermitIf(InputTrigger, State.下蹲, vec vec -Vector2.UnitY);stateMachine.Configure(State.空中).Permit(Trigger.落地, State.空闲).PermitIf(InputTrigger, State.二段跳, vec vec Vector2.UnitY);stateMachine.Configure(State.二段跳).SubstateOf(State.空中).IgnoreIf(InputTrigger, vec vec Vector2.UnitY);stateMachine.Configure(State.游泳).Permit(Trigger.出水, State.空中);stateMachine.Configure(State.奔跑).SubstateOf(State.移动);stateMachine.Configure(State.走路).SubstateOf(State.移动).PermitIf(Trigger.超时, State.奔跑, () DateTime.Now - TimeOut TimeSpan.FromMicroseconds(500))//如果最后更新时间超过500秒则说明纯按住了走路0.5秒.OnEntry(async () {TimeOut DateTime.Now;await Task.Delay(500);//在0.5秒延迟后尝试改为奔跑stateMachine.Fire(Trigger.超时);}).OnExit(() {TimeOut DateTime.Now;});}
}