Feature/physics and tilemap enhancement (#247)
* feat(behavior-tree,tilemap): 修复编辑器连线缩放问题并增强插件系统 * feat(node-editor,blueprint): 新增通用节点编辑器和蓝图可视化脚本系统 * feat(editor,tilemap): 优化编辑器UI样式和Tilemap编辑器功能 * fix: 修复CodeQL安全警告和CI类型检查错误 * fix: 修复CodeQL安全警告和CI类型检查错误 * fix: 修复CodeQL安全警告和CI类型检查错误
This commit is contained in:
91
packages/blueprint/src/nodes/debug/Print.ts
Normal file
91
packages/blueprint/src/nodes/debug/Print.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Print Node - Outputs a message for debugging
|
||||
* 打印节点 - 输出调试消息
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* Print node template
|
||||
* Print 节点模板
|
||||
*/
|
||||
export const PrintTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Print',
|
||||
title: 'Print String',
|
||||
category: 'debug',
|
||||
color: '#785EF0',
|
||||
description: 'Prints a message to the console for debugging (打印消息到控制台用于调试)',
|
||||
keywords: ['log', 'debug', 'console', 'output', 'print'],
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: 'string',
|
||||
displayName: 'Message',
|
||||
defaultValue: 'Hello Blueprint!'
|
||||
},
|
||||
{
|
||||
name: 'printToScreen',
|
||||
type: 'bool',
|
||||
displayName: 'Print to Screen',
|
||||
defaultValue: true
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
type: 'float',
|
||||
displayName: 'Duration',
|
||||
defaultValue: 2.0
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* Print node executor
|
||||
* Print 节点执行器
|
||||
*/
|
||||
@RegisterNode(PrintTemplate)
|
||||
export class PrintExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const message = context.evaluateInput(node.id, 'message', 'Hello Blueprint!');
|
||||
const printToScreen = context.evaluateInput(node.id, 'printToScreen', true);
|
||||
const duration = context.evaluateInput(node.id, 'duration', 2.0);
|
||||
|
||||
// Console output
|
||||
// 控制台输出
|
||||
console.log(`[Blueprint] ${message}`);
|
||||
|
||||
// Screen output via event (handled by runtime)
|
||||
// 通过事件输出到屏幕(由运行时处理)
|
||||
if (printToScreen) {
|
||||
const event = new CustomEvent('blueprint:print', {
|
||||
detail: {
|
||||
message: String(message),
|
||||
duration: Number(duration),
|
||||
entityId: context.entity.id,
|
||||
entityName: context.entity.name
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
6
packages/blueprint/src/nodes/debug/index.ts
Normal file
6
packages/blueprint/src/nodes/debug/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Debug Nodes - Tools for debugging blueprints
|
||||
* 调试节点 - 蓝图调试工具
|
||||
*/
|
||||
|
||||
export * from './Print';
|
||||
44
packages/blueprint/src/nodes/events/EventBeginPlay.ts
Normal file
44
packages/blueprint/src/nodes/events/EventBeginPlay.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Event Begin Play Node - Triggered when the blueprint starts
|
||||
* 开始播放事件节点 - 蓝图启动时触发
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* EventBeginPlay node template
|
||||
* EventBeginPlay 节点模板
|
||||
*/
|
||||
export const EventBeginPlayTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventBeginPlay',
|
||||
title: 'Event Begin Play',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered once when the blueprint starts executing (蓝图开始执行时触发一次)',
|
||||
keywords: ['start', 'begin', 'init', 'event'],
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* EventBeginPlay node executor
|
||||
* EventBeginPlay 节点执行器
|
||||
*/
|
||||
@RegisterNode(EventBeginPlayTemplate)
|
||||
export class EventBeginPlayExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode, _context: ExecutionContext): ExecutionResult {
|
||||
// Event nodes just trigger execution flow
|
||||
// 事件节点只触发执行流
|
||||
return {
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
42
packages/blueprint/src/nodes/events/EventEndPlay.ts
Normal file
42
packages/blueprint/src/nodes/events/EventEndPlay.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Event End Play Node - Triggered when the blueprint stops
|
||||
* 结束播放事件节点 - 蓝图停止时触发
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* EventEndPlay node template
|
||||
* EventEndPlay 节点模板
|
||||
*/
|
||||
export const EventEndPlayTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventEndPlay',
|
||||
title: 'Event End Play',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered once when the blueprint stops executing (蓝图停止执行时触发一次)',
|
||||
keywords: ['stop', 'end', 'destroy', 'event'],
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* EventEndPlay node executor
|
||||
* EventEndPlay 节点执行器
|
||||
*/
|
||||
@RegisterNode(EventEndPlayTemplate)
|
||||
export class EventEndPlayExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode, _context: ExecutionContext): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec'
|
||||
};
|
||||
}
|
||||
}
|
||||
50
packages/blueprint/src/nodes/events/EventTick.ts
Normal file
50
packages/blueprint/src/nodes/events/EventTick.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Event Tick Node - Triggered every frame
|
||||
* 每帧事件节点 - 每帧触发
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* EventTick node template
|
||||
* EventTick 节点模板
|
||||
*/
|
||||
export const EventTickTemplate: BlueprintNodeTemplate = {
|
||||
type: 'EventTick',
|
||||
title: 'Event Tick',
|
||||
category: 'event',
|
||||
color: '#CC0000',
|
||||
description: 'Triggered every frame during execution (执行期间每帧触发)',
|
||||
keywords: ['update', 'frame', 'tick', 'event'],
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'deltaTime',
|
||||
type: 'float',
|
||||
displayName: 'Delta Seconds'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* EventTick node executor
|
||||
* EventTick 节点执行器
|
||||
*/
|
||||
@RegisterNode(EventTickTemplate)
|
||||
export class EventTickExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
outputs: {
|
||||
deltaTime: context.deltaTime
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
8
packages/blueprint/src/nodes/events/index.ts
Normal file
8
packages/blueprint/src/nodes/events/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Event Nodes - Entry points for blueprint execution
|
||||
* 事件节点 - 蓝图执行的入口点
|
||||
*/
|
||||
|
||||
export * from './EventBeginPlay';
|
||||
export * from './EventTick';
|
||||
export * from './EventEndPlay';
|
||||
11
packages/blueprint/src/nodes/index.ts
Normal file
11
packages/blueprint/src/nodes/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
/**
|
||||
* Blueprint Nodes - All node definitions and executors
|
||||
* 蓝图节点 - 所有节点定义和执行器
|
||||
*/
|
||||
|
||||
// Import all nodes to trigger registration
|
||||
// 导入所有节点以触发注册
|
||||
export * from './events';
|
||||
export * from './debug';
|
||||
export * from './time';
|
||||
export * from './math';
|
||||
122
packages/blueprint/src/nodes/math/MathOperations.ts
Normal file
122
packages/blueprint/src/nodes/math/MathOperations.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Math Operation Nodes - Basic arithmetic operations
|
||||
* 数学运算节点 - 基础算术运算
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
// Add Node (加法节点)
|
||||
export const AddTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Add',
|
||||
title: 'Add',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Adds two numbers together (将两个数字相加)',
|
||||
keywords: ['add', 'plus', 'sum', '+', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(AddTemplate)
|
||||
export class AddExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: a + b } };
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract Node (减法节点)
|
||||
export const SubtractTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Subtract',
|
||||
title: 'Subtract',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Subtracts B from A (从 A 减去 B)',
|
||||
keywords: ['subtract', 'minus', '-', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 0 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(SubtractTemplate)
|
||||
export class SubtractExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 0));
|
||||
return { outputs: { result: a - b } };
|
||||
}
|
||||
}
|
||||
|
||||
// Multiply Node (乘法节点)
|
||||
export const MultiplyTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Multiply',
|
||||
title: 'Multiply',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Multiplies two numbers (将两个数字相乘)',
|
||||
keywords: ['multiply', 'times', '*', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 1 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(MultiplyTemplate)
|
||||
export class MultiplyExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 1));
|
||||
return { outputs: { result: a * b } };
|
||||
}
|
||||
}
|
||||
|
||||
// Divide Node (除法节点)
|
||||
export const DivideTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Divide',
|
||||
title: 'Divide',
|
||||
category: 'math',
|
||||
color: '#4CAF50',
|
||||
description: 'Divides A by B (A 除以 B)',
|
||||
keywords: ['divide', '/', 'math'],
|
||||
isPure: true,
|
||||
inputs: [
|
||||
{ name: 'a', type: 'float', displayName: 'A', defaultValue: 0 },
|
||||
{ name: 'b', type: 'float', displayName: 'B', defaultValue: 1 }
|
||||
],
|
||||
outputs: [
|
||||
{ name: 'result', type: 'float', displayName: 'Result' }
|
||||
]
|
||||
};
|
||||
|
||||
@RegisterNode(DivideTemplate)
|
||||
export class DivideExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const a = Number(context.evaluateInput(node.id, 'a', 0));
|
||||
const b = Number(context.evaluateInput(node.id, 'b', 1));
|
||||
|
||||
// Prevent division by zero (防止除零)
|
||||
if (b === 0) {
|
||||
return { outputs: { result: 0 } };
|
||||
}
|
||||
|
||||
return { outputs: { result: a / b } };
|
||||
}
|
||||
}
|
||||
6
packages/blueprint/src/nodes/math/index.ts
Normal file
6
packages/blueprint/src/nodes/math/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Math Nodes - Mathematical operation nodes
|
||||
* 数学节点 - 数学运算节点
|
||||
*/
|
||||
|
||||
export * from './MathOperations';
|
||||
57
packages/blueprint/src/nodes/time/Delay.ts
Normal file
57
packages/blueprint/src/nodes/time/Delay.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Delay Node - Pauses execution for a specified duration
|
||||
* 延迟节点 - 暂停执行指定的时长
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* Delay node template
|
||||
* Delay 节点模板
|
||||
*/
|
||||
export const DelayTemplate: BlueprintNodeTemplate = {
|
||||
type: 'Delay',
|
||||
title: 'Delay',
|
||||
category: 'flow',
|
||||
color: '#FFFFFF',
|
||||
description: 'Pauses execution for a specified number of seconds (暂停执行指定的秒数)',
|
||||
keywords: ['wait', 'delay', 'pause', 'sleep', 'timer'],
|
||||
inputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: ''
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
type: 'float',
|
||||
displayName: 'Duration',
|
||||
defaultValue: 1.0
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'exec',
|
||||
type: 'exec',
|
||||
displayName: 'Completed'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* Delay node executor
|
||||
* Delay 节点执行器
|
||||
*/
|
||||
@RegisterNode(DelayTemplate)
|
||||
export class DelayExecutor implements INodeExecutor {
|
||||
execute(node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
const duration = context.evaluateInput(node.id, 'duration', 1.0) as number;
|
||||
|
||||
return {
|
||||
nextExec: 'exec',
|
||||
delay: duration
|
||||
};
|
||||
}
|
||||
}
|
||||
45
packages/blueprint/src/nodes/time/GetDeltaTime.ts
Normal file
45
packages/blueprint/src/nodes/time/GetDeltaTime.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Get Delta Time Node - Returns the time since last frame
|
||||
* 获取增量时间节点 - 返回上一帧以来的时间
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* GetDeltaTime node template
|
||||
* GetDeltaTime 节点模板
|
||||
*/
|
||||
export const GetDeltaTimeTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetDeltaTime',
|
||||
title: 'Get Delta Time',
|
||||
category: 'time',
|
||||
color: '#4FC3F7',
|
||||
description: 'Returns the time elapsed since the last frame in seconds (返回上一帧以来经过的时间,单位秒)',
|
||||
keywords: ['delta', 'time', 'frame', 'dt'],
|
||||
isPure: true,
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'deltaTime',
|
||||
type: 'float',
|
||||
displayName: 'Delta Seconds'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* GetDeltaTime node executor
|
||||
* GetDeltaTime 节点执行器
|
||||
*/
|
||||
@RegisterNode(GetDeltaTimeTemplate)
|
||||
export class GetDeltaTimeExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
return {
|
||||
outputs: {
|
||||
deltaTime: context.deltaTime
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
45
packages/blueprint/src/nodes/time/GetTime.ts
Normal file
45
packages/blueprint/src/nodes/time/GetTime.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Get Time Node - Returns the total time since blueprint started
|
||||
* 获取时间节点 - 返回蓝图启动以来的总时间
|
||||
*/
|
||||
|
||||
import { BlueprintNodeTemplate, BlueprintNode } from '../../types/nodes';
|
||||
import { ExecutionContext, ExecutionResult } from '../../runtime/ExecutionContext';
|
||||
import { INodeExecutor, RegisterNode } from '../../runtime/NodeRegistry';
|
||||
|
||||
/**
|
||||
* GetTime node template
|
||||
* GetTime 节点模板
|
||||
*/
|
||||
export const GetTimeTemplate: BlueprintNodeTemplate = {
|
||||
type: 'GetTime',
|
||||
title: 'Get Game Time',
|
||||
category: 'time',
|
||||
color: '#4FC3F7',
|
||||
description: 'Returns the total time since the blueprint started in seconds (返回蓝图启动以来的总时间,单位秒)',
|
||||
keywords: ['time', 'total', 'elapsed', 'game'],
|
||||
isPure: true,
|
||||
inputs: [],
|
||||
outputs: [
|
||||
{
|
||||
name: 'time',
|
||||
type: 'float',
|
||||
displayName: 'Seconds'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/**
|
||||
* GetTime node executor
|
||||
* GetTime 节点执行器
|
||||
*/
|
||||
@RegisterNode(GetTimeTemplate)
|
||||
export class GetTimeExecutor implements INodeExecutor {
|
||||
execute(_node: BlueprintNode, context: ExecutionContext): ExecutionResult {
|
||||
return {
|
||||
outputs: {
|
||||
time: context.time
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
8
packages/blueprint/src/nodes/time/index.ts
Normal file
8
packages/blueprint/src/nodes/time/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Time Nodes - Time-related utility nodes
|
||||
* 时间节点 - 时间相关的工具节点
|
||||
*/
|
||||
|
||||
export * from './GetDeltaTime';
|
||||
export * from './GetTime';
|
||||
export * from './Delay';
|
||||
Reference in New Issue
Block a user