跳到主要内容

蓝图编辑器 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 的唯一性

永远不要手写固定字符串当 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;
};
Pin 偏移自动计算

你不需要手动设置 OffsetBeginBlueprint 的渲染器会根据节点大小、标题栏高度、行高自动排布每个 Pin 的 ScreenCenter

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();

参数

参数类型说明
idHXString蓝图的 IMGUI Id,需稳定。
profileBlueprintProfile &配置与状态,包含 GraphOffsetZoomSelectedNodeId 等。

返回值

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; // 最后错误
};
变量表与 Pin 缓存的区别
  • Variables:跨节点持久状态,如 Set Variable / Get Variable 的存储。
  • PinValues:单帧执行缓存,避免同一个 Pin 被多次求值。ForLoop 每次迭代会自动清空它,确保循环体节点能重新求值。

ExecuteBlueprint

void ExecuteBlueprint(HXNodeGraph *graph, HXBlueprintRuntime &runtime);

执行规则

节点类型执行行为
数据节点(如 Add、Sine)从左到右求值,结果写入输出 Pin。
控制流节点(如 Branch)Flow 类型 Pin 的连线走,只执行命中分支。
Sequence依次执行所有 Then 输出。
ForLoop迭代 StartEnd,每轮清空 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