建立一個(gè)簡(jiǎn)單的企業(yè)官網(wǎng)seo現(xiàn)在還有前景嗎
準(zhǔn)備
Stateless是一個(gè)有限狀態(tài)機(jī)擴(kuò)展包。在c#項(xiàng)目中可以直接通過(guò)NuGet安裝。
使用他需要先用枚舉寫(xiě)好你所有可能的狀態(tài)和子狀態(tài)。
例如移動(dòng),下蹲,空閑,跳躍,游泳,奔跑,走路。
其中,奔跑和走路是移動(dòng)的子狀態(tài)。
然后需要寫(xiě)觸發(fā)器。所有狀態(tài)轉(zhuǎn)換必須要一個(gè)觸發(fā)器。
所以你需要把所有的時(shí)機(jī)都精確描述,并且哪怕只有一個(gè)地方用到也要描寫(xiě)。
class Player
{enum State { 移動(dòng), 下蹲, 空閑, 空中, 二段跳, 游泳, 奔跑, 走路 }enum Trigger { 上, 下, 左, 右, 松開(kāi)下, 落地, 落水, 出水, 跌落 }private StateMachine<State, Trigger> stateMachine = new StateMachine<State, Trigger>(State.空閑);
}
配置狀態(tài)機(jī)
對(duì)狀態(tài)機(jī)調(diào)用Configure
會(huì)啟用對(duì)這個(gè)狀態(tài)下的配置。
從這個(gè)配置上調(diào)用的配置方法全部都會(huì)再返回這個(gè)狀態(tài)的配置。
需要以此法對(duì)所有的狀態(tài)都進(jìn)行配置。
public Player(){stateMachine.Configure(State.空閑).Permit(Trigger.下, State.下蹲).Permit(Trigger.右, State.走路).Permit(Trigger.左, State.走路);stateMachine.Configure(State.下蹲).Permit(Trigger.松開(kāi)下, State.空閑);}
觸發(fā)器,目標(biāo),條件。
配置的方法有很多排列組合出來(lái)的版本,例如
- Permit
- PermitIf
- PermitDynamic
- PermitDynamicIf
一個(gè)基本的Permit方法接受兩個(gè)參數(shù),第一個(gè)是觸發(fā)器,第二個(gè)是觸發(fā)以后要變成誰(shuí)。
例如空閑在觸發(fā)了下的情況下會(huì)變成下蹲。
Permit(Trigger.下, State.下蹲)
Dynamic
如果有Dynamic,那么第二個(gè)參數(shù)會(huì)是一個(gè)委托,你可以動(dòng)態(tài)的決定要變成誰(shuí)。
PermitDynamic(Trigger.下, () => Guid.NewGuid() > Guid.NewGuid() ? State.下蹲 : State.空閑)
例如這句代碼中,有50%概率變成下蹲,有50%概率變成空閑。
If
如果有If,那么還有第三個(gè)參數(shù)。也是一個(gè)委托,表示條件,需要你返回bool。
PermitIf(Trigger.下, State.下蹲, () => Guid.NewGuid() > Guid.NewGuid())
只有條件滿(mǎn)足的時(shí)候,這個(gè)轉(zhuǎn)化才能成功。
當(dāng)一次觸發(fā)時(shí),會(huì)判斷所有的轉(zhuǎn)化。如果有多個(gè)方案可以通過(guò)(即便目標(biāo)相同),會(huì)報(bào)錯(cuò)。
- 有多個(gè)相同的方案
- 一個(gè)方案存在的時(shí)候注冊(cè)了他的If版本,并且If通過(guò)。
- 有多個(gè)If版本的方案種都通過(guò)了。
切換的目標(biāo)
按照目標(biāo)分組有以下分類(lèi)
- Permit
- PermitReentry
- InternalTransition
- InitialTransition
Permit
基礎(chǔ)的轉(zhuǎn)換,自選目標(biāo)。
PermitReentry
重新進(jìn)入自己,意義是觸發(fā)一次自己的退出和進(jìn)入方法。
和直接Permit填自己是一樣的。
InternalTransition
不會(huì)發(fā)生切換,也不會(huì)觸發(fā)退出和進(jìn)入。
取而代之的是給定一個(gè)動(dòng)作委托,觸發(fā)你的動(dòng)作。
使得看起來(lái)像因?yàn)榍袚Q狀態(tài)發(fā)生了什么事情。
.InternalTransition(Trigger.上, () => { Console.WriteLine("在蹲下的時(shí)候不能跳"); })
InitialTransition
指定一個(gè)子狀態(tài),表示這種狀態(tài)的默認(rèn)情況。
指定了以后就不會(huì)直接停留在這個(gè)狀態(tài)身上了,一旦回來(lái)就會(huì)切換走。
例如走路和奔跑是移動(dòng)的子狀態(tài)。
為移動(dòng)注冊(cè)初始子狀態(tài)為走路,那么以任何方式切換到移動(dòng)都會(huì)變?yōu)樽呗贰?br /> 包括從走路切到移動(dòng)。
忽略和未定義的觸發(fā)
Ignore
方法可以忽略這個(gè)觸發(fā)器,什么也不發(fā)生。
那如果聲明忽略而調(diào)用這個(gè)轉(zhuǎn)換會(huì)怎么樣呢?會(huì)報(bào)一個(gè)錯(cuò),表示這個(gè)轉(zhuǎn)換沒(méi)有注冊(cè)。
你可以對(duì)狀態(tài)機(jī)(不是配置)注冊(cè)一個(gè)委托,來(lái)表示有未注冊(cè)的轉(zhuǎn)換時(shí)什么也不做。
stateMachine.OnUnhandledTrigger((s, t) => { });
有參數(shù)的轉(zhuǎn)換
有時(shí)候,你從外部獲取了輸入,例如鼠標(biāo)的位置。這種情況下不能用枚舉來(lái)覆蓋所有情況。
你需要對(duì)狀態(tài)機(jī)調(diào)用SetTriggerParameters方法,然后保存這個(gè)返回值,之后要用到他。
var upTrigger = stateMachine.SetTriggerParameters<int>(Trigger.上);
只能在有If的情況下使用這個(gè)東西進(jìn)行配置。
因?yàn)槿绻銢](méi)有條件,那么這個(gè)參數(shù)是沒(méi)有意義的。
.PermitIf(upTrigger, State.下蹲, i => i > 4);
使用剛剛獲得的東西作為觸發(fā)器,那么你的條件委托就能使用他的參數(shù)。
給狀態(tài)機(jī)觸發(fā)器
使用Fire方法傳遞給狀態(tài)機(jī)觸發(fā)器。
他會(huì)根據(jù)配置進(jìn)行轉(zhuǎn)換狀態(tài)。
如果你使用注冊(cè)了參數(shù)的觸發(fā)器,你還可以傳遞參數(shù)。
stateMachine.Fire(Trigger.出水);
stateMachine.Fire(upTrigger, 10086);
注冊(cè)進(jìn)入和退出
OnEntry方法注冊(cè)進(jìn)入這個(gè)狀態(tài)時(shí)會(huì)發(fā)生什么事情。
OnExit方法注冊(cè)退出這個(gè)狀態(tài)時(shí)會(huì)發(fā)生什么事情。
OnEntryFrom版本的方法。表示從xx觸發(fā)器來(lái),使用注冊(cè)了參數(shù)的觸發(fā)器就能讀取參數(shù)。
.OnEntry(() => { Console.WriteLine("進(jìn)入到下蹲"); })
.OnExit(() => { Console.WriteLine("從下蹲退出"); })
.OnEntryFrom(Trigger.出水, () => { })
.OnEntryFrom(upTrigger, i => Console.WriteLine("攜帶的參數(shù)是" + i));
子級(jí)狀態(tài)
一個(gè)狀態(tài)可以聲明為另一個(gè)狀態(tài)的子狀態(tài)(只能聲明一個(gè)直接父狀態(tài))。
stateMachine.Configure(State.二段跳).SubstateOf(State.空中);stateMachine.Configure(State.奔跑).SubstateOf(State.移動(dòng));stateMachine.Configure(State.走路).SubstateOf(State.移動(dòng));
如果只涉及子狀態(tài)改變,狀態(tài)是同一個(gè),那么不會(huì)執(zhí)行父狀態(tài)的進(jìn)入和退出。
例如說(shuō),從走路切換到奔跑,就移動(dòng)而言是沒(méi)有變化的。
如果從子狀態(tài)越過(guò)父狀態(tài)切換到了其他狀態(tài),那么他們的退出都會(huì)執(zhí)行。
例如從走路切換到空閑。那么此時(shí)也不能算作移動(dòng)狀態(tài),所以移動(dòng)也會(huì)一起結(jié)束。
bool b1 = stateMachine.IsInState(State.移動(dòng));
bool b2 = stateMachine.State == State.移動(dòng);
可以使用IsInState
方法帶父子級(jí)進(jìn)行狀態(tài)檢測(cè)。
例如如果當(dāng)前是走路,那么b1是true,因?yàn)樽呗肥且苿?dòng)。
b2是false,他是直接比較的。
異步任務(wù)
帶有Async的方法可以把注冊(cè)的委托改為異步形式。
StateMachine<string, int> st = new StateMachine<string, 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("進(jìn)入2的第一個(gè)異步");await Task.Delay(100);await Console.Out.WriteLineAsync("進(jìn)入2的第二個(gè)異步");}).OnEntryFrom(3, () => Console.WriteLine("進(jìn)入2的同步")).Permit(1, "1").Permit(3, "3").OnExit(() => Console.WriteLine("離開(kāi)2"));st.Configure("3").OnEntry(() => Console.WriteLine("進(jìn)入3")).Permit(1, "1").Permit(3, "2");var t = st.FireAsync(2);
_ = st.FireAsync(3);
_ = st.FireAsync(1);
await t;
Console.WriteLine(st.State);
會(huì)有以下幾個(gè)影響
- 在異步期間,所有的輸入會(huì)排隊(duì),直到異步完成再依次執(zhí)行
- 如果不使用Async后綴的方法,注冊(cè)的委托也可以是異步的。狀態(tài)機(jī)會(huì)認(rèn)為他是普通方法,而程序會(huì)在后臺(tái)把異步繼續(xù)下去。
- 如果轉(zhuǎn)換的源狀態(tài)的退出,或是目標(biāo)狀態(tài)的進(jìn)入有異步任務(wù),那么就要使用
FireAsync
- 即便使用了
OnEntryFromAsync
攜帶條件,并且沒(méi)有通過(guò)此條件,也會(huì)報(bào)錯(cuò)。 - 可以使用
Fire
的轉(zhuǎn)換,可以使用FireAsync
而不會(huì)報(bào)錯(cuò)。
- 即便使用了
- 如果使用了任何通用轉(zhuǎn)換的異步,那么所有轉(zhuǎn)換都要使用
FireAsync
- OnTransitionCompletedAsync
- OnTransitionedAsync
- OnUnhandledTriggerAsync
先后順序
- 從子級(jí)到父級(jí),依次經(jīng)過(guò)所有Exit。
- 執(zhí)行轉(zhuǎn)換間隙。
- 從父級(jí)到子級(jí),依次執(zhí)行Entry,帶參數(shù)的Entry,進(jìn)入下一級(jí)。
- 完成切換后執(zhí)行轉(zhuǎn)換完成。
StateMachine<string, int> st = new StateMachine<string, 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("進(jìn)入2")).OnEntryFrom(0, () => Console.WriteLine("通過(guò)0進(jìn)入2"));st.Configure("2.1").OnEntry(() => Console.WriteLine("進(jìn)入2.1")).OnEntryFrom(0, () => Console.WriteLine("通過(guò)0進(jìn)入2.2")).SubstateOf("2");st.OnTransitioned(st => Console.WriteLine("完成所有退出"));
st.OnTransitionCompleted(st => Console.WriteLine("進(jìn)入到目標(biāo)狀態(tài)"));st.Fire(0);
/*
從1.1退出
從1退出
完成所有退出
進(jìn)入2
通過(guò)0進(jìn)入2
進(jìn)入2.1
通過(guò)0進(jìn)入2.2
進(jìn)入到目標(biāo)狀態(tài)
*/
示例
class Player
{enum State { 移動(dòng), 下蹲, 空閑, 空中, 二段跳, 游泳, 奔跑, 走路 }enum Trigger { 鍵盤(pán)輸入, 落地, 落水, 出水, 跌落, 超時(shí) }DateTime TimeOut;StateMachine<State, Trigger> stateMachine;StateMachine<State, Trigger>.TriggerWithParameters<Vector2> InputTrigger;public Player(){stateMachine = new StateMachine<State, Trigger>(State.空閑);InputTrigger = stateMachine.SetTriggerParameters<Vector2>(Trigger.鍵盤(pán)輸入);stateMachine.OnUnhandledTrigger((s, t) => { });stateMachine.Configure(State.移動(dòng)).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.移動(dòng));stateMachine.Configure(State.走路).SubstateOf(State.移動(dòng)).PermitIf(Trigger.超時(shí), State.奔跑, () => DateTime.Now - TimeOut > TimeSpan.FromMicroseconds(500))//如果最后更新時(shí)間超過(guò)500秒則說(shuō)明純按住了走路0.5秒.OnEntry(async () =>{TimeOut = DateTime.Now;await Task.Delay(500);//在0.5秒延遲后嘗試改為奔跑stateMachine.Fire(Trigger.超時(shí));}).OnExit(() =>{TimeOut = DateTime.Now;});}
}