feat(blueprint): refactor BlueprintComponent as proper ECS Component (#432)
- Convert BlueprintComponent from interface to actual ECS Component class - Add ready-to-use BlueprintSystem that extends EntitySystem - Remove deprecated legacy APIs (createBlueprintSystem, etc.) - Update all blueprint documentation (Chinese & English) - Simplify user API: just add BlueprintSystem and BlueprintComponent BREAKING CHANGE: BlueprintComponent is now a class extending Component, not an interface. Use `new BlueprintComponent()` instead of `createBlueprintComponentData()`.
This commit is contained in:
@@ -14,23 +14,27 @@
|
||||
* - Auto component node generation (using decorators)
|
||||
* - Runtime blueprint execution
|
||||
*
|
||||
* @example 基础使用 | Basic usage:
|
||||
* @example 基础使用 | Basic Usage:
|
||||
* ```typescript
|
||||
* import {
|
||||
* createBlueprintSystem,
|
||||
* registerAllComponentNodes
|
||||
* } from '@esengine/blueprint';
|
||||
* import { BlueprintSystem, BlueprintComponent } from '@esengine/blueprint';
|
||||
* import { Scene, Core } from '@esengine/ecs-framework';
|
||||
*
|
||||
* // 注册所有标记的组件节点 | Register all marked component nodes
|
||||
* registerAllComponentNodes();
|
||||
* // 创建场景并添加蓝图系统
|
||||
* const scene = new Scene();
|
||||
* scene.addSystem(new BlueprintSystem());
|
||||
* Core.setScene(scene);
|
||||
*
|
||||
* // 创建蓝图系统 | Create blueprint system
|
||||
* const blueprintSystem = createBlueprintSystem(scene);
|
||||
* // 为实体添加蓝图
|
||||
* const entity = scene.createEntity('Player');
|
||||
* const blueprint = new BlueprintComponent();
|
||||
* blueprint.blueprintAsset = await loadBlueprintAsset('player.bp');
|
||||
* entity.addComponent(blueprint);
|
||||
* ```
|
||||
*
|
||||
* @example 标记组件 | Mark components:
|
||||
* @example 标记组件 | Mark Components:
|
||||
* ```typescript
|
||||
* import { BlueprintExpose, BlueprintProperty, BlueprintMethod } from '@esengine/blueprint';
|
||||
* import { Component, ECSComponent } from '@esengine/ecs-framework';
|
||||
*
|
||||
* @ECSComponent('Health')
|
||||
* @BlueprintExpose({ displayName: '生命值' })
|
||||
@@ -69,19 +73,8 @@ import './nodes';
|
||||
// Re-export commonly used items
|
||||
export { NodeRegistry, RegisterNode } from './runtime/NodeRegistry';
|
||||
export { BlueprintVM } from './runtime/BlueprintVM';
|
||||
export {
|
||||
createBlueprintComponentData,
|
||||
initializeBlueprintVM,
|
||||
startBlueprint,
|
||||
stopBlueprint,
|
||||
tickBlueprint,
|
||||
cleanupBlueprint
|
||||
} from './runtime/BlueprintComponent';
|
||||
export {
|
||||
createBlueprintSystem,
|
||||
triggerBlueprintEvent,
|
||||
triggerCustomBlueprintEvent
|
||||
} from './runtime/BlueprintSystem';
|
||||
export { BlueprintComponent } from './runtime/BlueprintComponent';
|
||||
export { BlueprintSystem } from './runtime/BlueprintSystem';
|
||||
export { createEmptyBlueprint, validateBlueprintAsset } from './types/blueprint';
|
||||
|
||||
// Re-export registry for convenience
|
||||
|
||||
@@ -1,116 +1,117 @@
|
||||
/**
|
||||
* Blueprint Component - Attaches a blueprint to an entity
|
||||
* 蓝图组件 - 将蓝图附加到实体
|
||||
* @zh 蓝图组件 - 将蓝图附加到实体
|
||||
* @en Blueprint Component - Attaches a blueprint to an entity
|
||||
*/
|
||||
|
||||
import type { Entity, IScene } from '@esengine/ecs-framework';
|
||||
import { Component, ECSComponent, type Entity, type IScene } from '@esengine/ecs-framework';
|
||||
import { BlueprintAsset } from '../types/blueprint';
|
||||
import { BlueprintVM } from './BlueprintVM';
|
||||
|
||||
/**
|
||||
* Component interface for ECS integration
|
||||
* 用于 ECS 集成的组件接口
|
||||
* @zh 蓝图组件,用于将可视化脚本附加到 ECS 实体
|
||||
* @en Blueprint component for attaching visual scripts to ECS entities
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const entity = scene.createEntity('Player');
|
||||
* const blueprint = new BlueprintComponent();
|
||||
* blueprint.blueprintAsset = await loadBlueprintAsset('player.bp');
|
||||
* blueprint.autoStart = true;
|
||||
* entity.addComponent(blueprint);
|
||||
* ```
|
||||
*/
|
||||
export interface IBlueprintComponent {
|
||||
/** Entity ID this component belongs to (此组件所属的实体ID) */
|
||||
entityId: number | null;
|
||||
@ECSComponent('Blueprint')
|
||||
export class BlueprintComponent extends Component {
|
||||
/**
|
||||
* @zh 蓝图资产引用
|
||||
* @en Blueprint asset reference
|
||||
*/
|
||||
blueprintAsset: BlueprintAsset | null = null;
|
||||
|
||||
/** Blueprint asset reference (蓝图资产引用) */
|
||||
blueprintAsset: BlueprintAsset | null;
|
||||
/**
|
||||
* @zh 用于序列化的蓝图资产路径
|
||||
* @en Blueprint asset path for serialization
|
||||
*/
|
||||
blueprintPath: string = '';
|
||||
|
||||
/** Blueprint asset path for serialization (用于序列化的蓝图资产路径) */
|
||||
blueprintPath: string;
|
||||
/**
|
||||
* @zh 实体创建时自动开始执行
|
||||
* @en Auto-start execution when entity is created
|
||||
*/
|
||||
autoStart: boolean = true;
|
||||
|
||||
/** Auto-start execution when entity is created (实体创建时自动开始执行) */
|
||||
autoStart: boolean;
|
||||
/**
|
||||
* @zh 启用 VM 调试模式
|
||||
* @en Enable debug mode for VM
|
||||
*/
|
||||
debug: boolean = false;
|
||||
|
||||
/** Enable debug mode for VM (启用 VM 调试模式) */
|
||||
debug: boolean;
|
||||
/**
|
||||
* @zh 运行时 VM 实例
|
||||
* @en Runtime VM instance
|
||||
*/
|
||||
vm: BlueprintVM | null = null;
|
||||
|
||||
/** Runtime VM instance (运行时 VM 实例) */
|
||||
vm: BlueprintVM | null;
|
||||
/**
|
||||
* @zh 蓝图是否已启动
|
||||
* @en Whether the blueprint has started
|
||||
*/
|
||||
isStarted: boolean = false;
|
||||
|
||||
/** Whether the blueprint has started (蓝图是否已启动) */
|
||||
isStarted: boolean;
|
||||
}
|
||||
/**
|
||||
* @zh 初始化蓝图 VM
|
||||
* @en Initialize blueprint VM
|
||||
*/
|
||||
initialize(entity: Entity, scene: IScene): void {
|
||||
if (!this.blueprintAsset) return;
|
||||
|
||||
/**
|
||||
* Creates a blueprint component data object
|
||||
* 创建蓝图组件数据对象
|
||||
*/
|
||||
export function createBlueprintComponentData(): IBlueprintComponent {
|
||||
return {
|
||||
entityId: null,
|
||||
blueprintAsset: null,
|
||||
blueprintPath: '',
|
||||
autoStart: true,
|
||||
debug: false,
|
||||
vm: null,
|
||||
isStarted: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the VM for a blueprint component
|
||||
* 为蓝图组件初始化 VM
|
||||
*/
|
||||
export function initializeBlueprintVM(
|
||||
component: IBlueprintComponent,
|
||||
entity: Entity,
|
||||
scene: IScene
|
||||
): void {
|
||||
if (!component.blueprintAsset) {
|
||||
return;
|
||||
this.vm = new BlueprintVM(this.blueprintAsset, entity, scene);
|
||||
this.vm.debug = this.debug;
|
||||
}
|
||||
|
||||
// Create VM instance
|
||||
// 创建 VM 实例
|
||||
component.vm = new BlueprintVM(component.blueprintAsset, entity, scene);
|
||||
component.vm.debug = component.debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start blueprint execution
|
||||
* 开始蓝图执行
|
||||
*/
|
||||
export function startBlueprint(component: IBlueprintComponent): void {
|
||||
if (component.vm && !component.isStarted) {
|
||||
component.vm.start();
|
||||
component.isStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop blueprint execution
|
||||
* 停止蓝图执行
|
||||
*/
|
||||
export function stopBlueprint(component: IBlueprintComponent): void {
|
||||
if (component.vm && component.isStarted) {
|
||||
component.vm.stop();
|
||||
component.isStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update blueprint execution
|
||||
* 更新蓝图执行
|
||||
*/
|
||||
export function tickBlueprint(component: IBlueprintComponent, deltaTime: number): void {
|
||||
if (component.vm && component.isStarted) {
|
||||
component.vm.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up blueprint resources
|
||||
* 清理蓝图资源
|
||||
*/
|
||||
export function cleanupBlueprint(component: IBlueprintComponent): void {
|
||||
if (component.vm) {
|
||||
if (component.isStarted) {
|
||||
component.vm.stop();
|
||||
/**
|
||||
* @zh 开始执行蓝图
|
||||
* @en Start blueprint execution
|
||||
*/
|
||||
start(): void {
|
||||
if (this.vm && !this.isStarted) {
|
||||
this.vm.start();
|
||||
this.isStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 停止执行蓝图
|
||||
* @en Stop blueprint execution
|
||||
*/
|
||||
stop(): void {
|
||||
if (this.vm && this.isStarted) {
|
||||
this.vm.stop();
|
||||
this.isStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 更新蓝图
|
||||
* @en Update blueprint
|
||||
*/
|
||||
tick(deltaTime: number): void {
|
||||
if (this.vm && this.isStarted) {
|
||||
this.vm.tick(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 清理蓝图资源
|
||||
* @en Cleanup blueprint resources
|
||||
*/
|
||||
cleanup(): void {
|
||||
if (this.vm) {
|
||||
if (this.isStarted) {
|
||||
this.vm.stop();
|
||||
}
|
||||
this.vm = null;
|
||||
this.isStarted = false;
|
||||
}
|
||||
component.vm = null;
|
||||
component.isStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +1,86 @@
|
||||
/**
|
||||
* Blueprint Execution System - Manages blueprint lifecycle and execution
|
||||
* 蓝图执行系统 - 管理蓝图生命周期和执行
|
||||
* @zh 蓝图系统 - 处理所有带有 BlueprintComponent 的实体
|
||||
* @en Blueprint System - Processes all entities with BlueprintComponent
|
||||
*/
|
||||
|
||||
import type { Entity, IScene } from '@esengine/ecs-framework';
|
||||
import {
|
||||
IBlueprintComponent,
|
||||
initializeBlueprintVM,
|
||||
startBlueprint,
|
||||
tickBlueprint,
|
||||
cleanupBlueprint
|
||||
} from './BlueprintComponent';
|
||||
import { EntitySystem, Matcher, ECSSystem, type Entity, Time } from '@esengine/ecs-framework';
|
||||
import { BlueprintComponent } from './BlueprintComponent';
|
||||
import { registerAllComponentNodes } from '../registry';
|
||||
|
||||
/**
|
||||
* Blueprint system interface for engine integration
|
||||
* 用于引擎集成的蓝图系统接口
|
||||
* @zh 蓝图执行系统
|
||||
* @en Blueprint execution system
|
||||
*
|
||||
* @zh 自动处理所有带有 BlueprintComponent 的实体,管理蓝图的初始化、执行和清理
|
||||
* @en Automatically processes all entities with BlueprintComponent, manages blueprint initialization, execution and cleanup
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { BlueprintSystem } from '@esengine/blueprint';
|
||||
*
|
||||
* // 添加到场景
|
||||
* scene.addSystem(new BlueprintSystem());
|
||||
*
|
||||
* // 为实体添加蓝图
|
||||
* const entity = scene.createEntity('Player');
|
||||
* const blueprint = new BlueprintComponent();
|
||||
* blueprint.blueprintAsset = await loadBlueprintAsset('player.bp');
|
||||
* entity.addComponent(blueprint);
|
||||
* ```
|
||||
*/
|
||||
export interface IBlueprintSystem {
|
||||
/** Process entities with blueprint components (处理带有蓝图组件的实体) */
|
||||
process(entities: IBlueprintEntity[], deltaTime: number): void;
|
||||
@ECSSystem('BlueprintSystem')
|
||||
export class BlueprintSystem extends EntitySystem {
|
||||
private _componentsRegistered = false;
|
||||
|
||||
/** Called when entity is added to system (实体添加到系统时调用) */
|
||||
onEntityAdded(entity: IBlueprintEntity): void;
|
||||
constructor() {
|
||||
super(Matcher.all(BlueprintComponent));
|
||||
}
|
||||
|
||||
/** Called when entity is removed from system (实体从系统移除时调用) */
|
||||
onEntityRemoved(entity: IBlueprintEntity): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity with blueprint component
|
||||
* 带有蓝图组件的实体
|
||||
*/
|
||||
export interface IBlueprintEntity extends Entity {
|
||||
/** Blueprint component data (蓝图组件数据) */
|
||||
blueprintComponent: IBlueprintComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blueprint execution system
|
||||
* 创建蓝图执行系统
|
||||
*/
|
||||
export function createBlueprintSystem(scene: IScene): IBlueprintSystem {
|
||||
return {
|
||||
process(entities: IBlueprintEntity[], deltaTime: number): void {
|
||||
for (const entity of entities) {
|
||||
const component = entity.blueprintComponent;
|
||||
|
||||
// Skip if no blueprint asset loaded
|
||||
// 如果没有加载蓝图资产则跳过
|
||||
if (!component.blueprintAsset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialize VM if needed
|
||||
// 如果需要则初始化 VM
|
||||
if (!component.vm) {
|
||||
initializeBlueprintVM(component, entity, scene);
|
||||
}
|
||||
|
||||
// Auto-start if enabled
|
||||
// 如果启用则自动启动
|
||||
if (component.autoStart && !component.isStarted) {
|
||||
startBlueprint(component);
|
||||
}
|
||||
|
||||
// Tick the blueprint
|
||||
// 更新蓝图
|
||||
tickBlueprint(component, deltaTime);
|
||||
}
|
||||
},
|
||||
|
||||
onEntityAdded(entity: IBlueprintEntity): void {
|
||||
const component = entity.blueprintComponent;
|
||||
|
||||
if (component.blueprintAsset) {
|
||||
initializeBlueprintVM(component, entity, scene);
|
||||
|
||||
if (component.autoStart) {
|
||||
startBlueprint(component);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onEntityRemoved(entity: IBlueprintEntity): void {
|
||||
cleanupBlueprint(entity.blueprintComponent);
|
||||
/**
|
||||
* @zh 系统初始化时注册所有组件节点
|
||||
* @en Register all component nodes when system initializes
|
||||
*/
|
||||
protected override onInitialize(): void {
|
||||
if (!this._componentsRegistered) {
|
||||
registerAllComponentNodes();
|
||||
this._componentsRegistered = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to manually trigger blueprint events
|
||||
* 手动触发蓝图事件的工具
|
||||
*/
|
||||
export function triggerBlueprintEvent(
|
||||
entity: IBlueprintEntity,
|
||||
eventType: string,
|
||||
data?: Record<string, unknown>
|
||||
): void {
|
||||
const vm = entity.blueprintComponent.vm;
|
||||
/**
|
||||
* @zh 处理所有带有蓝图组件的实体
|
||||
* @en Process all entities with blueprint components
|
||||
*/
|
||||
protected override process(entities: readonly Entity[]): void {
|
||||
const dt = Time.deltaTime;
|
||||
|
||||
if (vm && entity.blueprintComponent.isStarted) {
|
||||
vm.triggerEvent(eventType, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to trigger custom events by name
|
||||
* 按名称触发自定义事件的工具
|
||||
*/
|
||||
export function triggerCustomBlueprintEvent(
|
||||
entity: IBlueprintEntity,
|
||||
eventName: string,
|
||||
data?: Record<string, unknown>
|
||||
): void {
|
||||
const vm = entity.blueprintComponent.vm;
|
||||
|
||||
if (vm && entity.blueprintComponent.isStarted) {
|
||||
vm.triggerCustomEvent(eventName, data);
|
||||
for (const entity of entities) {
|
||||
const blueprint = entity.getComponent(BlueprintComponent);
|
||||
if (!blueprint?.blueprintAsset) continue;
|
||||
|
||||
// 初始化 VM
|
||||
if (!blueprint.vm) {
|
||||
blueprint.initialize(entity, this.scene!);
|
||||
}
|
||||
|
||||
// 自动启动
|
||||
if (blueprint.autoStart && !blueprint.isStarted) {
|
||||
blueprint.start();
|
||||
}
|
||||
|
||||
// 每帧更新
|
||||
blueprint.tick(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @zh 实体移除时清理蓝图资源
|
||||
* @en Cleanup blueprint resources when entity is removed
|
||||
*/
|
||||
protected override onRemoved(entity: Entity): void {
|
||||
const blueprint = entity.getComponent(BlueprintComponent);
|
||||
if (blueprint) {
|
||||
blueprint.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user