Custom Nodes
Blueprint Decorators
Section titled “Blueprint Decorators”Use decorators to quickly expose ECS components as blueprint nodes.
@BlueprintComponent
Section titled “@BlueprintComponent”Mark a component class as blueprint-enabled:
import { BlueprintComponent, BlueprintProperty } from '@esengine/blueprint';
@BlueprintComponent({ title: 'Player Controller', category: 'gameplay', color: '#4a90d9', description: 'Controls player movement and interaction'})class PlayerController extends Component { @BlueprintProperty({ displayName: 'Move Speed' }) speed: number = 100;
@BlueprintProperty({ displayName: 'Jump Height' }) jumpHeight: number = 200;}@BlueprintProperty
Section titled “@BlueprintProperty”Expose component properties as node inputs:
@BlueprintProperty({ displayName: 'Health', description: 'Current health value', isInput: true, isOutput: true})health: number = 100;@BlueprintArray
Section titled “@BlueprintArray”For array type properties, supports editing complex object arrays:
import { BlueprintArray, Schema } from '@esengine/blueprint';
interface Waypoint { position: { x: number; y: number }; waitTime: number; speed: number;}
@BlueprintComponent({ title: 'Patrol Path', category: 'ai'})class PatrolPath extends Component { @BlueprintArray({ displayName: 'Waypoints', description: 'Points along the patrol path', itemSchema: Schema.object({ position: Schema.vector2({ defaultValue: { x: 0, y: 0 } }), waitTime: Schema.float({ min: 0, max: 10, defaultValue: 1.0 }), speed: Schema.float({ min: 0, max: 500, defaultValue: 100 }) }), reorderable: true, exposeElementPorts: true, portNameTemplate: 'Waypoint {index1}' }) waypoints: Waypoint[] = [];}Schema Type System
Section titled “Schema Type System”Schema defines type information for complex data structures, enabling the editor to automatically generate corresponding UI.
Primitive Types
Section titled “Primitive Types”import { Schema } from '@esengine/blueprint';
// Number typesSchema.float({ min: 0, max: 100, defaultValue: 50, step: 0.1 })Schema.int({ min: 0, max: 10, defaultValue: 5 })
// StringSchema.string({ defaultValue: 'Hello', multiline: false, placeholder: 'Enter text...' })
// BooleanSchema.boolean({ defaultValue: true })
// VectorsSchema.vector2({ defaultValue: { x: 0, y: 0 } })Schema.vector3({ defaultValue: { x: 0, y: 0, z: 0 } })Composite Types
Section titled “Composite Types”// ObjectSchema.object({ name: Schema.string({ defaultValue: '' }), health: Schema.float({ min: 0, max: 100 }), position: Schema.vector2()})
// ArraySchema.array({ items: Schema.float(), minItems: 0, maxItems: 10})
// EnumSchema.enum({ options: ['idle', 'walk', 'run', 'jump'], defaultValue: 'idle'})
// ReferenceSchema.ref({ refType: 'entity' })Schema.ref({ refType: 'asset', assetType: 'texture' })Complete Example
Section titled “Complete Example”@BlueprintComponent({ title: 'Enemy Config', category: 'ai' })class EnemyConfig extends Component { @BlueprintArray({ displayName: 'Attack Patterns', itemSchema: Schema.object({ name: Schema.string({ defaultValue: 'Basic Attack' }), damage: Schema.float({ min: 0, max: 100, defaultValue: 10 }), cooldown: Schema.float({ min: 0, max: 10, defaultValue: 1 }), range: Schema.float({ min: 0, max: 500, defaultValue: 50 }), animation: Schema.string({ defaultValue: 'attack_01' }) }), reorderable: true }) attackPatterns: AttackPattern[] = [];
@BlueprintProperty({ displayName: 'Patrol Area', schema: Schema.object({ center: Schema.vector2(), radius: Schema.float({ min: 0, defaultValue: 100 }) }) }) patrolArea: { center: { x: number; y: number }; radius: number } = { center: { x: 0, y: 0 }, radius: 100 };}Defining Node Template
Section titled “Defining Node Template”import { BlueprintNodeTemplate } from '@esengine/blueprint';
const MyNodeTemplate: BlueprintNodeTemplate = { type: 'MyCustomNode', title: 'My Custom Node', category: 'custom', description: 'A custom node example', keywords: ['custom', 'example'], inputs: [ { name: 'exec', type: 'exec', direction: 'input', isExec: true }, { name: 'value', type: 'number', direction: 'input', defaultValue: 0 } ], outputs: [ { name: 'exec', type: 'exec', direction: 'output', isExec: true }, { name: 'result', type: 'number', direction: 'output' } ]};Implementing Node Executor
Section titled “Implementing Node Executor”import { INodeExecutor, RegisterNode, BlueprintNode, ExecutionContext, ExecutionResult } from '@esengine/blueprint';
@RegisterNode(MyNodeTemplate)class MyNodeExecutor implements INodeExecutor { execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult { const value = context.evaluateInput(node.id, 'value', 0) as number; const result = value * 2; return { outputs: { result }, nextExec: 'exec' }; }}Registration Methods
Section titled “Registration Methods”// Method 1: Using decorator@RegisterNode(MyNodeTemplate)class MyNodeExecutor implements INodeExecutor { ... }
// Method 2: Manual registrationNodeRegistry.instance.register(MyNodeTemplate, new MyNodeExecutor());Node Registry
Section titled “Node Registry”import { NodeRegistry } from '@esengine/blueprint';
const registry = NodeRegistry.instance;const allTemplates = registry.getAllTemplates();const mathNodes = registry.getTemplatesByCategory('math');const results = registry.searchTemplates('add');if (registry.has('MyCustomNode')) { ... }Pure Nodes
Section titled “Pure Nodes”Pure nodes have no side effects and their outputs are cached:
const PureNodeTemplate: BlueprintNodeTemplate = { type: 'GetDistance', title: 'Get Distance', category: 'math', isPure: true, inputs: [ { name: 'a', type: 'vector2', direction: 'input' }, { name: 'b', type: 'vector2', direction: 'input' } ], outputs: [ { name: 'distance', type: 'number', direction: 'output' } ]};