refactor: reorganize package structure and decouple framework packages (#338)
* refactor: reorganize package structure and decouple framework packages ## Package Structure Reorganization - Reorganized 55 packages into categorized subdirectories: - packages/framework/ - Generic framework (Laya/Cocos compatible) - packages/engine/ - ESEngine core modules - packages/rendering/ - Rendering modules (WASM dependent) - packages/physics/ - Physics modules - packages/streaming/ - World streaming - packages/network-ext/ - Network extensions - packages/editor/ - Editor framework and plugins - packages/rust/ - Rust WASM engine - packages/tools/ - Build tools and SDK ## Framework Package Decoupling - Decoupled behavior-tree and blueprint packages from ESEngine dependencies - Created abstracted interfaces (IBTAssetManager, IBehaviorTreeAssetContent) - ESEngine-specific code moved to esengine/ subpath exports - Framework packages now usable with Cocos/Laya without ESEngine ## CI Configuration - Updated CI to only type-check and lint framework packages - Added type-check:framework and lint:framework scripts ## Breaking Changes - Package import paths changed due to directory reorganization - ESEngine integrations now use subpath imports (e.g., '@esengine/behavior-tree/esengine') * fix: update es-engine file path after directory reorganization * docs: update README to focus on framework over engine * ci: only build framework packages, remove Rust/WASM dependencies * fix: remove esengine subpath from behavior-tree and blueprint builds ESEngine integration code will only be available in full engine builds. Framework packages are now purely engine-agnostic. * fix: move network-protocols to framework, build both in CI * fix: update workflow paths from packages/core to packages/framework/core * fix: exclude esengine folder from type-check in behavior-tree and blueprint * fix: update network tsconfig references to new paths * fix: add test:ci:framework to only test framework packages in CI * fix: only build core and math npm packages in CI * fix: exclude test files from CodeQL and fix string escaping security issue
This commit is contained in:
364
packages/framework/fsm/src/IStateMachine.ts
Normal file
364
packages/framework/fsm/src/IStateMachine.ts
Normal file
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
* @zh 状态机接口
|
||||
* @en State Machine Interface
|
||||
*
|
||||
* @zh 提供有限状态机的核心接口
|
||||
* @en Provides core interfaces for finite state machine
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 状态配置 | State Configuration
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 状态配置
|
||||
* @en State configuration
|
||||
*/
|
||||
export interface StateConfig<TState extends string = string, TContext = unknown> {
|
||||
/**
|
||||
* @zh 状态名称
|
||||
* @en State name
|
||||
*/
|
||||
readonly name: TState;
|
||||
|
||||
/**
|
||||
* @zh 进入状态时的回调
|
||||
* @en Callback when entering state
|
||||
*/
|
||||
onEnter?: (context: TContext, from: TState | null) => void;
|
||||
|
||||
/**
|
||||
* @zh 退出状态时的回调
|
||||
* @en Callback when exiting state
|
||||
*/
|
||||
onExit?: (context: TContext, to: TState) => void;
|
||||
|
||||
/**
|
||||
* @zh 状态更新回调
|
||||
* @en State update callback
|
||||
*/
|
||||
onUpdate?: (context: TContext, deltaTime: number) => void;
|
||||
|
||||
/**
|
||||
* @zh 状态标签
|
||||
* @en State tags
|
||||
*/
|
||||
tags?: string[];
|
||||
|
||||
/**
|
||||
* @zh 状态元数据
|
||||
* @en State metadata
|
||||
*/
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 转换配置 | Transition Configuration
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 转换条件函数
|
||||
* @en Transition condition function
|
||||
*/
|
||||
export type TransitionCondition<TContext = unknown> = (context: TContext) => boolean;
|
||||
|
||||
/**
|
||||
* @zh 转换配置
|
||||
* @en Transition configuration
|
||||
*/
|
||||
export interface TransitionConfig<TState extends string = string, TContext = unknown> {
|
||||
/**
|
||||
* @zh 源状态
|
||||
* @en Source state
|
||||
*/
|
||||
readonly from: TState;
|
||||
|
||||
/**
|
||||
* @zh 目标状态
|
||||
* @en Target state
|
||||
*/
|
||||
readonly to: TState;
|
||||
|
||||
/**
|
||||
* @zh 转换条件
|
||||
* @en Transition condition
|
||||
*/
|
||||
condition?: TransitionCondition<TContext>;
|
||||
|
||||
/**
|
||||
* @zh 转换优先级(数字越大优先级越高)
|
||||
* @en Transition priority (higher number = higher priority)
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* @zh 转换名称(用于调试)
|
||||
* @en Transition name (for debugging)
|
||||
*/
|
||||
name?: string;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 状态机事件 | State Machine Events
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 状态变更事件
|
||||
* @en State change event
|
||||
*/
|
||||
export interface StateChangeEvent<TState extends string = string> {
|
||||
/**
|
||||
* @zh 之前的状态
|
||||
* @en Previous state
|
||||
*/
|
||||
readonly from: TState | null;
|
||||
|
||||
/**
|
||||
* @zh 当前状态
|
||||
* @en Current state
|
||||
*/
|
||||
readonly to: TState;
|
||||
|
||||
/**
|
||||
* @zh 时间戳
|
||||
* @en Timestamp
|
||||
*/
|
||||
readonly timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 状态变更监听器
|
||||
* @en State change listener
|
||||
*/
|
||||
export type StateChangeListener<TState extends string = string> = (
|
||||
event: StateChangeEvent<TState>
|
||||
) => void;
|
||||
|
||||
// =============================================================================
|
||||
// 状态机接口 | State Machine Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 状态机接口
|
||||
* @en State machine interface
|
||||
*
|
||||
* @zh 通用有限状态机,用于角色/AI 状态管理
|
||||
* @en Generic finite state machine for character/AI state management
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* type PlayerState = 'idle' | 'walk' | 'run' | 'jump' | 'attack';
|
||||
*
|
||||
* const fsm = createStateMachine<PlayerState>('idle');
|
||||
*
|
||||
* fsm.defineState('idle', {
|
||||
* onEnter: () => console.log('Entering idle'),
|
||||
* onExit: () => console.log('Exiting idle')
|
||||
* });
|
||||
*
|
||||
* fsm.defineTransition('idle', 'walk', () => isMoving);
|
||||
* fsm.defineTransition('walk', 'run', () => isRunning);
|
||||
*
|
||||
* fsm.transition('walk'); // 手动转换
|
||||
* fsm.evaluateTransitions(); // 自动评估条件
|
||||
* ```
|
||||
*/
|
||||
export interface IStateMachine<TState extends string = string, TContext = unknown> {
|
||||
/**
|
||||
* @zh 当前状态
|
||||
* @en Current state
|
||||
*/
|
||||
readonly current: TState;
|
||||
|
||||
/**
|
||||
* @zh 之前的状态
|
||||
* @en Previous state
|
||||
*/
|
||||
readonly previous: TState | null;
|
||||
|
||||
/**
|
||||
* @zh 状态机上下文
|
||||
* @en State machine context
|
||||
*/
|
||||
readonly context: TContext;
|
||||
|
||||
/**
|
||||
* @zh 是否正在转换中
|
||||
* @en Whether a transition is in progress
|
||||
*/
|
||||
readonly isTransitioning: boolean;
|
||||
|
||||
/**
|
||||
* @zh 当前状态持续时间(毫秒)
|
||||
* @en Current state duration in milliseconds
|
||||
*/
|
||||
readonly currentStateDuration: number;
|
||||
|
||||
// =========================================================================
|
||||
// 状态定义 | State Definition
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 定义状态
|
||||
* @en Define state
|
||||
*
|
||||
* @param state - @zh 状态名称 @en State name
|
||||
* @param config - @zh 状态配置 @en State configuration
|
||||
*/
|
||||
defineState(state: TState, config?: Partial<StateConfig<TState, TContext>>): void;
|
||||
|
||||
/**
|
||||
* @zh 检查状态是否已定义
|
||||
* @en Check if state is defined
|
||||
*
|
||||
* @param state - @zh 状态名称 @en State name
|
||||
*/
|
||||
hasState(state: TState): boolean;
|
||||
|
||||
/**
|
||||
* @zh 获取状态配置
|
||||
* @en Get state configuration
|
||||
*
|
||||
* @param state - @zh 状态名称 @en State name
|
||||
*/
|
||||
getStateConfig(state: TState): StateConfig<TState, TContext> | undefined;
|
||||
|
||||
/**
|
||||
* @zh 获取所有定义的状态
|
||||
* @en Get all defined states
|
||||
*/
|
||||
getStates(): TState[];
|
||||
|
||||
// =========================================================================
|
||||
// 转换定义 | Transition Definition
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 定义转换
|
||||
* @en Define transition
|
||||
*
|
||||
* @param from - @zh 源状态 @en Source state
|
||||
* @param to - @zh 目标状态 @en Target state
|
||||
* @param condition - @zh 转换条件 @en Transition condition
|
||||
* @param priority - @zh 优先级 @en Priority
|
||||
*/
|
||||
defineTransition(
|
||||
from: TState,
|
||||
to: TState,
|
||||
condition?: TransitionCondition<TContext>,
|
||||
priority?: number
|
||||
): void;
|
||||
|
||||
/**
|
||||
* @zh 移除转换
|
||||
* @en Remove transition
|
||||
*
|
||||
* @param from - @zh 源状态 @en Source state
|
||||
* @param to - @zh 目标状态 @en Target state
|
||||
*/
|
||||
removeTransition(from: TState, to: TState): void;
|
||||
|
||||
/**
|
||||
* @zh 获取从指定状态可用的转换
|
||||
* @en Get available transitions from state
|
||||
*
|
||||
* @param from - @zh 源状态 @en Source state
|
||||
*/
|
||||
getTransitionsFrom(from: TState): TransitionConfig<TState, TContext>[];
|
||||
|
||||
// =========================================================================
|
||||
// 转换操作 | Transition Operations
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 检查是否可以转换到目标状态
|
||||
* @en Check if can transition to target state
|
||||
*
|
||||
* @param to - @zh 目标状态 @en Target state
|
||||
*/
|
||||
canTransition(to: TState): boolean;
|
||||
|
||||
/**
|
||||
* @zh 转换到目标状态
|
||||
* @en Transition to target state
|
||||
*
|
||||
* @param to - @zh 目标状态 @en Target state
|
||||
* @param force - @zh 强制转换(忽略条件)@en Force transition (ignore condition)
|
||||
* @returns @zh 是否成功 @en Whether successful
|
||||
*/
|
||||
transition(to: TState, force?: boolean): boolean;
|
||||
|
||||
/**
|
||||
* @zh 评估并执行满足条件的转换
|
||||
* @en Evaluate and execute transitions that meet conditions
|
||||
*
|
||||
* @returns @zh 是否发生转换 @en Whether a transition occurred
|
||||
*/
|
||||
evaluateTransitions(): boolean;
|
||||
|
||||
// =========================================================================
|
||||
// 生命周期 | Lifecycle
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 更新状态机
|
||||
* @en Update state machine
|
||||
*
|
||||
* @param deltaTime - @zh 增量时间(毫秒)@en Delta time in milliseconds
|
||||
*/
|
||||
update(deltaTime: number): void;
|
||||
|
||||
/**
|
||||
* @zh 重置状态机到初始状态
|
||||
* @en Reset state machine to initial state
|
||||
*
|
||||
* @param initialState - @zh 初始状态 @en Initial state
|
||||
*/
|
||||
reset(initialState?: TState): void;
|
||||
|
||||
// =========================================================================
|
||||
// 事件监听 | Event Listening
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 监听状态进入事件
|
||||
* @en Listen to state enter event
|
||||
*
|
||||
* @param state - @zh 状态名称 @en State name
|
||||
* @param callback - @zh 回调函数 @en Callback function
|
||||
*/
|
||||
onEnter(state: TState, callback: (from: TState | null) => void): () => void;
|
||||
|
||||
/**
|
||||
* @zh 监听状态退出事件
|
||||
* @en Listen to state exit event
|
||||
*
|
||||
* @param state - @zh 状态名称 @en State name
|
||||
* @param callback - @zh 回调函数 @en Callback function
|
||||
*/
|
||||
onExit(state: TState, callback: (to: TState) => void): () => void;
|
||||
|
||||
/**
|
||||
* @zh 监听任意状态变更
|
||||
* @en Listen to any state change
|
||||
*
|
||||
* @param callback - @zh 回调函数 @en Callback function
|
||||
*/
|
||||
onChange(callback: StateChangeListener<TState>): () => void;
|
||||
|
||||
// =========================================================================
|
||||
// 调试 | Debug
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* @zh 获取状态历史
|
||||
* @en Get state history
|
||||
*/
|
||||
getHistory(): StateChangeEvent<TState>[];
|
||||
|
||||
/**
|
||||
* @zh 清除历史
|
||||
* @en Clear history
|
||||
*/
|
||||
clearHistory(): void;
|
||||
}
|
||||
445
packages/framework/fsm/src/StateMachine.ts
Normal file
445
packages/framework/fsm/src/StateMachine.ts
Normal file
@@ -0,0 +1,445 @@
|
||||
/**
|
||||
* @zh 状态机实现
|
||||
* @en State Machine Implementation
|
||||
*
|
||||
* @zh 提供有限状态机的默认实现
|
||||
* @en Provides default implementation for finite state machine
|
||||
*/
|
||||
|
||||
import type {
|
||||
IStateMachine,
|
||||
StateConfig,
|
||||
TransitionConfig,
|
||||
TransitionCondition,
|
||||
StateChangeEvent,
|
||||
StateChangeListener
|
||||
} from './IStateMachine';
|
||||
|
||||
// =============================================================================
|
||||
// 状态机配置 | State Machine Configuration
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 状态机配置选项
|
||||
* @en State machine configuration options
|
||||
*/
|
||||
export interface StateMachineOptions<TContext = unknown> {
|
||||
/**
|
||||
* @zh 上下文对象
|
||||
* @en Context object
|
||||
*/
|
||||
context?: TContext;
|
||||
|
||||
/**
|
||||
* @zh 最大历史记录数量
|
||||
* @en Maximum history size
|
||||
*/
|
||||
maxHistorySize?: number;
|
||||
|
||||
/**
|
||||
* @zh 是否启用历史记录
|
||||
* @en Whether to enable history
|
||||
*/
|
||||
enableHistory?: boolean;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 状态机实现 | State Machine Implementation
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 状态机实现
|
||||
* @en State machine implementation
|
||||
*/
|
||||
export class StateMachine<TState extends string = string, TContext = unknown>
|
||||
implements IStateMachine<TState, TContext>
|
||||
{
|
||||
private _current: TState;
|
||||
private _previous: TState | null = null;
|
||||
private _context: TContext;
|
||||
private _isTransitioning = false;
|
||||
private _stateStartTime = 0;
|
||||
|
||||
private states: Map<TState, StateConfig<TState, TContext>> = new Map();
|
||||
private transitions: Map<TState, TransitionConfig<TState, TContext>[]> = new Map();
|
||||
|
||||
private enterListeners: Map<TState, Set<(from: TState | null) => void>> = new Map();
|
||||
private exitListeners: Map<TState, Set<(to: TState) => void>> = new Map();
|
||||
private changeListeners: Set<StateChangeListener<TState>> = new Set();
|
||||
|
||||
private history: StateChangeEvent<TState>[] = [];
|
||||
private maxHistorySize: number;
|
||||
private enableHistory: boolean;
|
||||
|
||||
constructor(initialState: TState, options: StateMachineOptions<TContext> = {}) {
|
||||
this._current = initialState;
|
||||
this._context = (options.context ?? {}) as TContext;
|
||||
this.maxHistorySize = options.maxHistorySize ?? 100;
|
||||
this.enableHistory = options.enableHistory ?? true;
|
||||
this._stateStartTime = Date.now();
|
||||
|
||||
// Auto-define initial state if not defined
|
||||
this.defineState(initialState);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 属性 | Properties
|
||||
// =========================================================================
|
||||
|
||||
get current(): TState {
|
||||
return this._current;
|
||||
}
|
||||
|
||||
get previous(): TState | null {
|
||||
return this._previous;
|
||||
}
|
||||
|
||||
get context(): TContext {
|
||||
return this._context;
|
||||
}
|
||||
|
||||
get isTransitioning(): boolean {
|
||||
return this._isTransitioning;
|
||||
}
|
||||
|
||||
get currentStateDuration(): number {
|
||||
return Date.now() - this._stateStartTime;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 状态定义 | State Definition
|
||||
// =========================================================================
|
||||
|
||||
defineState(state: TState, config?: Partial<StateConfig<TState, TContext>>): void {
|
||||
const stateConfig: StateConfig<TState, TContext> = {
|
||||
name: state,
|
||||
...config
|
||||
};
|
||||
this.states.set(state, stateConfig);
|
||||
}
|
||||
|
||||
hasState(state: TState): boolean {
|
||||
return this.states.has(state);
|
||||
}
|
||||
|
||||
getStateConfig(state: TState): StateConfig<TState, TContext> | undefined {
|
||||
return this.states.get(state);
|
||||
}
|
||||
|
||||
getStates(): TState[] {
|
||||
return Array.from(this.states.keys());
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 转换定义 | Transition Definition
|
||||
// =========================================================================
|
||||
|
||||
defineTransition(
|
||||
from: TState,
|
||||
to: TState,
|
||||
condition?: TransitionCondition<TContext>,
|
||||
priority = 0
|
||||
): void {
|
||||
if (!this.transitions.has(from)) {
|
||||
this.transitions.set(from, []);
|
||||
}
|
||||
|
||||
const transitions = this.transitions.get(from)!;
|
||||
|
||||
// Remove existing transition with same from/to
|
||||
const existingIndex = transitions.findIndex(t => t.to === to);
|
||||
if (existingIndex >= 0) {
|
||||
transitions.splice(existingIndex, 1);
|
||||
}
|
||||
|
||||
transitions.push({
|
||||
from,
|
||||
to,
|
||||
condition,
|
||||
priority
|
||||
});
|
||||
|
||||
// Sort by priority (descending)
|
||||
transitions.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
||||
}
|
||||
|
||||
removeTransition(from: TState, to: TState): void {
|
||||
const transitions = this.transitions.get(from);
|
||||
if (transitions) {
|
||||
const index = transitions.findIndex(t => t.to === to);
|
||||
if (index >= 0) {
|
||||
transitions.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getTransitionsFrom(from: TState): TransitionConfig<TState, TContext>[] {
|
||||
return this.transitions.get(from) ?? [];
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 转换操作 | Transition Operations
|
||||
// =========================================================================
|
||||
|
||||
canTransition(to: TState): boolean {
|
||||
if (this._isTransitioning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._current === to) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const transitions = this.transitions.get(this._current);
|
||||
if (!transitions) {
|
||||
return true; // Allow if no restrictions defined
|
||||
}
|
||||
|
||||
const transition = transitions.find(t => t.to === to);
|
||||
if (!transition) {
|
||||
return true; // Allow if no specific transition defined
|
||||
}
|
||||
|
||||
if (transition.condition) {
|
||||
return transition.condition(this._context);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
transition(to: TState, force = false): boolean {
|
||||
if (this._isTransitioning) {
|
||||
console.warn('StateMachine: Cannot transition while already transitioning');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._current === to) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!force && !this.canTransition(to)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.performTransition(to);
|
||||
return true;
|
||||
}
|
||||
|
||||
evaluateTransitions(): boolean {
|
||||
if (this._isTransitioning) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const transitions = this.transitions.get(this._current);
|
||||
if (!transitions || transitions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const transition of transitions) {
|
||||
if (!transition.condition || transition.condition(this._context)) {
|
||||
this.performTransition(transition.to);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private performTransition(to: TState): void {
|
||||
this._isTransitioning = true;
|
||||
|
||||
const from = this._current;
|
||||
|
||||
// Exit current state
|
||||
const currentConfig = this.states.get(from);
|
||||
if (currentConfig?.onExit) {
|
||||
try {
|
||||
currentConfig.onExit(this._context, to);
|
||||
} catch (error) {
|
||||
console.error(`StateMachine: Error in onExit for state '${from}':`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify exit listeners
|
||||
const exitListeners = this.exitListeners.get(from);
|
||||
if (exitListeners) {
|
||||
for (const listener of exitListeners) {
|
||||
try {
|
||||
listener(to);
|
||||
} catch (error) {
|
||||
console.error(`StateMachine: Error in exit listener for state '${from}':`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update state
|
||||
this._previous = from;
|
||||
this._current = to;
|
||||
this._stateStartTime = Date.now();
|
||||
|
||||
// Record history
|
||||
if (this.enableHistory) {
|
||||
const event: StateChangeEvent<TState> = {
|
||||
from,
|
||||
to,
|
||||
timestamp: this._stateStartTime
|
||||
};
|
||||
this.history.push(event);
|
||||
|
||||
if (this.history.length > this.maxHistorySize) {
|
||||
this.history.shift();
|
||||
}
|
||||
|
||||
// Notify change listeners
|
||||
for (const listener of this.changeListeners) {
|
||||
try {
|
||||
listener(event);
|
||||
} catch (error) {
|
||||
console.error('StateMachine: Error in change listener:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enter new state
|
||||
const newConfig = this.states.get(to);
|
||||
if (newConfig?.onEnter) {
|
||||
try {
|
||||
newConfig.onEnter(this._context, from);
|
||||
} catch (error) {
|
||||
console.error(`StateMachine: Error in onEnter for state '${to}':`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify enter listeners
|
||||
const enterListeners = this.enterListeners.get(to);
|
||||
if (enterListeners) {
|
||||
for (const listener of enterListeners) {
|
||||
try {
|
||||
listener(from);
|
||||
} catch (error) {
|
||||
console.error(`StateMachine: Error in enter listener for state '${to}':`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._isTransitioning = false;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 生命周期 | Lifecycle
|
||||
// =========================================================================
|
||||
|
||||
update(deltaTime: number): void {
|
||||
const config = this.states.get(this._current);
|
||||
if (config?.onUpdate) {
|
||||
try {
|
||||
config.onUpdate(this._context, deltaTime);
|
||||
} catch (error) {
|
||||
console.error(`StateMachine: Error in onUpdate for state '${this._current}':`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reset(initialState?: TState): void {
|
||||
const targetState = initialState ?? this._current;
|
||||
|
||||
this._previous = null;
|
||||
this._current = targetState;
|
||||
this._stateStartTime = Date.now();
|
||||
this._isTransitioning = false;
|
||||
|
||||
this.clearHistory();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 事件监听 | Event Listening
|
||||
// =========================================================================
|
||||
|
||||
onEnter(state: TState, callback: (from: TState | null) => void): () => void {
|
||||
if (!this.enterListeners.has(state)) {
|
||||
this.enterListeners.set(state, new Set());
|
||||
}
|
||||
this.enterListeners.get(state)!.add(callback);
|
||||
|
||||
return () => {
|
||||
this.enterListeners.get(state)?.delete(callback);
|
||||
};
|
||||
}
|
||||
|
||||
onExit(state: TState, callback: (to: TState) => void): () => void {
|
||||
if (!this.exitListeners.has(state)) {
|
||||
this.exitListeners.set(state, new Set());
|
||||
}
|
||||
this.exitListeners.get(state)!.add(callback);
|
||||
|
||||
return () => {
|
||||
this.exitListeners.get(state)?.delete(callback);
|
||||
};
|
||||
}
|
||||
|
||||
onChange(callback: StateChangeListener<TState>): () => void {
|
||||
this.changeListeners.add(callback);
|
||||
|
||||
return () => {
|
||||
this.changeListeners.delete(callback);
|
||||
};
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// 调试 | Debug
|
||||
// =========================================================================
|
||||
|
||||
getHistory(): StateChangeEvent<TState>[] {
|
||||
return [...this.history];
|
||||
}
|
||||
|
||||
clearHistory(): void {
|
||||
this.history = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 获取状态机的调试信息
|
||||
* @en Get debug info for the state machine
|
||||
*/
|
||||
getDebugInfo(): {
|
||||
current: TState;
|
||||
previous: TState | null;
|
||||
duration: number;
|
||||
stateCount: number;
|
||||
transitionCount: number;
|
||||
historySize: number;
|
||||
} {
|
||||
let transitionCount = 0;
|
||||
for (const transitions of this.transitions.values()) {
|
||||
transitionCount += transitions.length;
|
||||
}
|
||||
|
||||
return {
|
||||
current: this._current,
|
||||
previous: this._previous,
|
||||
duration: this.currentStateDuration,
|
||||
stateCount: this.states.size,
|
||||
transitionCount,
|
||||
historySize: this.history.length
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 工厂函数 | Factory Functions
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 创建状态机
|
||||
* @en Create state machine
|
||||
*
|
||||
* @param initialState - @zh 初始状态 @en Initial state
|
||||
* @param options - @zh 配置选项 @en Configuration options
|
||||
* @returns @zh 状态机实例 @en State machine instance
|
||||
*/
|
||||
export function createStateMachine<TState extends string = string, TContext = unknown>(
|
||||
initialState: TState,
|
||||
options?: StateMachineOptions<TContext>
|
||||
): IStateMachine<TState, TContext> {
|
||||
return new StateMachine<TState, TContext>(initialState, options);
|
||||
}
|
||||
60
packages/framework/fsm/src/index.ts
Normal file
60
packages/framework/fsm/src/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* @zh @esengine/fsm - 有限状态机
|
||||
* @en @esengine/fsm - Finite State Machine
|
||||
*
|
||||
* @zh 提供通用状态机功能,用于角色/AI 状态管理
|
||||
* @en Provides generic state machine for character/AI state management
|
||||
*/
|
||||
|
||||
// =============================================================================
|
||||
// 接口和类型 | Interfaces and Types
|
||||
// =============================================================================
|
||||
|
||||
export type {
|
||||
StateConfig,
|
||||
TransitionConfig,
|
||||
TransitionCondition,
|
||||
StateChangeEvent,
|
||||
StateChangeListener,
|
||||
IStateMachine
|
||||
} from './IStateMachine';
|
||||
|
||||
// =============================================================================
|
||||
// 实现 | Implementations
|
||||
// =============================================================================
|
||||
|
||||
export type { StateMachineOptions } from './StateMachine';
|
||||
export { StateMachine, createStateMachine } from './StateMachine';
|
||||
|
||||
// =============================================================================
|
||||
// 服务令牌 | Service Tokens
|
||||
// =============================================================================
|
||||
|
||||
export { StateMachineToken } from './tokens';
|
||||
|
||||
// =============================================================================
|
||||
// 蓝图节点 | Blueprint Nodes
|
||||
// =============================================================================
|
||||
|
||||
export {
|
||||
// Templates
|
||||
GetCurrentStateTemplate,
|
||||
TransitionToTemplate,
|
||||
CanTransitionTemplate,
|
||||
IsInStateTemplate,
|
||||
WasInStateTemplate,
|
||||
GetStateDurationTemplate,
|
||||
EvaluateTransitionsTemplate,
|
||||
ResetStateMachineTemplate,
|
||||
// Executors
|
||||
GetCurrentStateExecutor,
|
||||
TransitionToExecutor,
|
||||
CanTransitionExecutor,
|
||||
IsInStateExecutor,
|
||||
WasInStateExecutor,
|
||||
GetStateDurationExecutor,
|
||||
EvaluateTransitionsExecutor,
|
||||
ResetStateMachineExecutor,
|
||||
// Collection
|
||||
StateMachineNodeDefinitions
|
||||
} from './nodes';
|
||||
497
packages/framework/fsm/src/nodes/StateMachineNodes.ts
Normal file
497
packages/framework/fsm/src/nodes/StateMachineNodes.ts
Normal file
@@ -0,0 +1,497 @@
|
||||
/**
|
||||
* @zh 状态机蓝图节点
|
||||
* @en State Machine Blueprint Nodes
|
||||
*
|
||||
* @zh 提供状态机功能的蓝图节点
|
||||
* @en Provides blueprint nodes for state machine functionality
|
||||
*/
|
||||
|
||||
import type { BlueprintNodeTemplate, BlueprintNode, INodeExecutor, ExecutionResult } from '@esengine/blueprint';
|
||||
import type { IStateMachine } from '../IStateMachine';
|
||||
|
||||
// =============================================================================
|
||||
// 执行上下文接口 | Execution Context Interface
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 状态机上下文
|
||||
* @en State machine context
|
||||
*/
|
||||
interface FSMContext {
|
||||
stateMachine: IStateMachine<string, unknown>;
|
||||
evaluateInput(nodeId: string, pinName: string, defaultValue?: unknown): unknown;
|
||||
setOutputs(nodeId: string, outputs: Record<string, unknown>): void;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GetCurrentState 节点 | GetCurrentState Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh GetCurrentState 节点模板
|
||||
* @en GetCurrentState node template
|
||||
*/
|
||||
export const GetCurrentStateTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetCurrentState',
|
||||
title: 'Get Current State',
|
||||
category: 'logic',
|
||||
description: 'Get current state of the state machine / 获取状态机当前状态',
|
||||
keywords: ['fsm', 'state', 'current', 'get'],
|
||||
menuPath: ['State Machine', 'Get Current State'],
|
||||
isPure: true,
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'state',
|
||||
displayName: 'State',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
name: 'previous',
|
||||
displayName: 'Previous',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
displayName: 'Duration (ms)',
|
||||
type: 'float'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh GetCurrentState 节点执行器
|
||||
* @en GetCurrentState node executor
|
||||
*/
|
||||
export class GetCurrentStateExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const fsm = ctx.stateMachine;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
state: fsm?.current ?? '',
|
||||
previous: fsm?.previous ?? '',
|
||||
duration: fsm?.currentStateDuration ?? 0
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// TransitionTo 节点 | TransitionTo Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh TransitionTo 节点模板
|
||||
* @en TransitionTo node template
|
||||
*/
|
||||
export const TransitionToTemplate: BlueprintNodeTemplate = {
|
||||
type: 'TransitionTo',
|
||||
title: 'Transition To',
|
||||
category: 'logic',
|
||||
description: 'Transition to a new state / 转换到新状态',
|
||||
keywords: ['fsm', 'state', 'transition', 'change'],
|
||||
menuPath: ['State Machine', 'Transition To'],
|
||||
isPure: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
},
|
||||
{
|
||||
name: 'state',
|
||||
displayName: 'Target State',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
},
|
||||
{
|
||||
name: 'force',
|
||||
displayName: 'Force',
|
||||
type: 'bool',
|
||||
defaultValue: false
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
},
|
||||
{
|
||||
name: 'success',
|
||||
displayName: 'Success',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh TransitionTo 节点执行器
|
||||
* @en TransitionTo node executor
|
||||
*/
|
||||
export class TransitionToExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const state = ctx.evaluateInput(node.id, 'state', '') as string;
|
||||
const force = ctx.evaluateInput(node.id, 'force', false) as boolean;
|
||||
|
||||
let success = false;
|
||||
if (state && ctx.stateMachine) {
|
||||
success = ctx.stateMachine.transition(state, force);
|
||||
}
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
success
|
||||
},
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CanTransition 节点 | CanTransition Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh CanTransition 节点模板
|
||||
* @en CanTransition node template
|
||||
*/
|
||||
export const CanTransitionTemplate: BlueprintNodeTemplate = {
|
||||
type: 'CanTransition',
|
||||
title: 'Can Transition',
|
||||
category: 'logic',
|
||||
description: 'Check if can transition to state / 检查是否可以转换到状态',
|
||||
keywords: ['fsm', 'state', 'transition', 'can', 'check'],
|
||||
menuPath: ['State Machine', 'Can Transition'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'state',
|
||||
displayName: 'Target State',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'canTransition',
|
||||
displayName: 'Can Transition',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh CanTransition 节点执行器
|
||||
* @en CanTransition node executor
|
||||
*/
|
||||
export class CanTransitionExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const state = ctx.evaluateInput(node.id, 'state', '') as string;
|
||||
|
||||
const canTransition = state ? ctx.stateMachine?.canTransition(state) ?? false : false;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
canTransition
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// IsInState 节点 | IsInState Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh IsInState 节点模板
|
||||
* @en IsInState node template
|
||||
*/
|
||||
export const IsInStateTemplate: BlueprintNodeTemplate = {
|
||||
type: 'IsInState',
|
||||
title: 'Is In State',
|
||||
category: 'logic',
|
||||
description: 'Check if currently in a specific state / 检查是否处于特定状态',
|
||||
keywords: ['fsm', 'state', 'is', 'check', 'current'],
|
||||
menuPath: ['State Machine', 'Is In State'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'state',
|
||||
displayName: 'State',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'isInState',
|
||||
displayName: 'Is In State',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh IsInState 节点执行器
|
||||
* @en IsInState node executor
|
||||
*/
|
||||
export class IsInStateExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const state = ctx.evaluateInput(node.id, 'state', '') as string;
|
||||
|
||||
const isInState = state ? ctx.stateMachine?.current === state : false;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
isInState
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// WasInState 节点 | WasInState Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh WasInState 节点模板
|
||||
* @en WasInState node template
|
||||
*/
|
||||
export const WasInStateTemplate: BlueprintNodeTemplate = {
|
||||
type: 'WasInState',
|
||||
title: 'Was In State',
|
||||
category: 'logic',
|
||||
description: 'Check if was previously in a specific state / 检查之前是否处于特定状态',
|
||||
keywords: ['fsm', 'state', 'was', 'previous', 'check'],
|
||||
menuPath: ['State Machine', 'Was In State'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{
|
||||
name: 'state',
|
||||
displayName: 'State',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'wasInState',
|
||||
displayName: 'Was In State',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh WasInState 节点执行器
|
||||
* @en WasInState node executor
|
||||
*/
|
||||
export class WasInStateExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const state = ctx.evaluateInput(node.id, 'state', '') as string;
|
||||
|
||||
const wasInState = state ? ctx.stateMachine?.previous === state : false;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
wasInState
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GetStateDuration 节点 | GetStateDuration Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh GetStateDuration 节点模板
|
||||
* @en GetStateDuration node template
|
||||
*/
|
||||
export const GetStateDurationTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetStateDuration',
|
||||
title: 'Get State Duration',
|
||||
category: 'logic',
|
||||
description: 'Get how long current state has been active / 获取当前状态持续时间',
|
||||
keywords: ['fsm', 'state', 'duration', 'time'],
|
||||
menuPath: ['State Machine', 'Get State Duration'],
|
||||
isPure: true,
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'duration',
|
||||
displayName: 'Duration (ms)',
|
||||
type: 'float'
|
||||
},
|
||||
{
|
||||
name: 'seconds',
|
||||
displayName: 'Seconds',
|
||||
type: 'float'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh GetStateDuration 节点执行器
|
||||
* @en GetStateDuration node executor
|
||||
*/
|
||||
export class GetStateDurationExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const duration = ctx.stateMachine?.currentStateDuration ?? 0;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
duration,
|
||||
seconds: duration / 1000
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// EvaluateTransitions 节点 | EvaluateTransitions Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh EvaluateTransitions 节点模板
|
||||
* @en EvaluateTransitions node template
|
||||
*/
|
||||
export const EvaluateTransitionsTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EvaluateTransitions',
|
||||
title: 'Evaluate Transitions',
|
||||
category: 'logic',
|
||||
description: 'Evaluate and execute automatic transitions / 评估并执行自动转换',
|
||||
keywords: ['fsm', 'state', 'transition', 'evaluate', 'auto'],
|
||||
menuPath: ['State Machine', 'Evaluate Transitions'],
|
||||
isPure: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
},
|
||||
{
|
||||
name: 'transitioned',
|
||||
displayName: 'Transitioned',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh EvaluateTransitions 节点执行器
|
||||
* @en EvaluateTransitions node executor
|
||||
*/
|
||||
export class EvaluateTransitionsExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const transitioned = ctx.stateMachine?.evaluateTransitions() ?? false;
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
transitioned
|
||||
},
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// ResetStateMachine 节点 | ResetStateMachine Node
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh ResetStateMachine 节点模板
|
||||
* @en ResetStateMachine node template
|
||||
*/
|
||||
export const ResetStateMachineTemplate: BlueprintNodeTemplate = {
|
||||
type: 'ResetStateMachine',
|
||||
title: 'Reset State Machine',
|
||||
category: 'logic',
|
||||
description: 'Reset state machine to initial state / 重置状态机到初始状态',
|
||||
keywords: ['fsm', 'state', 'reset', 'initial'],
|
||||
menuPath: ['State Machine', 'Reset'],
|
||||
isPure: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
},
|
||||
{
|
||||
name: 'state',
|
||||
displayName: 'Initial State',
|
||||
type: 'string',
|
||||
defaultValue: ''
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
displayName: '',
|
||||
type: 'exec'
|
||||
}
|
||||
],
|
||||
color: '#8b5a8b'
|
||||
};
|
||||
|
||||
/**
|
||||
* @zh ResetStateMachine 节点执行器
|
||||
* @en ResetStateMachine node executor
|
||||
*/
|
||||
export class ResetStateMachineExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: unknown): ExecutionResult {
|
||||
const ctx = context as FSMContext;
|
||||
const state = ctx.evaluateInput(node.id, 'state', '') as string;
|
||||
|
||||
if (ctx.stateMachine) {
|
||||
ctx.stateMachine.reset(state || undefined);
|
||||
}
|
||||
|
||||
return {
|
||||
outputs: {},
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// 节点定义集合 | Node Definition Collection
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @zh 状态机节点定义
|
||||
* @en State machine node definitions
|
||||
*/
|
||||
export const StateMachineNodeDefinitions = [
|
||||
{ template: GetCurrentStateTemplate, executor: new GetCurrentStateExecutor() },
|
||||
{ template: TransitionToTemplate, executor: new TransitionToExecutor() },
|
||||
{ template: CanTransitionTemplate, executor: new CanTransitionExecutor() },
|
||||
{ template: IsInStateTemplate, executor: new IsInStateExecutor() },
|
||||
{ template: WasInStateTemplate, executor: new WasInStateExecutor() },
|
||||
{ template: GetStateDurationTemplate, executor: new GetStateDurationExecutor() },
|
||||
{ template: EvaluateTransitionsTemplate, executor: new EvaluateTransitionsExecutor() },
|
||||
{ template: ResetStateMachineTemplate, executor: new ResetStateMachineExecutor() }
|
||||
];
|
||||
27
packages/framework/fsm/src/nodes/index.ts
Normal file
27
packages/framework/fsm/src/nodes/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @zh 状态机蓝图节点导出
|
||||
* @en State Machine Blueprint Nodes Export
|
||||
*/
|
||||
|
||||
export {
|
||||
// Templates
|
||||
GetCurrentStateTemplate,
|
||||
TransitionToTemplate,
|
||||
CanTransitionTemplate,
|
||||
IsInStateTemplate,
|
||||
WasInStateTemplate,
|
||||
GetStateDurationTemplate,
|
||||
EvaluateTransitionsTemplate,
|
||||
ResetStateMachineTemplate,
|
||||
// Executors
|
||||
GetCurrentStateExecutor,
|
||||
TransitionToExecutor,
|
||||
CanTransitionExecutor,
|
||||
IsInStateExecutor,
|
||||
WasInStateExecutor,
|
||||
GetStateDurationExecutor,
|
||||
EvaluateTransitionsExecutor,
|
||||
ResetStateMachineExecutor,
|
||||
// Collection
|
||||
StateMachineNodeDefinitions
|
||||
} from './StateMachineNodes';
|
||||
16
packages/framework/fsm/src/tokens.ts
Normal file
16
packages/framework/fsm/src/tokens.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @zh 状态机服务令牌
|
||||
* @en State Machine Service Tokens
|
||||
*/
|
||||
|
||||
import { createServiceToken } from '@esengine/ecs-framework';
|
||||
import type { IStateMachine } from './IStateMachine';
|
||||
|
||||
/**
|
||||
* @zh 状态机服务令牌
|
||||
* @en State machine service token
|
||||
*
|
||||
* @zh 用于注入状态机服务
|
||||
* @en Used for injecting state machine service
|
||||
*/
|
||||
export const StateMachineToken = createServiceToken<IStateMachine<string, unknown>>('stateMachine');
|
||||
Reference in New Issue
Block a user