蓝图编辑器 Blueprint
蓝图(Blueprint)是 HiEasyX 的节点图编辑器。它不只是“画线连盒子”——它自带执行引擎,能把连好的图跑起来。你可以用它做材质编辑器、行为树、流程图、着色器节点,甚至简单的可视化脚本。
核心数据模型
HXNodeGraph
一张图由节点和连线组成:
struct HXNodeGraph {
std::vector<HXNode> Nodes;
std::vector<HXNodeLink> Links;
};
HXNode
struct HXNode {
HXString Id; // 全局唯一标识
HXString Title; // 显示标题
HXString Icon; // 图标(可留空)
HXNodeCategory Category; // 分类(决定标题栏颜色)
HXString TypeName; // 执行引擎用类型标识(如 "Add", "Branch")
HXPoint Position; // 世界坐标(非屏幕坐标)
HXPoint Size; // 节点尺寸(通常自动计算)
std::vector<HXNodePin> Inputs;
std::vector<HXNodePin> Outputs;
std::vector<HXNodeValue> Values; // 内部默认值/常量值
bool Selected = false;
bool Collapsed = false;
};
永远不要手写固定字符串当 Id。每次创建节点请调用 HX::MakeBlueprintNodeId(),否则复制粘贴、动态创建节点时会发生 ID 冲突,导致连线错乱或选中异常。
HXNodePin
struct HXNodePin {
HXString Id;
HXString Label;
HXPinType Type; // 数据类型:Float, Int, Bool, String, Vector, Color, Texture, Flow, Ref ...
HXPinDirection Direction; // Input / Output
HXString DefaultValue;
bool Connected = false;
HXPoint ScreenCenter; // 每帧自动计算,用于画线
bool Hovered = false;
};
你不需要手动设置 Offset。BeginBlueprint 的渲染器会根据节点大小、标题栏高度、行高自动排布每个 Pin 的 ScreenCenter。
HXNodeLink
struct HXNodeLink {
HXString StartPinId; // 输出 Pin 的 Id
HXString EndPinId; // 输入 Pin 的 Id
};
坐标系统
蓝图使用世界坐标存储节点位置,使用屏幕坐标渲染和交互。鼠标事件、框选、拖拽都在屏幕空间;节点位置、连线插值在世界空间。
世界坐标 ↔ 屏幕坐标
HXPoint WorldToScreen(const HXPoint &worldPos,
const HXPoint &canvasOffset,
float zoom);
HXPoint ScreenToWorld(const HXPoint &screenPos,
const HXPoint &canvasOffset,
float zoom);
参数
| 参数 | 说明 |
|---|---|
worldPos / screenPos | 待转换的坐标 |
canvasOffset | 画布平移偏移量(通常是 Profile.Offset) |
zoom | 缩放倍数(1.0 = 原始大小) |
蓝图支持“缩放至鼠标指针”:滚轮事件发生时,先用 ScreenToWorld 把鼠标位置转成世界坐标,调整 zoom 后,再用 WorldToScreen 反推新的 canvasOffset,让鼠标指向的世界点保持在屏幕同一位置。
主 API
BeginBlueprint / EndBlueprint
bool BeginBlueprint(const HXString &id, BlueprintProfile &profile);
void EndBlueprint();
参数
| 参数 | 类型 | 说明 |
|---|---|---|
id | HXString | 蓝图的 IMGUI Id,需稳定。 |
profile | BlueprintProfile & | 配置与状态,包含 Graph、Offset、Zoom、SelectedNodeId 等。 |
返回值
bool — 始终为 true(与 HiEasyX 其他 BeginXxx 风格一致)。
BlueprintProfile 关键字段
struct BlueprintProfile {
HXNodeGraph *Graph = nullptr; // 必须提前构造并传入
HXPoint Offset = {0, 0}; // 画布平移(像素)
float Zoom = 1.0f; // 缩放
HXString SelectedNodeId; // 当前选中节点
bool ContextMenuRequested = false; // 右键菜单信号
HXPoint ContextMenuPos; // 右键菜单位置(屏幕坐标)
// ... 内部状态省略
};
节点工厂与 ID
MakeBlueprintNodeId
HXString MakeBlueprintNodeId();
生成全局唯一的节点 Id。基于内部计数器,线程不安全(IMGUI 单线程设计)。
MakeBlueprintNode
HXNode MakeBlueprintNode(const HXString &title,
const HXString &category,
const HXString &typeName);
快速构造一个标准节点骨架,Pin 需要你后续手动填充。
序列化
void SaveBlueprint(const HXNodeGraph &graph, const HXString &filepath);
bool LoadBlueprint(HXNodeGraph &graph, const HXString &filepath);
蓝图序列化目前采用管道分隔的文本格式。这是 Phase 7 的临时实现,后续会迁移到带版本号的结构化格式(如类 JSON)。生产使用建议在外层做版本校验与迁移逻辑。
执行引擎
蓝图不只是画板——它能跑起来。
HXBlueprintValue
多类型值容器,替代了早期只支持 float 的备忘录:
struct HXBlueprintValue {
enum Type { Bool, Int, Float, String, Vector, Color };
// 内部 union + type tag,可隐式转换构造
};
HXBlueprintRuntime
struct HXBlueprintRuntime {
std::unordered_map<HXString, HXBlueprintValue> Variables; // 变量表
std::unordered_map<HXString, HXBlueprintValue> PinValues; // Pin 缓存
std::vector<HXString> Log; // 执行日志
HXString LastError; // 最后错误
};
Variables:跨节点持久状态,如Set Variable/Get Variable的存储。PinValues:单帧执行缓存,避免同一个 Pin 被多次求值。ForLoop每次迭代会自动清空它,确保循环体节点能重新求值。
ExecuteBlueprint
void ExecuteBlueprint(HXNodeGraph *graph, HXBlueprintRuntime &runtime);
执行规则
| 节点类型 | 执行行为 |
|---|---|
| 数据节点(如 Add、Sine) | 从左到右求值,结果写入输出 Pin。 |
| 控制流节点(如 Branch) | 按 Flow 类型 Pin 的连线走,只执行命中分支。 |
| Sequence | 依次执行所有 Then 输出。 |
| ForLoop | 迭代 Start 到 End,每轮清空 PinValues 后重跑 Loop Body。 |
| Variable | 输出变量名引用(Ref 类型),由 Set/Get Variable 解析。 |
自定义节点注册
不修改库源码就能扩展蓝图节点,这是工业级编辑器的门槛。
注册数据节点
void RegisterBlueprintDataNode(
const HXString &typeName,
std::function<HXBlueprintValue(const HXNode&, HXBlueprintRuntime&)> handler
);
注册控制流节点
void RegisterBlueprintFlowNode(
const HXString &typeName,
std::function<void(const HXNode&, HXBlueprintRuntime&, const HXString &outputPinId)> handler
);
注册节点模板(自动出现在右键菜单)
void RegisterBlueprintNodeTemplate(const HXBlueprintNodeTemplate &tmpl);
struct HXBlueprintNodeTemplate {
HXString TypeName; // 执行键
HXString DisplayName; // 菜单显示名
HXString Category; // 菜单分组
std::vector<HXNodePin> Inputs;
std::vector<HXNodePin> Outputs;
std::vector<HXString> DefaultValues;
};
建议在 main 或插件的 OnLoad 中一次性注册。执行引擎查询顺序:先内置节点,再注册节点,最后回退到通用透传。
完整示例:简单的加法蓝图
#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>
void DemoBlueprint() {
static HX::HXNodeGraph graph;
static HX::BlueprintProfile bp;
static bool init = false;
if (!init) {
// 构造两个常量节点和一个 Add 节点
HX::HXNode a = HX::MakeBlueprintNode(HXStr("Constant Float"), HXStr("Constant"), HXStr("ConstantFloat"));
a.Values = {HXStr("5.0")};
a.Outputs.push_back(HX::HXNodePin{HXStr("out"), HXStr("Value"), HX::HXPinType::Float, HX::HXPinDirection::Output});
HX::HXNode b = HX::MakeBlueprintNode(HXStr("Constant Float"), HXStr("Constant"), HXStr("ConstantFloat"));
b.Values = {HXStr("3.0")};
b.Outputs.push_back(HX::HXNodePin{HXStr("out"), HXStr("Value"), HX::HXPinType::Float, HX::HXPinDirection::Output});
HX::HXNode add = HX::MakeBlueprintNode(HXStr("Add"), HXStr("Math"), HXStr("Add"));
add.Inputs.push_back(HX::HXNodePin{HXStr("a"), HXStr("A"), HX::HXPinType::Float, HX::HXPinDirection::Input});
add.Inputs.push_back(HX::HXNodePin{HXStr("b"), HXStr("B"), HX::HXPinType::Float, HX::HXPinDirection::Input});
add.Outputs.push_back(HX::HXNodePin{HXStr("out"), HXStr("Result"), HX::HXPinType::Float, HX::HXPinDirection::Output});
// 连线
graph.Nodes = {a, b, add};
graph.Links = {
{HXStr("out_0"), HXStr("a_2")}, // 注意:示例简化,实际用真实 Pin Id
{HXStr("out_1"), HXStr("b_2")}
};
bp.Graph = &graph;
init = true;
}
// 运行蓝图
HX::HXBlueprintRuntime runtime;
HX::ExecuteBlueprint(&graph, runtime);
// 渲染
if (HX::BeginBlueprint(HXStr("bp_demo"), bp)) {
// 所有渲染在 Begin/End 内部自动完成
HX::EndBlueprint();
}
// 显示执行日志
for (const auto &line : runtime.Log) {
// 可接 Text() 输出到侧边面板
}
}
交互速查表
| 操作 | 行为 |
|---|---|
| 左键单击节点 | 选中 / 取消选中 |
| 左键拖拽节点 | 移动节点(世界坐标跟随) |
| 右键拖拽空白处 | 平移画布 |
| 滚轮 | 以鼠标位置为中心缩放 |
| 空白处左键拖拽框选 | 框选多个节点 |
Delete 键 | 删除选中节点及关联连线 |
Ctrl+C / Ctrl+V | 复制 / 粘贴(自动重生成 Id) |
| 右键单击空白处(无拖拽) | 触发 ContextMenuRequested,可接 PopupMenu |
| 拖拽输出 Pin 到输入 Pin | 创建连线 |
| 拖拽输出 Pin 到空白释放 | 出现上下文菜单创建新节点并自动连接 |
总结
| 能力 | 关键词 |
|---|---|
| 无限画布 | 世界坐标 + 缩放平移 |
| 数据流 | Pin 求值 + PinValues 缓存 |
| 控制流 | Branch, Sequence, ForLoop |
| 可扩展 | RegisterBlueprintDataNode, RegisterBlueprintFlowNode, RegisterBlueprintNodeTemplate |
| 序列化 | SaveBlueprint, LoadBlueprint |
| 执行 | ExecuteBlueprint |
蓝图系统是 HiEasyX 迈向引擎级编辑器的核心拼图。把节点连起来,然后看着它跑起来——这是可视化编程的魔法 ✨
运行效果
截图占位符:请补充 $name 的运行效果截图。
截图占位符:请补充
蓝图编辑器 Blueprint 运行效果的运行效果截图,保存为./assets/蓝图编辑器 Blueprint_view.png。`n