feat(blueprint): Add Component nodes + ECS refactor + node-editor fixes (#435)
* feat(blueprint): refactor BlueprintComponent as proper ECS Component - 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()`. * chore(blueprint): add changeset for ECS component refactor * fix(node-editor): fix connections not rendering when node is collapsed - getPinPosition now returns node header position when pin element is not found - Added collapsedNodesKey to force re-render connections after collapse/expand - Input pins connect to left side, output pins to right side of collapsed nodes * chore(node-editor): add changeset for collapse connection fix * feat(blueprint): add Add Component nodes for entity-component creation - Add type-specific Add_ComponentName nodes via ComponentNodeGenerator - Add generic ECS_AddComponent node for dynamic component creation - Add ExecutionContext.getComponentClass() for component lookup - Add registerComponentClass() helper for manual component registration - Each Add node supports initial property values from @BlueprintProperty * docs: update changeset with Add Component feature * feat(blueprint): improve event nodes with Self output and auto-create BeginPlay - Event Begin Play now outputs Self entity - Event Tick now outputs Self entity + Delta Seconds - Event End Play now outputs Self entity - createEmptyBlueprint() now includes Event Begin Play by default - Added menuPath to all event nodes for better organization * docs: update changeset for auto component registration * feat(blueprint): add variable nodes (Get/Set Variable) * docs: update changeset with variable nodes
This commit is contained in:
18
.changeset/blueprint-ecs-component.md
Normal file
18
.changeset/blueprint-ecs-component.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
"@esengine/blueprint": minor
|
||||
---
|
||||
|
||||
feat(blueprint): 添加 Add Component 节点支持 + 变量节点 + ECS 模式重构
|
||||
|
||||
新功能:
|
||||
- 为每个 @BlueprintExpose 组件自动生成 Add_ComponentName 节点
|
||||
- Add 节点支持设置初始属性值
|
||||
- 添加通用 ECS_AddComponent 节点用于动态添加组件
|
||||
- @BlueprintExpose 装饰的组件自动注册,无需手动调用 registerComponentClass()
|
||||
- 添加变量节点:GetVariable, SetVariable, GetBoolVariable, GetFloatVariable, GetIntVariable, GetStringVariable
|
||||
|
||||
重构:
|
||||
- BlueprintComponent 使用 @ECSComponent 装饰器注册
|
||||
- BlueprintSystem 继承标准 System 基类
|
||||
- 简化组件 API,优化 VM 生命周期管理
|
||||
- ExecutionContext.getComponentClass() 自动查找 @BlueprintExpose 注册的组件
|
||||
8
.changeset/node-editor-collapse-fix.md
Normal file
8
.changeset/node-editor-collapse-fix.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@esengine/node-editor": patch
|
||||
---
|
||||
|
||||
fix(node-editor): 修复节点收缩后连线不显示的问题
|
||||
|
||||
- 节点收缩时,连线会连接到节点头部(输入引脚在左侧,输出引脚在右侧)
|
||||
- 展开后连线会自动恢复到正确位置
|
||||
@@ -5,6 +5,7 @@
|
||||
* @zh 节点分类:
|
||||
* - events: 生命周期事件(BeginPlay, Tick, EndPlay)
|
||||
* - ecs: ECS 操作(Entity, Component, Flow)
|
||||
* - variables: 变量读写
|
||||
* - math: 数学运算
|
||||
* - time: 时间工具
|
||||
* - debug: 调试工具
|
||||
@@ -12,6 +13,7 @@
|
||||
* @en Node categories:
|
||||
* - events: Lifecycle events (BeginPlay, Tick, EndPlay)
|
||||
* - ecs: ECS operations (Entity, Component, Flow)
|
||||
* - variables: Variable get/set
|
||||
* - math: Math operations
|
||||
* - time: Time utilities
|
||||
* - debug: Debug utilities
|
||||
@@ -23,6 +25,9 @@ export * from './events';
|
||||
// ECS operations | ECS 操作
|
||||
export * from './ecs';
|
||||
|
||||
// Variables | 变量
|
||||
export * from './variables';
|
||||
|
||||
// Math operations | 数学运算
|
||||
export * from './math';
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/**
|
||||
* @zh 变量节点 - 读取和设置蓝图变量
|
||||
* @en Variable Nodes - Get and set blueprint variables
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
// ============================================================================
|
||||
// Get Variable | 获取变量
|
||||
// ============================================================================
|
||||
|
||||
export const GetVariableTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetVariable',
|
||||
title: 'Get Variable',
|
||||
category: 'variable',
|
||||
color: '#4a9c6d',
|
||||
isPure: true,
|
||||
description: 'Gets the value of a variable (获取变量的值)',
|
||||
keywords: ['variable', 'get', 'read', 'value'],
|
||||
menuPath: ['Variable', 'Get Variable'],
|
||||
inputs: [
|
||||
{ name: 'variableName', type: 'string', displayName: 'Variable Name', defaultValue: '' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'value', type: 'any', displayName: 'Value' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(GetVariableTemplate)
|
||||
export class GetVariableExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const variableName = context.evaluateInput(node.id, 'variableName', '') as string;
|
||||
|
||||
if (!variableName) {
|
||||
return { outputs: { value: null } };
|
||||
}
|
||||
|
||||
const value = context.getVariable(variableName);
|
||||
return { outputs: { value } };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Set Variable | 设置变量
|
||||
// ============================================================================
|
||||
|
||||
export const SetVariableTemplate: BlueprintNodeTemplate = {
|
||||
type: 'SetVariable',
|
||||
title: 'Set Variable',
|
||||
category: 'variable',
|
||||
color: '#4a9c6d',
|
||||
description: 'Sets the value of a variable (设置变量的值)',
|
||||
keywords: ['variable', 'set', 'write', 'assign', 'value'],
|
||||
menuPath: ['Variable', 'Set Variable'],
|
||||
inputs: [
|
||||
{ name: 'exec', type: 'exec', displayName: '' },
|
||||
{ name: 'variableName', type: 'string', displayName: 'Variable Name', defaultValue: '' },
|
||||
{ name: 'value', type: 'any', displayName: 'Value' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'exec', type: 'exec', displayName: '' },
|
||||
{ name: 'value', type: 'any', displayName: 'Value' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(SetVariableTemplate)
|
||||
export class SetVariableExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const variableName = context.evaluateInput(node.id, 'variableName', '') as string;
|
||||
const value = context.evaluateInput(node.id, 'value', null);
|
||||
|
||||
if (!variableName) {
|
||||
return { outputs: { value: null }, nextExec: 'exec' };
|
||||
}
|
||||
|
||||
context.setVariable(variableName, value);
|
||||
return { outputs: { value }, nextExec: 'exec' };
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Get Variable By Name (typed variants) | 按名称获取变量(类型变体)
|
||||
// ============================================================================
|
||||
|
||||
export const GetBoolVariableTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetBoolVariable',
|
||||
title: 'Get Bool',
|
||||
category: 'variable',
|
||||
color: '#8b1e3f',
|
||||
isPure: true,
|
||||
description: 'Gets a boolean variable (获取布尔变量)',
|
||||
keywords: ['variable', 'get', 'bool', 'boolean'],
|
||||
menuPath: ['Variable', 'Get Bool'],
|
||||
inputs: [
|
||||
{ name: 'variableName', type: 'string', displayName: 'Variable Name', defaultValue: '' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'value', type: 'bool', displayName: 'Value' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(GetBoolVariableTemplate)
|
||||
export class GetBoolVariableExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const variableName = context.evaluateInput(node.id, 'variableName', '') as string;
|
||||
const value = context.getVariable(variableName);
|
||||
return { outputs: { value: Boolean(value) } };
|
||||
}
|
||||
}
|
||||
|
||||
export const GetFloatVariableTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetFloatVariable',
|
||||
title: 'Get Float',
|
||||
category: 'variable',
|
||||
color: '#39c5bb',
|
||||
isPure: true,
|
||||
description: 'Gets a float variable (获取浮点变量)',
|
||||
keywords: ['variable', 'get', 'float', 'number'],
|
||||
menuPath: ['Variable', 'Get Float'],
|
||||
inputs: [
|
||||
{ name: 'variableName', type: 'string', displayName: 'Variable Name', defaultValue: '' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'value', type: 'float', displayName: 'Value' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(GetFloatVariableTemplate)
|
||||
export class GetFloatVariableExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const variableName = context.evaluateInput(node.id, 'variableName', '') as string;
|
||||
const value = context.getVariable(variableName);
|
||||
return { outputs: { value: Number(value) || 0 } };
|
||||
}
|
||||
}
|
||||
|
||||
export const GetIntVariableTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetIntVariable',
|
||||
title: 'Get Int',
|
||||
category: 'variable',
|
||||
color: '#1c8b8b',
|
||||
isPure: true,
|
||||
description: 'Gets an integer variable (获取整数变量)',
|
||||
keywords: ['variable', 'get', 'int', 'integer', 'number'],
|
||||
menuPath: ['Variable', 'Get Int'],
|
||||
inputs: [
|
||||
{ name: 'variableName', type: 'string', displayName: 'Variable Name', defaultValue: '' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'value', type: 'int', displayName: 'Value' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(GetIntVariableTemplate)
|
||||
export class GetIntVariableExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const variableName = context.evaluateInput(node.id, 'variableName', '') as string;
|
||||
const value = context.getVariable(variableName);
|
||||
return { outputs: { value: Math.floor(Number(value) || 0) } };
|
||||
}
|
||||
}
|
||||
|
||||
export const GetStringVariableTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetStringVariable',
|
||||
title: 'Get String',
|
||||
category: 'variable',
|
||||
color: '#e91e8c',
|
||||
isPure: true,
|
||||
description: 'Gets a string variable (获取字符串变量)',
|
||||
keywords: ['variable', 'get', 'string', 'text'],
|
||||
menuPath: ['Variable', 'Get String'],
|
||||
inputs: [
|
||||
{ name: 'variableName', type: 'string', displayName: 'Variable Name', defaultValue: '' }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'value', type: 'string', displayName: 'Value' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(GetStringVariableTemplate)
|
||||
export class GetStringVariableExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const variableName = context.evaluateInput(node.id, 'variableName', '') as string;
|
||||
const value = context.getVariable(variableName);
|
||||
return { outputs: { value: String(value ?? '') } };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* @zh 变量节点导出
|
||||
* @en Variable nodes export
|
||||
*/
|
||||
|
||||
export * from './VariableNodes';
|
||||
Reference in New Issue
Block a user