中EN
概念页

蓝图与 C++ 协作

Blueprint & C++ Interop

更新于 2026-10-12 约 9 分钟UE5蓝图C++脚本BlueprintNativeEvent性能

Epic 官方建议:多数项目混用蓝图 (Blueprint) 与 C++,以 C++ 作为基础、在其上构建蓝图类。C++ 适合“编程”——定义系统;蓝图适合“脚本”——在已有系统上定义具体行为。两者通过反射说明符互相调用:C++ 用 BlueprintCallable/BlueprintReadWrite 等暴露给蓝图,用 BlueprintImplementableEvent/BlueprintNativeEvent 让蓝图实现或覆盖 C++ 声明的函数。

编程与脚本的划分

  • 编程:定义系统的指令。例:载具系统的加速、转向等基础功能。
  • 脚本:通过与现有系统交互来定义行为的指令。例:汽车、船等具体载具类型。

C++ 是编程语言,蓝图是脚本语言,但界线并不绝对:C++ 也能写行为,蓝图也能搭系统,最佳拆分取决于场景、项目与团队。

优势对比

维度蓝图C++
擅长定义行为构建新系统
迭代速度创建、修改、编译、测试更快,适合原型较慢
可及性可视化,策划、美术也能用;API 与资产引用更好发现需要程序员
安全性内存模型设计成避免崩溃可直接接触底层,风险更高
性能编译为字节码在虚拟机执行,有额外开销编译为机器码直接在 CPU 执行
能力范围只能用已暴露的功能;不支持多线程可访问底层功能,接入外部系统和库
协作二进制资产,可用 Unreal Diff Tool 对比文本,易于 diff、合并、跨项目共享
规模与调试大型图表难维护;蓝图调试器大文件更易修改;调试工具更强

性能:何时真正有影响

二者差异通常不显著,主要在以下场景才明显:

  • 核心、底层基础设施;
  • 大量使用 I/O 或计算的紧密循环;
  • 处理大数据集的系统;
  • 实例很多且依赖 Tick 的类;
  • 能从多线程获益的场景。

官方建议:蓝图中用定时器或委托安排工作,代替 Tick(见 委托与 Tick 机制);遇到性能问题先用 Unreal Insights 分析,优化最大瓶颈,再考虑把蓝图改写为 C++。

互相调用的方式

下图展示两个方向的调用通道。

flowchart LR
  subgraph CPP["C++ 基类"]
    P1["UPROPERTY(BlueprintReadWrite / ReadOnly)"]
    F1["UFUNCTION(BlueprintCallable / Pure)"]
    E1["UFUNCTION(BlueprintImplementableEvent)"]
    E2["UFUNCTION(BlueprintNativeEvent)<br/>+ _Implementation 默认实现"]
    L["UBlueprintFunctionLibrary 静态函数"]
  end
  subgraph BP["蓝图子类"]
    G["事件图 / 构造脚本"]
  end
  P1 -->|"读写变量"| G
  F1 -->|"调用节点"| G
  L -->|"全局节点"| G
  G -->|"必须实现"| E1
  G -->|"可选覆盖"| E2
方向方式说明
C++ → 蓝图蓝图继承 C++ 类 + UPROPERTY/UFUNCTION 说明符未加说明符的组件、变量、函数在蓝图图表中不可访问
C++ → 蓝图继承 UBlueprintFunctionLibrary暴露静态函数
蓝图 → C++BlueprintImplementableEvent相当于在蓝图中实现的纯虚函数
蓝图 → C++BlueprintNativeEvent虚函数,C++ 默认实现写在 函数名_Implementation;蓝图未覆盖时执行 C++ 版本
蓝图 → C++(UI)UPROPERTY(meta=(BindWidget))访问蓝图中创建的 UserWidget 控件

官方示例:LightSwitchBoth

UCLASS()
class MYPROJECT_API ALightSwitchBoth : public AActor
{
    GENERATED_BODY()
public:
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Switch Components")
    class UPointLightComponent* PointLight1;

    UFUNCTION(BlueprintNativeEvent, Category="Switch Functions")
    void OnOverlapBegin(/* 重叠参数 */);
    void OnOverlapBegin_Implementation(/* 同上 */);   // C++ 默认:切换灯的可见性

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Switch Variables")
    float DesiredIntensity;
};
// 构造函数中:Sphere1->OnComponentBeginOverlap.AddDynamic(this, &ALightSwitchBoth::OnOverlapBegin);
  • 派生蓝图 LightSwitchBoth_BP 中,C++ 添加的组件显示为深蓝色图标(原生继承),蓝图新增的为浅蓝色。
  • 蓝图的 OnOverlap 事件覆盖了 _Implementation,改为随机设置灯光颜色。
  • 构造脚本 (Construction Script) 在 Actor 被移动或暴露的变量改变时重新执行,能即时预览;单靠 C++ 构造函数做不到。
  • EditAnywhere 让每个实例可以有自己的 DesiredIntensity。
  • Blueprintable 类说明符会被继承,AActor 已是 Blueprintable。

把蓝图转为 C++

用蓝图头文件视图 (Blueprint Header View) 生成包含变量与函数声明的 .h,函数实现需要手动迁移到 .cpp;引用大量需要更新时,用核心重定向 (Core Redirects) 自动重映射。

实际项目中的分工

  • 程序员在 C++ 中写基类、核心系统、网络与数据结构,暴露少量清晰的属性、函数和事件。
  • 策划/美术在蓝图子类中配置数值、资产引用,编写具体关卡和物件的行为。
  • 官方的 Lyra 示例项目包含上述所有暴露方式的例子。

常见坑

  • BlueprintPure 节点不缓存结果,每次连线取值都会重新执行,重计算会被悄悄放大。
  • 蓝图里大量 Tick 是常见性能问题;用事件、定时器、委托替代。
  • 首次在纯蓝图项目添加 C++ 类时,需关闭编辑器、在 IDE 中编译后重开;构建配置要与编辑器可执行文件一致。

GEA 的视角:蓝图在脚本谱系中的位置(卷 II 17.8.11、17.9)

GEA 第 17 章两次把 UE 蓝图当作例子:

  • 数据通路通信系统:对象有输入/输出端口,策划用 GUI 连线,数据流(布尔、单位浮点、向量、颜色……)在端口间传递,类型不兼容时禁止连接或自动转换;GEA 说这就是"基于 GUI 的事件系统(如 UE 蓝图)的本质"。它是"简单可配置事件"与"完全自由的脚本语言"之间的中间路线,可以限定一组受控的原子操作并做错误检查。详见 事件与消息传递。
  • GUI 编程的利弊:易用、学习曲线平缓、工具内帮助和大量错误检查;但开发调试维护成本高、额外复杂度可能带来恼人的 bug、策划受工具能力限制。文本脚本则更简单、可搜索替换、能用任何编辑器。这与本页"蓝图适合脚本、C++ 适合编程"的分工说法互补。
  • UnrealScript 的退役:GEA 第 4 版说明 Epic 已不再支持 UnrealScript,开发者改用蓝图或 C++ 定制游戏行为;GEA 还说 UnrealScript 是他见过唯一能让脚本类无缝派生自原生类的脚本语言,而在 UE5 中这一能力由"以 C++ 类为基础构建蓝图类"承担。
  • 解释执行的代价:GEA 指出虚拟机执行字节码通常比原生机器码慢得多,与本页"蓝图编译为字节码在虚拟机执行,有额外开销"一致。脚本架构的完整谱系见 游戏脚本系统。
  1. Coding in UE: Blueprint vs. C++ UE 5.8 文档
  2. C++ and Blueprints (LightSwitchBoth example) UE 5.8 文档
  3. UFunctions UE 5.8 文档
  4. Properties (UPROPERTY) UE 5.8 文档
  5. Game_Engine_Architecture_4th_Edtion_Volume_2_-_Jason_Gregory.pdf
  6. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 17 章 17.8.11 节
  7. Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 II 第 17 章 17.9 节

相关页面

游戏开发 · UE5UObject 与反射系统UObject & Reflection System虚幻引擎 (Unreal Engine) 的对象基类是 UObject。在 C++ 类、属性、函数上加 UCLASS/UPROPERTY/UFUNCTION 等宏,再由构建前的预处理工具虚幻头文件工具 (Unreal Header Tool, UHT) 扫描头文件生成代码,引擎就能在运行时“看见”这些类型——这就是反射系统 (Reflection System)。垃圾回收、序列化、编辑器面板、蓝图、网络复制都建立在它之上。游戏开发 · UE5Gameplay 框架Gameplay FrameworkGameplay 框架 (Gameplay Framework) 是 UE 提供的一组相互配合的类,用来搭建游戏的基本结构:Actor 与组件 (Component) 构成场景中的一切;GameMode 在服务器上定规则并生成玩家;玩家由“控制器 (Controller) + 棋子 (Pawn)”组成;GameState 与 PlayerState 负责把全局与每个玩家的状态复制给所有客户端;GameInstance 跨关卡存活。游戏开发 · UE5委托与 Tick 机制Delegates & TickUE 有两种驱动游戏逻辑的方式:事件驱动——委托 (Delegate) 在某事发生时回调绑定的函数;轮询驱动——Tick 每帧(或按最小间隔)调用 Actor/组件的更新函数,并按 Tick 组 (Tick Group) 与物理模拟排序。官方建议能用定时器、委托就不用 Tick,必须 Tick 时用 Tick 组和 Tick 依赖保证顺序,避免“差一帧”问题。游戏开发 · UE5模块、插件与构建系统Modules, Plugins & Build SystemUE 的代码由模块 (Module) 组成:每个模块是一个独立编译单元,用 [模块名].Build.cs 声明依赖。插件 (Plugin) 是可按项目启用/禁用的代码与内容集合,用 .uplugin 描述,可包含多个模块。虚幻构建工具 (UnrealBuildTool, UBT) 根据 .Target.cs 和 .Build.cs(而不是 IDE 的 .sln)编译,默认把模块编成 DLL 由一个可执行文件加载,也可整体链接成单体 (Monolithic) 可执行文件。游戏开发 · UE5动画蓝图与动画系统Animation Blueprint & Animation SystemUE5 角色动画以动画蓝图 (Animation Blueprint) 为核心:它每帧为骨骼网格体 (Skeletal Mesh) 计算最终姿势。动画蓝图内部用状态机 (State Machine) 组织“何时播什么”,用混合空间 (Blend Space) 按速度、方向等参数连续混合,用蒙太奇 (Animation Montage) 在插槽 (Slot) 中插播攻击、交互等一次性动作,用 Control Rig 做程序化骨骼控制(如贴地)。算出的骨骼姿势最终交给蒙皮 (Skinning) 驱动顶点。游戏开发 · UE5增强输入Enhanced Input增强输入 (Enhanced Input) 是 UE5 默认启用的输入插件,把输入拆成四层:输入动作 (Input Action) 表示“玩家想做什么”,输入映射上下文 (Input Mapping Context) 规定“哪些按键在什么情境下触发它”,输入修饰器 (Input Modifier) 预处理原始值,输入触发器 (Input Trigger) 判定是否触发。上下文可在运行时按优先级增删,从而用数据资产而非代码处理情境切换、按键冲突与改键。游戏开发 · UE5UMG、Slate 与 Common UIUMG, Slate & Common UISlate 是 UE 平台无关的 C++ 声明式 UI 框架(编辑器即用它构建),UMG 是在其之上的可视化控件蓝图层(UE 5.8 API:UWidget 是所有暴露给 UObject 的被包装 Slate 控件的基类),Common UI 插件再在 UMG 之上补充多层界面、输入路由、手柄导航、平台按键图标和样式资产。官方 UMG 最佳实践强调事件驱动更新而非绑定/Tick、固定分辨率与 DPI 制作、用 Scale Box 而非 Render Transform 做永久缩放。游戏开发 · UE5子系统Subsystems子系统 (Subsystem) 是自动实例化、生命周期绑定在宿主对象上的类:Engine、Editor、GameInstance、World、LocalPlayer 五种。宿主创建时自动创建并 Initialize(),宿主关闭时 Deinitialize(),之后无引用即被 GC。它替代了「继承 GameInstance 塞管理器」的老做法,自带按类型的蓝图节点和 Python 访问,特别适合插件。官方编程页只列四种,World 子系统依据 5.8 Python API 补充。游戏开发 · UE5行为树与 StateTreeBehavior Tree & StateTreeUE 的 AI 决策有两套工具:行为树 (Behavior Tree) + 黑板 (Blackboard) 是事件驱动的,条件写成装饰器 (Decorator),用 Simple Parallel、Services 和 Observer Aborts 代替传统并行节点;StateTree 是结合了行为树选择器和状态机转换的通用分层状态机,只在转换触发时按需选择状态,激活路径上的任务并发运行,通过属性绑定传递数据,也是 Mass 的行为层。优先级抢占多的战斗 AI 适合行为树,状态显式、分层或大规模的场景适合 StateTree。游戏开发 · UE5编辑器扩展与 Python 脚本Editor Extension & Python Scripting扩展 UE 编辑器有三条主要路线,门槛由低到高:Python 脚本(只在编辑器中可用,适合批量处理资产和串联外部 DCC 管线)、Editor Utility Widget(基于 UMG 的蓝图编辑器界面,做成自定义标签页),以及 C++ 编辑器插件(如 Editor Standalone Window 模板,用 Slate 的声明式语法构建窗口)。游戏开发字节码模式Bytecode Pattern字节码模式 (Bytecode Pattern) 把行为编码成虚拟机 (VM) 能执行的指令序列,让行为拥有数据一样的灵活性:可以热加载、与引擎核心隔离(沙盒化),还可以由非程序员编写。它是全书最复杂的模式,代价是要写编译器或创作工具,调试器也用不上了,而且这类小语言容易无节制地膨胀。游戏开发游戏脚本系统Game Scripting脚本语言是主要用来让用户控制和定制软件行为的编程语言;在游戏引擎中,它是高层、相对易用、能方便访问引擎大多数常用功能的语言,程序员和非程序员都能用它开发新游戏或给现有游戏做 mod。GEA 先区分数据定义语言和运行时脚本语言,梳理语言分类与游戏脚本的典型特征(解释执行、轻量、快速迭代、方便),介绍 QuakeC、UnrealScript、Lua、Python、Pawn,列出从"脚本回调"到"脚本驱动整个游戏"的六种架构,再以 Naughty Dog 的 DCScript 为例讲虚拟机与原生函数接口,最后是对象引用、事件收发、面向对象脚本、脚本状态机和协作式多线程脚本(GEA4 卷 II 17.游戏开发事件与消息传递Events & Message Passing游戏天然是事件驱动的:爆炸、玩家被敌人发现、血包被捡起都是事件,系统要通知感兴趣的对象并让它们以各自的方式处理。用虚函数 OnExplosion() 通知属于静态类型的后期绑定,迫使基类为所有可能的事件声明虚函数;GEA 主张把事件封装成对象(类型 + 参数),实现动态类型的后期绑定,从而获得单一处理函数、持久性和盲转发。GEA 依次讨论事件类型(枚举 vs 字符串/字符串 id)、参数(派生类、Variant 数组、键值对)、处理器、职责链、兴趣注册、排队与否的利弊,最后是数据驱动的事件系统和以数据通路连接端口的图形化系统(如 UE 蓝图)(GEA4 卷 II 17.8)。游戏开发子类沙盒Subclass Sandbox子类沙盒 (Subclass Sandbox) 让基类提供一组受保护的“提供的操作”和一个抽象的沙盒方法,派生类只用这些操作来实现沙盒方法。这样大量派生类(例如几十种超能力)只耦合到基类,不直接接触引擎的各个系统;代价是基类会越来越臃肿,变成脆弱基类。综合游戏编程模式选型:解耦与性能Choosing Game Programming Patterns本页把《Game Programming Patterns》里的 19 个模式放到同一张取舍图上:大多数模式要么为了解耦(降低改代码时需要装进脑子里的知识量),要么为了性能(利用对数据和硬件的具体假设),而这两个目标经常互相拉扯。下面先讲全书的定位和 Nystrom 的架构观,再给出按问题选模式的路线和与 UE 的对照。

被这些页面引用1 篇

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原