diff --git a/client/assets/scripts/Framework.meta b/client/assets/scripts/Framework.meta new file mode 100644 index 0000000..aef1aa8 --- /dev/null +++ b/client/assets/scripts/Framework.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "6426d655-54cb-4449-b6e8-1ab2f7707997", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/FSM.meta b/client/assets/scripts/Framework/FSM.meta new file mode 100644 index 0000000..7b540dc --- /dev/null +++ b/client/assets/scripts/Framework/FSM.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "a669701c-71dc-4aa3-9ffe-b9f4a8a884ac", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/FSM/BaseState.ts b/client/assets/scripts/Framework/FSM/BaseState.ts new file mode 100644 index 0000000..7f92751 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/BaseState.ts @@ -0,0 +1,53 @@ +import { IState } from "./IState"; + +/** + * 状态基类 + * 提供状态接口的基础实现 + */ +export abstract class BaseState implements IState { + /** + * 状态所属的状态机 + */ + protected _fsm: any; + + /** + * 状态名称 + */ + public readonly name: string; + + /** + * 构造函数 + * @param fsm 状态所属的状态机 + * @param name 状态名称 + */ + constructor(fsm: any, name: string) { + this._fsm = fsm; + this.name = name; + } + + /** + * 进入状态 + * 子类可以重写此方法来实现自定义逻辑 + * @param params 可选参数 + */ + onEnter(params?: any): void { + console.log(`[FSM] 进入状态: ${this.name}`, params || ''); + } + + /** + * 更新状态 + * 子类可以重写此方法来实现每帧更新逻辑 + * @param dt 距离上一帧的时间增量(秒) + */ + onUpdate?(dt: number): void { + // 子类可选择实现 + } + + /** + * 退出状态 + * 子类可以重写此方法来实现清理逻辑 + */ + onExit(): void { + console.log(`[FSM] 退出状态: ${this.name}`); + } +} diff --git a/client/assets/scripts/Framework/FSM/BaseState.ts.meta b/client/assets/scripts/Framework/FSM/BaseState.ts.meta new file mode 100644 index 0000000..d63b3c0 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/BaseState.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "e27b7261-06e4-49d9-9936-bbf7aa6abacf", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/FSM/FSM.ts b/client/assets/scripts/Framework/FSM/FSM.ts new file mode 100644 index 0000000..8ca772a --- /dev/null +++ b/client/assets/scripts/Framework/FSM/FSM.ts @@ -0,0 +1,139 @@ +import { IState } from "./IState"; + +/** + * 有限状态机 (Finite State Machine) + * 管理状态的添加、移除和切换 + */ +export class FSM { + /** + * 存储所有状态的Map,键为状态名称 + */ + private _states: Map; + + /** + * 当前活动的状态 + */ + private _currentState: IState | null; + + /** + * 构造函数 + */ + constructor() { + this._states = new Map(); + this._currentState = null; + } + + /** + * 添加状态 + * @param state 要添加的状态 + */ + addState(state: IState): void { + if (this._states.has(state.name)) { + console.warn(`[FSM] 状态 "${state.name}" 已存在,将被覆盖`); + } + this._states.set(state.name, state); + console.log(`[FSM] 添加状态: ${state.name}`); + } + + /** + * 移除状态 + * @param stateName 要移除的状态名称 + */ + removeState(stateName: string): void { + if (!this._states.has(stateName)) { + console.warn(`[FSM] 状态 "${stateName}" 不存在,无法移除`); + return; + } + + // 如果当前状态就是要移除的状态,先退出 + if (this._currentState && this._currentState.name === stateName) { + this._currentState.onExit(); + this._currentState = null; + } + + this._states.delete(stateName); + console.log(`[FSM] 移除状态: ${stateName}`); + } + + /** + * 切换状态 + * @param stateName 要切换到的状态名称 + * @param params 可选参数,传递给新状态的onEnter方法 + */ + changeState(stateName: string, params?: any): void { + const newState = this._states.get(stateName); + + if (!newState) { + console.error(`[FSM] 状态 "${stateName}" 不存在,无法切换`); + return; + } + + // 退出当前状态 + if (this._currentState) { + this._currentState.onExit(); + } + + // 切换到新状态 + this._currentState = newState; + this._currentState.onEnter(params); + } + + /** + * 获取当前状态 + * @returns 当前活动的状态,如果没有状态则返回null + */ + getCurrentState(): IState | null { + return this._currentState; + } + + /** + * 获取当前状态名称 + * @returns 当前状态名称,如果没有状态则返回null + */ + getCurrentStateName(): string | null { + return this._currentState ? this._currentState.name : null; + } + + /** + * 检查是否存在指定状态 + * @param stateName 状态名称 + * @returns 如果状态存在返回true,否则返回false + */ + hasState(stateName: string): boolean { + return this._states.has(stateName); + } + + /** + * 获取状态 + * @param stateName 状态名称 + * @returns 状态实例,如果不存在则返回undefined + */ + getState(stateName: string): IState | undefined { + return this._states.get(stateName); + } + + /** + * 更新状态机 + * 如果当前状态实现了onUpdate方法,则调用它 + * @param dt 距离上一帧的时间增量(秒) + */ + update(dt: number): void { + if (this._currentState && this._currentState.onUpdate) { + this._currentState.onUpdate(dt); + } + } + + /** + * 清空所有状态 + */ + clear(): void { + // 退出当前状态 + if (this._currentState) { + this._currentState.onExit(); + this._currentState = null; + } + + this._states.clear(); + console.log('[FSM] 已清空所有状态'); + } +} diff --git a/client/assets/scripts/Framework/FSM/FSM.ts.meta b/client/assets/scripts/Framework/FSM/FSM.ts.meta new file mode 100644 index 0000000..c7dfd38 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/FSM.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "775f2d87-91ed-4af7-bec6-d03bbf83a3eb", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/FSM/FSMExample.ts b/client/assets/scripts/Framework/FSM/FSMExample.ts new file mode 100644 index 0000000..59632b0 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/FSMExample.ts @@ -0,0 +1,194 @@ +import { FSM } from "./FSM"; +import { BaseState } from "./BaseState"; + +/** + * FSM 使用示例 + * 展示如何创建和使用状态机 + */ + +// ========== 示例1: 角色状态机 ========== + +/** + * 待机状态 + */ +class IdleState extends BaseState { + constructor(fsm: FSM) { + super(fsm, "Idle"); + } + + onEnter(params?: any): void { + super.onEnter(params); + console.log("角色进入待机状态"); + } + + onExit(): void { + super.onExit(); + console.log("角色离开待机状态"); + } +} + +/** + * 移动状态 + */ +class MoveState extends BaseState { + private speed: number = 1; + + constructor(fsm: FSM) { + super(fsm, "Move"); + } + + onEnter(params?: any): void { + super.onEnter(params); + if (params && params.speed) { + this.speed = params.speed; + } + console.log(`角色开始移动,速度: ${this.speed}`); + } + + onUpdate(dt: number): void { + console.log(`移动中... 速度: ${this.speed}`); + // 在这里实现移动逻辑 + } + + onExit(): void { + super.onExit(); + console.log("角色停止移动"); + } +} + +/** + * 攻击状态 + */ +class AttackState extends BaseState { + constructor(fsm: FSM) { + super(fsm, "Attack"); + } + + onEnter(params?: any): void { + super.onEnter(params); + console.log("角色进入攻击状态"); + // 播放攻击动画 + // 延迟后自动返回待机状态 + setTimeout(() => { + if (this._fsm.getCurrentStateName() === this.name) { + this._fsm.changeState("Idle"); + } + }, 1000); + } +} + +/** + * 死亡状态 + */ +class DeadState extends BaseState { + constructor(fsm: FSM) { + super(fsm, "Dead"); + } + + onEnter(params?: any): void { + super.onEnter(params); + console.log("角色死亡"); + // 播放死亡动画 + } +} + +// ========== 使用示例 ========== + +/** + * 创建角色状态机 + */ +export function createCharacterFSM(): FSM { + const fsm = new FSM(); + + // 添加所有状态 + fsm.addState(new IdleState(fsm)); + fsm.addState(new MoveState(fsm)); + fsm.addState(new AttackState(fsm)); + fsm.addState(new DeadState(fsm)); + + // 设置初始状态 + fsm.changeState("Idle"); + + return fsm; +} + +/** + * 测试状态机 + */ +export function testFSM(): void { + console.log("========== 开始测试FSM =========="); + + const fsm = createCharacterFSM(); + + // 测试状态切换 + console.log("\n--- 测试1: 从待机到移动 ---"); + fsm.changeState("Move", { speed: 5 }); + + console.log("\n--- 测试2: 从移动到攻击 ---"); + fsm.changeState("Attack"); + + // 等待攻击状态自动结束 + setTimeout(() => { + console.log("\n--- 测试3: 攻击结束后返回待机 ---"); + console.log(`当前状态: ${fsm.getCurrentStateName()}`); + + console.log("\n--- 测试4: 角色死亡 ---"); + fsm.changeState("Dead"); + + console.log("\n========== FSM测试完成 =========="); + }, 1500); +} + +// ========== 示例2: 简单的游戏状态机 ========== + +class MenuState extends BaseState { + constructor(fsm: FSM) { + super(fsm, "Menu"); + } + + onEnter(params?: any): void { + super.onEnter(params); + console.log("显示主菜单"); + } +} + +class PlayingState extends BaseState { + constructor(fsm: FSM) { + super(fsm, "Playing"); + } + + onEnter(params?: any): void { + super.onEnter(params); + console.log("游戏开始"); + } + + onUpdate(dt: number): void { + // 游戏主循环 + } +} + +class PausedState extends BaseState { + constructor(fsm: FSM) { + super(fsm, "Paused"); + } + + onEnter(params?: any): void { + super.onEnter(params); + console.log("游戏暂停"); + } +} + +/** + * 创建游戏状态机 + */ +export function createGameFSM(): FSM { + const fsm = new FSM(); + + fsm.addState(new MenuState(fsm)); + fsm.addState(new PlayingState(fsm)); + fsm.addState(new PausedState(fsm)); + + fsm.changeState("Menu"); + + return fsm; +} diff --git a/client/assets/scripts/Framework/FSM/FSMExample.ts.meta b/client/assets/scripts/Framework/FSM/FSMExample.ts.meta new file mode 100644 index 0000000..f2e2629 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/FSMExample.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "d4b05ea7-41dd-4f10-a5df-da10f91aa330", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/FSM/IState.ts b/client/assets/scripts/Framework/FSM/IState.ts new file mode 100644 index 0000000..29256c6 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/IState.ts @@ -0,0 +1,30 @@ +/** + * 状态接口 + * 定义状态机中状态的标准接口 + */ +export interface IState { + /** + * 状态名称 + * 用于标识和切换状态 + */ + readonly name: string; + + /** + * 进入状态时调用 + * @param params 可选参数,用于传递状态切换时的数据 + */ + onEnter(params?: any): void; + + /** + * 更新状态(可选) + * 如果状态需要每帧更新,可以实现此方法 + * @param dt 距离上一帧的时间增量(秒) + */ + onUpdate?(dt: number): void; + + /** + * 退出状态时调用 + * 用于清理状态相关的资源 + */ + onExit(): void; +} diff --git a/client/assets/scripts/Framework/FSM/IState.ts.meta b/client/assets/scripts/Framework/FSM/IState.ts.meta new file mode 100644 index 0000000..c96c012 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/IState.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.24", + "importer": "typescript", + "imported": true, + "uuid": "e1f22ce5-2a7d-440c-8532-cece7a66a2bc", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/client/assets/scripts/Framework/FSM/README.md b/client/assets/scripts/Framework/FSM/README.md new file mode 100644 index 0000000..06eca30 --- /dev/null +++ b/client/assets/scripts/Framework/FSM/README.md @@ -0,0 +1,396 @@ +# 状态机模块 (Framework/FSM) + +## 📋 模块概述 +通用的有限状态机(FSM)框架,支持状态切换、状态生命周期管理,可用于游戏状态、AI行为、动画控制等场景。 + +## 🎯 核心特性 +- ✅ 标准状态接口定义 +- ✅ 状态基类实现 +- ✅ 状态机核心管理 +- ✅ 完整的生命周期 +- ✅ 状态切换管理 +- ✅ 状态更新机制 + +## 🗂️ 文件结构 + +``` +Framework/FSM/ +├── IState.ts # 状态接口 +├── BaseState.ts # 状态基类 +└── FSM.ts # 状态机核心 +``` + +## 📘 核心类详解 + +### IState - 状态接口 + +**职责**: 定义状态机中状态的标准接口 + +```typescript +interface IState { + // 状态名称(只读) + readonly name: string; + + // 进入状态时调用 + onEnter(params?: any): void; + + // 更新状态(可选,每帧调用) + onUpdate?(dt: number): void; + + // 退出状态时调用 + onExit(): void; +} +``` + +### BaseState - 状态基类 + +**职责**: 提供状态接口的基础实现 + +```typescript +abstract class BaseState implements IState { + protected _fsm: FSM; // 状态所属的状态机 + readonly name: string; // 状态名称 + + constructor(fsm: FSM, name: string); + + // 进入状态(可重写) + onEnter(params?: any): void { + console.log(`[FSM] Enter state: ${this.name}`); + } + + // 更新状态(可重写) + onUpdate?(dt: number): void {} + + // 退出状态(可重写) + onExit(): void { + console.log(`[FSM] Exit state: ${this.name}`); + } +} +``` + +### FSM - 状态机核心 + +**职责**: 管理状态的添加、移除和切换 + +```typescript +class FSM { + private _states: Map; // 状态集合 + private _currentState: IState | null; // 当前状态 + + // 添加状态 + addState(state: IState): void; + + // 移除状态 + removeState(stateName: string): void; + + // 切换状态 + changeState(stateName: string, params?: any): void; + + // 获取当前状态 + getCurrentState(): IState | null; + + // 获取当前状态名称 + getCurrentStateName(): string | null; + + // 检查状态是否存在 + hasState(stateName: string): boolean; + + // 获取状态实例 + getState(stateName: string): IState | undefined; + + // 更新状态机(调用当前状态的 onUpdate) + update(dt: number): void; + + // 清空所有状态 + clear(): void; +} +``` + +## 📝 使用指南 + +### 1. 定义状态类 + +```typescript +import { FSM } from './Framework/FSM/FSM'; +import { BaseState } from './Framework/FSM/BaseState'; + +// 待机状态 +class IdleState extends BaseState { + constructor(fsm: FSM) { + super(fsm, "Idle"); + } + + onEnter(params?: any): void { + super.onEnter(params); + console.log("角色进入待机状态"); + } + + onUpdate(dt: number): void { + // 检测输入,切换到移动状态 + if (hasInput()) { + this._fsm.changeState("Move"); + } + } +} + +// 移动状态 +class MoveState extends BaseState { + private _speed: number = 5; + + constructor(fsm: FSM) { + super(fsm, "Move"); + } + + onEnter(params?: any): void { + super.onEnter(params); + if (params?.speed) { + this._speed = params.speed; + } + console.log(`角色开始移动,速度: ${this._speed}`); + } + + onUpdate(dt: number): void { + // 执行移动逻辑 + this.move(dt); + + // 检测停止输入 + if (!hasInput()) { + this._fsm.changeState("Idle"); + } + } + + onExit(): void { + super.onExit(); + console.log("角色停止移动"); + } + + private move(dt: number): void { + // 移动逻辑 + } +} +``` + +### 2. 创建并使用状态机 + +```typescript +import { FSM } from './Framework/FSM/FSM'; + +// 1. 创建状态机 +const fsm = new FSM(); + +// 2. 添加状态 +fsm.addState(new IdleState(fsm)); +fsm.addState(new MoveState(fsm)); +fsm.addState(new AttackState(fsm)); + +// 3. 切换到初始状态 +fsm.changeState("Idle"); + +// 4. 在游戏主循环中更新 +function update(dt: number) { + fsm.update(dt); +} + +// 5. 切换状态(可传递参数) +fsm.changeState("Move", { speed: 10 }); +``` + +### 3. 角色 AI 示例 + +```typescript +// 敌人 AI 状态机 +class EnemyAI { + private _fsm: FSM; + + constructor() { + this._fsm = new FSM(); + + // 添加 AI 状态 + this._fsm.addState(new PatrolState(this._fsm)); + this._fsm.addState(new ChaseState(this._fsm)); + this._fsm.addState(new AttackState(this._fsm)); + this._fsm.addState(new FleeState(this._fsm)); + + // 从巡逻状态开始 + this._fsm.changeState("Patrol"); + } + + update(dt: number): void { + this._fsm.update(dt); + } +} + +// 巡逻状态 +class PatrolState extends BaseState { + onEnter(params?: any): void { + super.onEnter(params); + console.log("敌人开始巡逻"); + } + + onUpdate(dt: number): void { + // 检测玩家 + if (detectPlayer()) { + this._fsm.changeState("Chase"); + } + } +} + +// 追击状态 +class ChaseState extends BaseState { + onEnter(params?: any): void { + super.onEnter(params); + console.log("敌人发现玩家,开始追击"); + } + + onUpdate(dt: number): void { + // 靠近玩家 + moveToPlayer(); + + // 进入攻击范围 + if (inAttackRange()) { + this._fsm.changeState("Attack"); + } + // 玩家逃离 + else if (!canSeePlayer()) { + this._fsm.changeState("Patrol"); + } + } +} +``` + +### 4. 动画状态机示例 + +```typescript +// 角色动画状态机 +class CharacterAnimFSM { + private _fsm: FSM; + private _animator: any; // Cocos 动画组件 + + constructor(animator: any) { + this._animator = animator; + this._fsm = new FSM(); + + // 添加动画状态 + this._fsm.addState(new IdleAnimState(this._fsm, this._animator)); + this._fsm.addState(new RunAnimState(this._fsm, this._animator)); + this._fsm.addState(new JumpAnimState(this._fsm, this._animator)); + this._fsm.addState(new AttackAnimState(this._fsm, this._animator)); + + this._fsm.changeState("Idle"); + } + + // 播放动画 + play(animName: string, params?: any): void { + this._fsm.changeState(animName, params); + } +} + +// 待机动画状态 +class IdleAnimState extends BaseState { + private _animator: any; + + constructor(fsm: FSM, animator: any) { + super(fsm, "Idle"); + this._animator = animator; + } + + onEnter(params?: any): void { + super.onEnter(params); + this._animator.play("idle"); + } +} +``` + +## 🔄 状态切换流程 + +``` +changeState("NewState", params) + ↓ +1. 检查新状态是否存在 + ↓ +2. 如果有当前状态,调用 currentState.onExit() + ↓ +3. 切换到新状态 + ↓ +4. 调用 newState.onEnter(params) + ↓ +5. 更新当前状态引用 +``` + +## 📊 状态生命周期 + +``` +[创建状态实例] + ↓ +addState(state) - 添加到状态机 + ↓ +changeState("StateName") - 切换状态 + ↓ +onEnter(params) - 进入状态 + ↓ +onUpdate(dt) - 每帧更新(如果实现) + ↓ +[触发状态切换] + ↓ +onExit() - 退出状态 + ↓ +removeState("StateName") - 从状态机移除(可选) +``` + +## ⚠️ 注意事项 + +1. **状态名称唯一**: 每个状态的名称必须唯一,重复添加会覆盖 +2. **构造函数传递 FSM**: 状态类的构造函数需要接收 FSM 实例 +3. **调用 super**: 重写生命周期方法时,建议先调用 `super.xxx()` +4. **状态切换时机**: 建议在 `onUpdate()` 中判断并切换状态 +5. **参数传递**: 切换状态时可以通过 `params` 传递数据 +6. **避免循环切换**: 注意状态切换逻辑,避免无限循环 + +## 🔍 调试技巧 + +### 启用状态日志 + +```typescript +// BaseState 默认会输出状态切换日志 +// [FSM] Enter state: Idle +// [FSM] Exit state: Idle +``` + +### 获取当前状态信息 + +```typescript +// 获取当前状态名称 +const stateName = fsm.getCurrentStateName(); +console.log(`当前状态: ${stateName}`); + +// 获取当前状态实例 +const state = fsm.getCurrentState(); +if (state) { + console.log(`状态实例: ${state.name}`); +} +``` + +### 检查状态是否存在 + +```typescript +if (fsm.hasState("Attack")) { + console.log("Attack 状态已添加"); +} +``` + +## 💡 最佳实践 + +1. **单一职责**: 每个状态只负责一种行为 +2. **状态解耦**: 状态之间通过 FSM 切换,避免直接依赖 +3. **参数传递**: 使用 `params` 在状态间传递数据 +4. **及时清理**: 不再使用的状态及时移除 +5. **文档注释**: 为每个状态类添加清晰的职责说明 + +## 📚 应用场景 + +- ✅ 游戏状态管理(Boot、Login、Game 等) +- ✅ 角色状态控制(Idle、Move、Attack 等) +- ✅ 敌人 AI 行为(Patrol、Chase、Attack、Flee 等) +- ✅ 动画状态机(各种动画之间的切换) +- ✅ UI 界面流转(各个界面之间的切换) +- ✅ 关卡状态(Ready、Playing、Pause、GameOver 等)