aigc實現(xiàn)的并發(fā)狀態(tài)機

參考游戲編程模式
并發(fā)狀態(tài)機
我們決定給英雄拿槍的能力昨悼。 當她拿著槍的時候,她還是能做她之前的任何事情:跑動跃洛,
跳躍率触,跳斬,等等汇竭。 但是她在做這些的同時也要能開火葱蝗。
如果我們執(zhí)著于FSM,我們需要翻倍現(xiàn)有狀態(tài)细燎。 對于每個現(xiàn)有狀態(tài)两曼,我們需要另一個她持
槍狀態(tài):站立,持槍站立玻驻,跳躍悼凑,持槍跳躍, 你知道我的意思了吧璧瞬。
多加幾種武器户辫,狀態(tài)就會指數(shù)爆炸。 不但增加了大量的狀態(tài)嗤锉,這也增加了大量的冗余: 持
槍和不持槍的狀態(tài)是完全一樣的寸莫,只是多了一點負責射擊的代碼。
問題在于我們將兩種狀態(tài)綁定——她做的和她攜帶的——到了一個狀態(tài)機上档冬。 為了處理所有
可能的組合膘茎,我們需要為每一對組合寫一個狀態(tài)。 修復方法很明顯:使用兩個單獨的狀態(tài)
機酷誓。
如果她在做什么有n個狀態(tài)披坏,而她攜帶了什么有m個狀態(tài),要塞到一個狀態(tài)機中盐数,
我們需要n × m個狀態(tài)棒拂。使用兩個狀態(tài)機,就只有n + m個。
我們保留之前記錄她在做什么的狀態(tài)機帚屉,不用管它谜诫。 然后定義她攜帶了什么的單獨狀態(tài)機
。 Heroine將會有兩個“狀態(tài)”引用攻旦,每個對應一個狀態(tài)機喻旷,就像這樣:
···
class Heroine
{
// 其他代碼……
private:
HeroineState* state_;
HeroineState* equipment_;
};
···
為了便于說明,她的裝備也使用了狀態(tài)模式牢屋。 在實踐中且预,由于裝備只有兩個狀態(tài)
,一個布爾標識就夠了烙无。
當英雄把輸入委托給了狀態(tài)锋谐,兩個狀態(tài)都需要委托:
···
void Heroine::handleInput(Input input)
{
state_->handleInput(this, input);
equipment_->handleInput(
this, input);
}
···
功能更完備的系統(tǒng)也許能讓狀態(tài)機銷毀輸入,這樣其他狀態(tài)機就不會收到了截酷。 這
能阻止兩個狀態(tài)機響應同一輸入涮拗。
每個狀態(tài)機之后都能響應輸入,發(fā)生行為迂苛,獨立于其它機器改變狀態(tài)三热。 當兩個狀態(tài)集合幾
乎沒有聯(lián)系的時候,它工作得不錯灾部。
在實踐中康铭,你會發(fā)現(xiàn)狀態(tài)有時需要交互惯退。 舉個例子赌髓,也許她在跳躍時不能開火,或者她在
持槍時不能跳斬攻擊催跪。 為了完成這個锁蠕,你也許會在狀態(tài)的代碼中做一些粗糙的if測試其他
狀態(tài)來協(xié)同, 這不是最優(yōu)雅的解決方案懊蒸,但這可以搞定工作荣倾。

···
// 定義角色狀態(tài)
enum CharacterState {
Idle,
Moving,
Attacking,
Ducking,
Jumping,
}
// 定義角色裝備狀態(tài)
enum EquipmentState {
None,
Gun,
}
// 定義角色類
class Character extends cc.Component {
private _stateStack: CharacterState[] = [CharacterState.Idle]; // 角色狀態(tài)棧
private _equipmentStack: EquipmentState[] = [EquipmentState.None]; // 角色裝備狀態(tài)棧
private _isFiring: boolean = false; // 是否正在開火
private _moveSpeed: number = 100; // 移動速度
private _fireInterval: number = 0.5; // 開火間隔時間
private _fireTimer: number = 0; // 開火計時器
constructor() {
super();
}
update(dt: number) {
this._fireTimer += dt;
}
// 切換角色狀態(tài)方法
private changeCharacterState(newState: CharacterState) {
if (this._stateStack[this._stateStack.length - 1] === newState) {
return;
}
this._stateStack.push(newState);
switch (newState) {
case CharacterState.Idle:
this.stopMoving();
break;
case CharacterState.Moving:
this.startMoving();
break;
case CharacterState.Attacking:
this.startAttacking();
break;
case CharacterState.Ducking:
this.startDucking();
break;
case CharacterState.Jumping:
this.startJumping();
break;
}
}
// 結束當前角色狀態(tài)方法
private endCurrentCharacterState() {
const currentState = this._stateStack.pop()!;
switch (currentState) {
case CharacterState.Idle:
break;
case CharacterState.Moving:
this.stopMoving();
break;
case CharacterState.Attacking:
this.stopAttacking();
break;
case CharacterState.Ducking:
this.stopDucking();
break;
case CharacterState.Jumping:
this.stopJumping();
break;
}
}
// 切換角色裝備狀態(tài)方法
private changeEquipmentState(newState: EquipmentState) {
if (this._equipmentStack[this._equipmentStack.length - 1] === newState) {
return;
}
this._equipmentStack.push(newState);
switch (newState) {
case EquipmentState.None:
this.stopFiring();
break;
case EquipmentState.Gun:
this.startFiring();
break;
}
}
// 結束當前角色裝備狀態(tài)方法
private endCurrentEquipmentState() {
const currentState = this._equipmentStack.pop()!;
switch (currentState) {
case EquipmentState.None:
break;
case EquipmentState.Gun:
this.stopFiring();
break;
}
}
// 開始移動方法
private startMoving() {
// TODO:播放移動動畫等操作
}
// 停止移動方法
private stopMoving() {
// TODO:播放停止動畫等操作
}
// 開始攻擊方法
private startAttacking() {
if (!this._isFiring && this._fireTimer >= this._fireInterval) {
this._isFiring = true;
this._fireTimer = 0;
// TODO:播放攻擊動畫等操作,并在攻擊結束后調用 stopAttacking 方法停止攻擊
}
}
// 停止攻擊方法
private stopAttacking() {
this._isFiring = false;
// TODO:停止播放攻擊動畫等操作
}
// 開始俯臥方法
private startDucking() {
// TODO:播放俯臥動畫等操作骑丸,并在俯臥結束后調用 stopDucking 方法停止俯臥
}
// 停止俯臥方法
private stopDucking() {
// TODO:停止播放俯臥動畫等操作
}
// 開始跳躍方法
private startJumping() {
// TODO:播放跳躍動畫等操作舌仍,并在跳躍結束后調用 stopJumping 方法停止跳躍
}
// 停止跳躍方法
private stopJumping() {
// TODO:停止播放跳躍動畫等操作
}
// 開始開火方法
private startFiring() {
if (!this._isFiring && this._fireTimer >= this._fireInterval) {
this._isFiring = true;
this._fireTimer = 0;
// TODO:播放開火動畫等操作,并在開火結束后調用 stopFiring 方法停止開火
}
}
// 停止開火方法
private stopFiring() {
this._isFiring = false;
// TODO:停止播放開火動畫等操作
}
// 移動方法
public move(direction: cc.Vec3) {
if (direction.mag() > 0) {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Moving) {
this.changeCharacterState(CharacterState.Moving);
}
this.node.position = this.node.position.add(direction.normalize().mul(this._moveSpeed));
} else {
if (this._stateStack[this._stateStack.length - 1] === CharacterState.Moving) {
this.endCurrentCharacterState();
}
}
}
// 切換裝備方法
public switchEquipment(equipment: EquipmentState) {
if (this._equipmentStack[this._equipmentStack.length - 1] !== equipment) {
this.endCurrentEquipmentState();
this.changeEquipmentState(equipment);
}
}
// 開火方法
public fire() {
if (this._equipmentStack[this._equipmentStack.length - 1] === EquipmentState.Gun) {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Attacking) {
this.changeCharacterState(CharacterState.Attacking);
}
}
}
// 跳躍方法
public jump() {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Jumping) {
this.changeCharacterState(CharacterState.Jumping);
}
}
// 俯臥方法
public duck() {
if (this._stateStack[this._stateStack.length - 1] !== CharacterState.Ducking) {
this.changeCharacterState(CharacterState.Ducking);
}
}
// 站起方法
public standUp() {
if (this._stateStack[this._stateStack.length - 1] === CharacterState.Ducking) {
this.endCurrentCharacterState();
}
}
}
···
在上面的代碼中通危,我們定義了一個名為 Character 的角色類铸豁,實現(xiàn)了并發(fā)狀態(tài)機。我們使用兩個狀態(tài)棧 _stateStack 和 _equipmentStack 分別表示角色的行為狀態(tài)和裝備狀態(tài)菊碟。在 update 方法中节芥,我們更新開火計時器 _fireTimer 的值。在 changeCharacterState 和 endCurrentCharacterState 方法中,我們根據(jù)新狀態(tài)切換角色的行為头镊,并在需要時播放相應的動畫蚣驼。在 changeEquipmentState 和 endCurrentEquipmentState 方法中,我們根據(jù)新裝備切換角色的裝備狀態(tài)相艇,并在需要時播放相應的動畫颖杏。在 move、fire厂捞、jump输玷、duck 和 standUp 方法中,我們切換角色的狀態(tài)靡馁,并在需要時播放相應的動畫欲鹏。需要注意的是,在實際開發(fā)中臭墨,我們還需要根據(jù)具體需求進行修改和優(yōu)化赔嚎,并添加相應的條件和轉換條件。同時胧弛,在使用并發(fā)狀態(tài)機時尤误,我們需要小心處理狀態(tài)之間的交互和沖突問題,并盡可能地保持代碼簡潔和易于維護结缚。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末损晤,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子红竭,更是在濱河造成了極大的恐慌尤勋,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茵宪,死亡現(xiàn)場離奇詭異最冰,居然都是意外死亡,警方通過查閱死者的電腦和手機稀火,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門暖哨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凰狞,你說我怎么就攤上這事篇裁。” “怎么了赡若?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵达布,是天一觀的道長。 經(jīng)常有香客問我斩熊,道長往枣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮分冈,結果婚禮上圾另,老公的妹妹穿的比我還像新娘。我一直安慰自己雕沉,他們只是感情好集乔,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著坡椒,像睡著了一般扰路。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上倔叼,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天汗唱,我揣著相機與錄音,去河邊找鬼丈攒。 笑死哩罪,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的巡验。 我是一名探鬼主播际插,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼显设!你這毒婦竟也來了框弛?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤捕捂,失蹤者是張志新(化名)和其女友劉穎瑟枫,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绞蹦,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡力奋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年榜旦,在試婚紗的時候發(fā)現(xiàn)自己被綠了幽七。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡溅呢,死狀恐怖澡屡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情咐旧,我是刑警寧澤驶鹉,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站铣墨,受9級特大地震影響室埋,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一姚淆、第九天 我趴在偏房一處隱蔽的房頂上張望孕蝉。 院中可真熱鬧,春花似錦腌逢、人聲如沸降淮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佳鳖。三九已至,卻和暖如春媒惕,著一層夾襖步出監(jiān)牢的瞬間系吩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工妒蔚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淑玫,地道東北人。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓面睛,卻偏偏與公主長得像絮蒿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子叁鉴,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348