1、模式標準
模式名稱:狀態模式
模式分類:行為型
模式意圖:允許一個對象在其內部狀態改變時改變它的行為。對象看起來似乎修改了它的類。
結構圖:
適用于:
1、一個對象的行為決定于它的狀態,并且它必須在運行時刻根據狀態改變它的行為。
2、一個操作中含有龐大的多分支的條件語句,且這些分支依賴丁該對象的狀態。這個狀態常用一個或多個枚舉常量表示。通常,有多個操作包含這一相同的條件結構。State模式將每一個條件分支放入一個獨立的類中。這使得開發者可以根據對象自身的情況將對象的狀態作為一個對象,這一對象可以不依賴于其他對象獨立變化。
主要成員:
- 上下文(Context):它定義了客戶端感興趣的接口,并且維護一個指向當前狀態的實例變量。
- 狀態抽象(State):這是一個接口或者抽象類,它定義了每個狀態必須實現的方法。
- 具體狀態(Concrete States):它們是實現狀態接口的類,每個類對應一種狀態,且包含了該狀態下的行為實現。
2、分析與設計??
在一般的游戲開發中狀態值通常是一個枚舉值,但在狀態模式中,狀態值是一個通過實現了 IUnitState
?接口的對象表示的。這種方法的優點是它更加靈活和強大,因為這個狀態值不僅僅是一個值,它還是一組行為的集合(即方法實現)。這允許您在不同的狀態之間切換行為,而不是僅僅改變一個表示狀態的值。
在游戲中的單位一般有以下幾種狀態:站立,移動,攻擊,釋放技能中,眩暈中,死亡。比較常見的是單位正在釋放一個技能,這個時候一個飛錘飛過來,將他擊暈了,他停止了技能的釋放。
接下來我們修改一下我們的意圖
意圖:允許一個對象(單位)在其內部狀態改變時(由其狀態對象來)改變它的行為。對象看起來似乎修改了它的類(實際是狀態對象干的)。
3、開始打造
export enum UnitStateType {Standing,Moving,Attacking,CastSkilling,Stuning,Die
}
export interface IUnitState {enterState(unitItem: IUnitItem): void//stand(): void; // 站立move(): void; // 移動attack(): void; // 攻擊castSkill(): void; // 釋放技能stun(): void; // 擊暈die(): void; // 死亡// getType(): UnitStateType
}
// 狀態基類,包含一個指向Unit的引用
export abstract class BaseState implements IUnitState {protected unitItem: IUnitItem;enterState(unitItem: IUnitItem) {this.unitItem = unitItem;}// 獲取狀態的type值abstract getType(): UnitStateType;// 狀態abstract stand(): void;abstract move(): void;abstract attack(): void;abstract castSkill(): void;abstract stun(): void;abstract die(): void;}
// 站立狀態
export class StandingState extends BaseState {getType(): UnitStateType {return UnitStateType.Standing;}stand() {console.log(this.unitItem, "單位已經進入站立狀態");}move() {console.log(this.unitItem, "單位準備進入移動狀態");this.unitItem.setState(new MovingState());}attack(): void {console.log(this.unitItem, "單位準備進入攻擊狀態");this.unitItem.setState(new AttackingState());}castSkill(): void {console.log(this.unitItem, "單位準備進入釋放技能狀態");this.unitItem.setState(new CastSkillingState());}stun(): void {console.log(this.unitItem, "單位準備進入擊暈狀態");this.unitItem.setState(new StuningState());}die() {console.log("單位準備進入死亡狀態");this.unitItem.setState(new DeadState());}}// 移動狀態
export class MovingState extends BaseState {getType(): UnitStateType {return UnitStateType.Moving;}stand() {console.log(this.unitItem, "單位準備進入站立狀態");this.unitItem.setState(new StandingState());}move() {console.log(this.unitItem, "單位已經進入移動狀態");}attack(): void {console.log(this.unitItem, "單位準備進入攻擊狀態");this.unitItem.setState(new AttackingState());}castSkill(): void {console.log(this.unitItem, "單位準備進入釋放技能狀態");this.unitItem.setState(new CastSkillingState());}stun(): void {console.log(this.unitItem, "單位準備進入擊暈狀態");this.unitItem.setState(new StuningState());}die() {console.log(this.unitItem, "單位準備進入死亡狀態");this.unitItem.setState(new DeadState());}}// 攻擊狀態
export class AttackingState extends BaseState {getType(): UnitStateType {return UnitStateType.Attacking;}enterState(unitItem: IUnitItem) {super.enterState(unitItem);this.doAction();}doAction() {// 執行攻擊this.unitItem.role.attack(); // 攻擊// 如果攻擊順利完成,進行清理并返回到正常狀態// 例如,設置一個延時來模擬攻擊動作的時間let attackDuration = 1000setTimeout(() => {if (this.unitItem.getCurrentState().getType() == UnitStateType.Attacking) {console.log('單位已從攻擊狀態到站立狀態')this.unitItem.getCurrentState().stand()}}, attackDuration);}stand() {console.log(this.unitItem, "單位準備進入站立狀態");this.unitItem.setState(new StandingState());}move() {console.log(this.unitItem, "單位準備進入移動狀態");this.unitItem.setState(new MovingState());}attack(): void {console.log(this.unitItem, "單位已經進入攻擊狀態");}castSkill(): void {console.log(this.unitItem, "單位準備進入釋放技能狀態");this.unitItem.setState(new CastSkillingState());}stun(): void {console.log(this.unitItem, "單位準備進入擊暈狀態");this.unitItem.setState(new StuningState());}die() {console.log(this.unitItem, "單位準備進入死亡狀態");this.unitItem.setState(new DeadState());}}// 釋放技能狀態
export class CastSkillingState extends BaseState {getType(): UnitStateType {return UnitStateType.CastSkilling;}enterState(unitItem: IUnitItem) {super.enterState(unitItem);this.doAction();}doAction() {// 執行攻擊// this.unitItem.role.attack(); // 攻擊// 如果攻擊順利完成,進行清理并返回到正常狀態// 例如,設置一個延時來模擬攻擊動作的時間let attackDuration = 1000setTimeout(() => {if (this.unitItem.getCurrentState().getType() == UnitStateType.CastSkilling) {console.log('單位已從技能釋放狀態到站立狀態')this.unitItem.getCurrentState().stand()}}, attackDuration);}stand() {console.log(this.unitItem, "單位準備進入站立狀態");this.unitItem.setState(new StandingState());}move() {console.log(this.unitItem, "單位準備進入移動狀態");this.unitItem.setState(new MovingState());}attack(): void {console.log(this.unitItem, "單位準備進入攻擊狀態");this.unitItem.setState(new AttackingState());}castSkill(): void {console.log(this.unitItem, "單位已經進入釋放技能狀態");}stun(): void {console.log(this.unitItem, "單位準備進入擊暈狀態");this.unitItem.setState(new StuningState());}die() {console.log(this.unitItem, "單位準備進入死亡狀態");this.unitItem.setState(new DeadState());}}// 擊暈狀態
export class StuningState extends BaseState {getType(): UnitStateType {return UnitStateType.Stuning;}enterState(unitItem: IUnitItem) {super.enterState(unitItem);this.stopCurrentAction();}stand() {console.log(this.unitItem, "單位準備進入站立狀態");this.unitItem.setState(new StandingState());}move() {console.log(this.unitItem, "單位準備進入移動狀態");this.unitItem.setState(new MovingState());}attack(): void {console.log(this.unitItem, "單位準備進入攻擊狀態");this.unitItem.setState(new AttackingState());}castSkill(): void {console.log(this.unitItem, "單位準備進入釋放技能狀態");this.unitItem.setState(new CastSkillingState());}stun(): void {console.log(this.unitItem, "單位已經進入擊暈狀態");}die() {console.log(this.unitItem, "單位準備進入死亡狀態");this.unitItem.setState(new DeadState());}stopCurrentAction() {console.log(this.unitItem, "單位所有動作停止,因為被擊暈");// 如果有正在進行的釋放技能的操作,這里將其中斷// 這可能包括清除技能計時器、動畫等}}
// 死亡狀態
export class DeadState extends BaseState {getType(): UnitStateType {return UnitStateType.Dead;}enterState(unitItem: IUnitItem) {super.enterState(unitItem);this.stopCurrentAction();}stand() {console.log(this.unitItem, "單位準備進入站立狀態");this.unitItem.setState(new StandingState());}move() {console.log(this.unitItem, "單位準備進入移動狀態");this.unitItem.setState(new MovingState());}attack(): void {console.log(this.unitItem, "單位準備進入攻擊狀態");this.unitItem.setState(new AttackingState());}castSkill(): void {console.log(this.unitItem, "單位準備進入釋放技能狀態");this.unitItem.setState(new CastSkillingState());}stun(): void {console.log(this.unitItem, "單位準備進入擊暈狀態");this.unitItem.setState(new StuningState());}die() {console.log(this.unitItem, "單位已經進入死亡狀態");}stopCurrentAction() {console.log(this.unitItem, "單位所有動作停止,因為已死亡");// 如果有正在進行的釋放技能的操作,這里將其中斷// 這可能包括清除技能計時器、動畫等}
}
接著是單位里的
export class UnitItem extends Component implements IItem, IUnitItem {ad: number = 100;mp: number = 0;role: Fighter;private currentState: IUnitState = null;accept(visitor: IAttackVisitor) {visitor.visitUnitItem(this)}setRole(role: Fighter): void {this.role = role;}setState(state: IUnitState) {this.currentState = state;state.enterState(this);}getCurrentState(): IUnitState {if (this.currentState == null) {this.setState(new StandingState())}return this.currentState;}move() {this.getCurrentState().move()}idle() {this.getCurrentState().stand()}attack(unitItem: UnitItem<T>) {if (!this.canAttack()) {// 不能處理攻擊的邏輯,可能是顯示消息或者進入其他狀態return;}// 嘗試進入攻擊狀態this.getCurrentState().attack()let damage = this.adlet attackVisitor = new MonomerAttackVisitor(damage)unitItem.accept(attackVisitor)// 臨時 todo 刪除console.log('假裝本次攻擊帶有擊暈效果')unitItem.getCurrentState().stun()}skill() {if (!this.canSkill()) {// 不能處理攻擊的邏輯,可能是顯示消息或者進入其他狀態return;}// 嘗試進入攻擊狀態this.getCurrentState().castSkill()}die() {this.getCurrentState().die()}private canSkill(): boolean {// 檢查單位是否可以進行技能攻擊// 例如,單位是否處于暈眩狀態或者攻擊是否冷卻中if (this.mp < 100) {console.log('不能處理skill攻擊的邏輯,因為魔法值不足100')return false}if (this.getCurrentState().getType() == UnitStateType.CastSkilling) {console.log('不能處理skill攻擊的邏輯,因為已經處于技能釋放中')return false}if (this.getCurrentState().getType() == UnitStateType.Stuning) {console.log('不能處理skill攻擊的邏輯,因為已經被擊暈')return false}if (this.getCurrentState().getType() == UnitStateType.Dead) {console.log('不能處理skill攻擊的邏輯,因為已經死亡')return false}return true;}private canAttack(): boolean {// 檢查單位是否可以進行攻擊// 例如,單位是否處于暈眩狀態或者攻擊是否冷卻中if (this.getCurrentState().getType() == UnitStateType.Attacking) {console.log('不能處理攻擊的邏輯,因為已經處于攻擊中')return false}if (this.getCurrentState().getType() == UnitStateType.Stuning) {console.log('不能處理攻擊的邏輯,因為已經被擊暈')return false}if (this.getCurrentState().getType() == UnitStateType.Dead) {console.log('不能處理攻擊的邏輯,因為已經死亡')return false}return true;}
}
4、開始使用
let unitItem001 = xhgame.itemFactory.createUnitItem('kuloubing', UnitType.UnitSpine)let unitItem002 = xhgame.itemFactory.createUnitItem('kuloubing', UnitType.UnitSpine)unitItem001.idle()unitItem002.idle()unitItem002.skill()unitItem002.mp = 100;unitItem002.skill()unitItem001.setRole(new Cavalry(new Sword()));console.log('unitItem001(騎兵)準備使用【劍】對unitItem002發起了攻擊')unitItem001.attack(unitItem002)unitItem001.setRole(new Cavalry(new Bow()));console.log('unitItem001(騎兵)準備使用【弓】對unitItem002發起了攻擊')unitItem001.attack(unitItem002)