在虚幻引擎蓝图中集成Rust:高性能模块与可视化脚本的融合实践

📅 2026/7/23 10:09:04 👁️ 阅读次数 📝 编程学习
在虚幻引擎蓝图中集成Rust:高性能模块与可视化脚本的融合实践

1. 项目概述:为什么要在虚幻引擎蓝图里集成Rust?

如果你正在用虚幻引擎(Unreal Engine, 简称UE)做项目,尤其是UE5, 大概率已经深度依赖蓝图(Blueprints)这套可视化脚本系统了。它上手快、迭代迅速, 是原型设计和实现游戏逻辑的利器。但项目规模一大, 性能敏感的核心模块(比如密集的数学计算、复杂的AI决策、高频的网络同步)如果全用蓝图, 可能会遇到性能瓶颈和代码维护的挑战。这时, 很多开发者会想到用C++来重写这些模块, 这当然是UE官方支持的正统路径。

但今天我们要聊的, 是一条更“极客”、更能兼顾性能与开发体验的“野路子”:在虚幻引擎的蓝图里, 直接调用用Rust语言编写的组件。你可能会问, 有现成的C++不用, 为什么是Rust?简单来说, Rust提供了媲美C++的零成本抽象和运行时性能, 同时通过其独特的所有权系统, 在编译期就解决了内存安全和数据竞争的问题。这意味着, 你可以用Rust来编写那些对稳定性和性能要求极高的底层逻辑(比如物理模拟、音频处理、自定义网络协议), 享受“编译通过即基本无误”的安心感, 同时又能通过蓝图保留其快速搭建上层游戏逻辑和UI的优势。

这个组合的潜力在于, 它让你能在一个项目里, 同时运用两种语言的顶级优势:蓝图的生产力与表现力, Rust的性能与安全性。想象一下, 你用Rust写了一个高性能的体素地形生成器或一个复杂的遗传算法“基因计算器”, 然后像使用一个普通的蓝图节点一样, 在关卡蓝图中拖拽、连线, 实时看到计算结果并驱动游戏世界的变化。这不仅仅是技术上的缝合, 更是开发范式上的一次解放。

本指南的目的, 就是为你彻底拆解从零开始, 将一个Rust库集成到虚幻引擎项目中, 并最终在蓝图中无障碍调用的完整流程。我们会从原理讲起, 一步步走过工具链配置、绑定代码生成、项目编译, 直到在蓝图中创建自定义节点并调试。无论你是对Rust感兴趣的UE开发者, 还是想为Rust程序寻找一个强大可视化前端的工程师, 这篇指南都将提供一份可直接“抄作业”的实战方案。

2. 核心思路与架构设计拆解

在开始敲代码之前, 我们必须把整个集成方案的架构和核心思路理清楚。这不是简单的“把两个东西拼在一起”, 而是需要在UE的C++框架和Rust的FFI(外部函数接口)之间, 搭建一座稳固的桥梁。

2.1 核心挑战:跨越语言与ABI的鸿沟

蓝图本质上是一套基于UE C++对象系统的可视化接口。一个蓝图节点, 无论是函数、事件还是变量, 其背后都对应着UCLASS、UFUNCTION、UPROPERTY等UE特有的反射宏标记的C++类。而Rust编译出来的是静态库(.a/.lib)或动态库(.so/.dll), 它没有原生的UObject概念。

因此, 集成的核心挑战有两个:

  1. 接口转换:如何让蓝图认识并调用Rust中的函数?
  2. 内存与生命周期管理:如何安全地在C++(UE)和Rust之间传递和管理数据, 避免内存泄漏、悬垂指针或数据竞争?

2.2 方案选型:为什么是unreal-rscbindgen

社区中有多个探索UE与Rust集成的项目, 如unreal-rsue4-rs等。经过实践对比, 我推荐以unreal-rsunreal-bindings工具链为核心方案。它并非一个全功能的游戏框架, 而是一个专注于“绑定生成”的轻量级工具, 更符合我们“将Rust作为高性能模块集成”的定位。

它的工作流非常清晰:

  1. Rust侧:在你的Rust库中, 使用特定的属性宏(如#[unreal])标记需要暴露给蓝图的函数和结构体。
  2. 绑定生成:运行unreal-bindings工具, 它会扫描你的Rust代码, 自动生成对应的C++头文件(.hpp)和源文件(.cpp)。这些C++代码充当了“胶水层”。
  3. UE侧:将生成的C++代码和编译好的Rust静态库一起加入到你的UE C++项目中编译。生成的C++类会继承自UObject, 并带有完整的蓝图反射支持。
  4. 蓝图调用:在编辑器中, 这些生成的C++类会像其他原生类一样, 可以创建蓝图类、添加函数库节点或直接调用。

为了确保C++和Rust之间数据结构布局的兼容性, 我们还需要cbindgen。它是一个从Rust代码生成C语言头文件的工具, 能确保#[repr(C)]的结构体在两边有一致的内存布局, 这对于传递复杂数据至关重要。

这个组合的优势在于自动化程度高, 避免了手动编写大量容易出错的FFI绑定代码。你只需要关注Rust本身的逻辑实现, 剩下的“翻译”工作交给工具。

2.3 整体架构图(文字描述)

为了更直观, 我将整个数据流和组件关系描述如下:

[你的 Rust 业务逻辑库 (lib.rs)] | | 使用 #[unreal] 等属性宏标记 v [unreal-bindings 工具] | | 扫描并生成 v [C++ 胶水层代码] (.hpp/.cpp) + [Rust 静态库] (.lib/.a) | | | | +--------- 集成到 ------------+ | v [Unreal Engine C++ 项目] | | 编译链接 v [可执行的 UE 编辑器/游戏] | | 反射系统加载 v [蓝图编辑器中的自定义节点] | | 调用 v [运行时执行 Rust 逻辑]

这个架构确保了从Rust源码到蓝图节点的端到端通路是清晰且可维护的。

3. 环境准备与工具链配置

工欲善其事, 必先利其器。这一步的稳定性直接决定了后续所有步骤能否顺利进行。请严格按照顺序操作。

3.1 Rust工具链安装与配置

首先, 确保你安装了最新稳定的Rust工具链。如果你还没有安装, 请访问 rust-lang.org 下载rustup并安装。

# 安装后, 在终端验证 rustc --version cargo --version

接下来, 需要安装Rust的msvc工具链, 因为虚幻引擎在Windows上主要使用MSVC编译器。这是避免后续链接错误的关键。

rustup default stable-msvc # 或者, 如果你已经安装了其他工具链, 可以添加msvc target rustup target add x86_64-pc-windows-msvc

注意:很多人在Windows上初次接触Rust时, 会遇到经典的linker 'link.exe' not found错误。这通常是因为没有安装Visual Studio的C++构建工具或rustup没有正确配置到msvc。请确保已安装“使用C++的桌面开发”工作负载的Visual Studio 2019或2022。安装后, 建议在“开始”菜单中搜索“x64 Native Tools Command Prompt for VS 2022”这类VS开发人员命令提示符中运行Cargo命令, 它会自动设置好所有必要的环境变量。

3.2 安装必要的Rust工具

我们需要安装用于生成绑定的命令行工具。

cargo install cbindgen cargo install --git https://github.com/rusty-bits/unreal-rs.git unreal-bindings

安装unreal-bindings时, 请确保其Git仓库是最新版本, 以兼容最新的UE和Rust特性。

3.3 虚幻引擎C++项目准备

你需要一个C++项目。纯蓝图项目是无法直接集成原生库的。如果你还没有, 请通过Unreal Editor创建一个新的C++项目(例如选择“第三人称游戏”模板)。

  1. 打开Unreal Editor, 创建新项目, 务必选择“C++”标签页下的模板。
  2. 项目创建后, 关闭编辑器。我们将在IDE中操作。
  3. 用Visual Studio或JetBrains Rider打开项目根目录下的.sln解决方案文件。

3.4 项目目录结构规划

清晰的目录结构能极大提升维护效率。我建议在你的UE项目根目录(与.uproject文件同级)下创建如下结构:

YourProject/ ├── YourProject.uproject ├── Source/ │ ├── YourProject/ # 主要的UE C++代码 │ └── YourProject.Target.cs ├── ThirdParty/ # 新建:存放所有第三方依赖 │ └── RustIntegration/ # 新建:我们的Rust集成专用目录 │ ├── rust_lib/ # Rust库的源代码目录 │ │ ├── Cargo.toml │ │ └── src/ │ ├── bindings/ # 存放生成的C++胶水代码 │ └── lib/ # 存放编译好的Rust静态库 (.lib) └── ...

这个结构将Rust相关的所有内容隔离在ThirdParty目录下, 与核心游戏逻辑分离, 干净且符合UE项目的常见规范。

4. 编写与暴露Rust组件

现在, 让我们开始编写真正的Rust代码, 并思考如何将其暴露给外部世界。

4.1 创建Rust库项目

进入我们规划好的ThirdParty/RustIntegration/rust_lib目录, 初始化一个Rust库项目。

cd YourProject/ThirdParty/RustIntegration cargo new rust_lib --lib

编辑生成的Cargo.toml文件。关键点在于:

  1. crate-type设置为staticlib, 因为我们需要编译成静态库供UE链接。
  2. 添加必要的依赖, 特别是unreal-bindings提供的属性宏库。
[package] name = "rust_lib" version = "0.1.0" edition = "2021" [lib] crate-type = ["staticlib"] # 关键:生成静态库 [dependencies] unreal-bindings-macros = { git = "https://github.com/rusty-bits/unreal-rs.git" } # 用于属性宏 [build-dependencies] cbindgen = "0.26" # 用于生成C头文件

4.2 实现核心逻辑并用属性宏标记

打开src/lib.rs, 开始编写你的Rust组件。这里我们以一个简单的“基因计算器”为例, 模拟一个计算密集型任务。

// 引入必要的宏 use unreal_bindings_macros::unreal; // 定义一个需要跨FFI传递的结构体。必须使用 #[repr(C)] 保证内存布局与C兼容。 #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct Gene { pub sequence: [u8; 32], // 假设基因是32字节的序列 pub fitness: f32, } // 标记一个可以被蓝图调用的类。生成的C++类将名为 `URustGeneCalculator`。 #[unreal(class = "RustGeneCalculator", category = "Rust")] pub struct RustGeneCalculator { population: Vec<Gene>, // 内部状态, 不直接暴露给FFI } // 为这个类实现方法。`#[unreal]`宏会处理FFI细节。 impl RustGeneCalculator { // 构造函数, 对应蓝图的“创建RustGeneCalculator对象”节点。 #[unreal(constructor)] pub fn new(initial_pop_size: i32) -> Self { let mut population = Vec::with_capacity(initial_pop_size as usize); // 初始化一些随机基因 for _ in 0..initial_pop_size { population.push(Gene { sequence: [0; 32], // 实际应用中这里应是随机值 fitness: 0.0, }); } Self { population } } // 一个简单的计算函数, 返回基础类型。对应蓝图的一个纯函数节点。 #[unreal(blueprint_callable)] pub fn calculate_fitness_simple(&self, gene_index: i32) -> f32 { if gene_index >= 0 && (gene_index as usize) < self.population.len() { // 模拟一个计算过程 let gene = &self.population[gene_index as usize]; gene.sequence.iter().map(|&b| b as f32).sum::<f32>() / 1024.0 } else { -1.0 } } // 一个处理并返回复杂结构体的函数。注意参数和返回值的FFI处理。 #[unreal(blueprint_callable)] pub fn evolve_population(&mut self, generations: i32) -> Gene { // 模拟进化过程:这里简单地将第一个基因的适应度增加 if let Some(first_gene) = self.population.first_mut() { for _ in 0..generations { first_gene.fitness += 0.01; } *first_gene } else { Gene { sequence: [0; 32], fitness: 0.0 } } } // 静态函数, 不需要对象实例即可调用。对应蓝图的静态函数库节点。 #[unreal(blueprint_callable, static)] pub fn get_random_gene() -> Gene { Gene { sequence: [rand::random(); 32], // 需要添加rand依赖 fitness: rand::random::<f32>(), } } } // 一个不依赖于类的全局函数, 可以暴露为蓝图函数库的一部分。 #[unreal(blueprint_callable)] pub fn add_two_floats(a: f32, b: f32) -> f32 { a + b }

关键点解析:

  • #[repr(C)]: 这是Rust与C/C++交互的基石。它告诉Rust编译器按照C语言的内存布局规则来排列结构体的字段, 确保双方对同一块内存的理解是一致的。
  • #[unreal]属性宏:这是unreal-bindings的核心。它包裹在structfn上, 指示工具需要为这些项生成UE绑定。
    • class: 指定生成的UObject类名(会自动加上‘U’前缀)。
    • category: 在蓝图编辑器中节点所属的分类。
    • blueprint_callable: 表示此函数可以在蓝图中调用。
    • constructor: 标记为构造函数。
    • static: 标记为静态函数。
  • 所有权与借用:注意evolve_population方法接收&mut self, 因为它要修改内部状态。工具会正确地将此映射到C++的非const成员函数。返回Gene时, 由于它是#[repr(C)]且可拷贝(Copytrait), 会通过值传递。

4.3 生成C绑定头文件

为了确保C++胶水层能正确理解我们的Gene结构体, 需要使用cbindgen生成C头文件。在rust_lib目录下创建build.rs文件:

// build.rs extern crate cbindgen; use std::env; use std::path::PathBuf; fn main() { let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let out_dir = PathBuf::from("../bindings"); // 输出到上级的bindings目录 let out_file = out_dir.join("rust_lib_ffi.h").display().to_string(); cbindgen::Builder::new() .with_crate(crate_dir) .with_language(cbindgen::Language::C) .generate() .expect("Unable to generate bindings") .write_to_file(out_file); }

运行cargo build时, 这个构建脚本会自动执行, 在../bindings目录下生成rust_lib_ffi.h文件, 其中包含了Gene结构体的C语言定义。这个文件稍后需要被我们的UE项目包含。

5. 生成UE C++绑定与项目集成

这是连接Rust世界和UE世界最关键的一步。

5.1 使用unreal-bindings生成胶水代码

rust_lib目录下, 运行绑定生成命令。你需要告诉工具你的UE项目的名称(即.uproject文件的名字, 不带后缀)。

# 假设你的UE项目名为 MyRustProject unreal-bindings generate --crate-name rust_lib --unreal-project-name MyRustProject --output-dir ../bindings

这个命令会做以下几件事:

  1. 扫描当前Rust库中所有被#[unreal]宏标记的代码。
  2. 根据标记, 生成对应的C++类(如URustGeneCalculator), 这些类继承自UObjectUBlueprintFunctionLibrary
  3. 生成相应的.hpp(声明)和.cpp(定义)文件到指定的../bindings目录。
  4. 生成的C++代码中, 会包含对Rust函数的外部(extern "C")声明, 并包装成符合UE反射规范的UFUNCTION。

5.2 编译Rust静态库

我们需要为UE项目编译出Rust静态库。由于UE通常是64位, 请确保编译目标正确。

cargo build --release --target x86_64-pc-windows-msvc

编译成功后, 你可以在target/x86_64-pc-windows-msvc/release/目录下找到rust_lib.lib文件。将其复制到我们规划好的ThirdParty/RustIntegration/lib/目录下。

5.3 将绑定与库集成到UE项目

现在, 我们需要让UE的构建系统(UnrealBuildTool, UBT)知道我们的Rust库和绑定代码。

  1. 复制文件:将ThirdParty/RustIntegration/bindings/下生成的所有.hpp.cpp文件, 复制到你的UE项目的Source/MyRustProject/目录下(或者一个你喜欢的子目录, 如Source/MyRustProject/RustBindings/)。同时, 把之前生成的rust_lib_ffi.h也复制过来。

  2. 修改项目构建文件 (.Build.cs):打开你UE项目的Source/MyRustProject/MyRustProject.Build.cs文件。这是配置模块依赖和链接设置的地方。

using UnrealBuildTool; public class MyRustProject : ModuleRules { public MyRustProject(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); PrivateDependencyModuleNames.AddRange(new string[] { }); // --- 以下是新增的Rust集成配置 --- // 1. 添加包含路径, 让编译器能找到我们的绑定头文件 PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "RustBindings")); // 如果rust_lib_ffi.h放在别处, 也需要添加其路径 // 2. 添加Rust静态库的链接 string RustLibPath = Path.Combine(PluginDirectory, "../ThirdParty/RustIntegration/lib"); // 对于开发编辑器目标(Editor) if (Target.Type == TargetType.Editor) { PublicAdditionalLibraries.Add(Path.Combine(RustLibPath, "rust_lib.lib")); } // 对于游戏目标(Game) else { // 可能需要区分Debug/Release, 这里链接Release版 PublicAdditionalLibraries.Add(Path.Combine(RustLibPath, "rust_lib.lib")); } // 3. 可能需要的Windows系统库(Rust标准库可能会用到) PublicSystemLibraries.Add("advapi32.lib"); PublicSystemLibraries.Add("userenv.lib"); // --- 配置结束 --- } }

重要提示PluginDirectory指向的是项目的Plugins目录。我们之前把第三方库放在项目根目录的ThirdParty下, 所以使用Path.Combine(PluginDirectory, "../ThirdParty/...")来定位。你也可以使用Path.Combine(ModuleDirectory, "../../ThirdParty/..."), 关键是找到正确的相对路径。

  1. 重新生成项目文件:在项目根目录(有.uproject文件的地方)右键, 选择“Generate Visual Studio project files”。或者通过命令行运行YourProject.uproject -projectfiles

  2. 编译UE项目:用Visual Studio打开解决方案, 编译你的UE项目(通常是“Development Editor”配置)。如果一切配置正确, 编译应该能成功通过, 并将Rust静态库链接到最终的可执行文件中。

6. 在蓝图中调用Rust函数

最激动人心的部分来了!打开Unreal Editor, 加载你的项目。

6.1 在内容浏览器中创建基于Rust类的蓝图

  1. 在内容浏览器中右键, 选择“蓝图类”。
  2. 在“所有类”搜索框中, 输入RustGeneCalculator。你应该能看到我们生成的RustGeneCalculator类(注意, 在C++中是URustGeneCalculator, 但在蓝图下拉列表中通常不显示‘U’前缀)。
  3. 选择它作为父类, 命名并创建你的蓝图, 比如BP_GeneSimulator

6.2 在事件图表中使用Rust节点

双击打开BP_GeneSimulator的蓝图图表。

  • 创建对象:在图表中右键, 搜索 “Create Rust Gene Calculator”。你应该能看到这个节点, 它对应Rust代码中的#[unreal(constructor)]。你可以给它一个“初始种群大小”的输入。
  • 调用成员函数:从“创建”节点输出的对象引用(Object Reference)拖出引线, 搜索Calculate Fitness SimpleEvolve Population。这些就是你在Rust中标记为#[unreal(blueprint_callable)]的成员函数。尝试连接它们, 传入参数, 打印输出。
  • 调用静态/全局函数:在图表中右键, 直接搜索Get Random GeneAdd Two Floats。这些节点不需要对象实例, 可以直接调用。

你会发现, 这些节点和任何其他蓝图节点(如Print StringGet Actor Location)的使用方式一模一样。参数类型(f32变成floati32变成integerGene结构体会变成一个包含sequence数组和fitness浮点数的结构体变量)被自动映射, 返回值也可以直接连接到其他节点的输入。

6.3 暴露变量与事件

unreal-bindings也支持将Rust结构体的字段暴露为蓝图的属性(UPROPERTY), 或者将Rust函数标记为蓝图可调用的事件。这需要在Rust代码中使用相应的属性, 例如#[unreal(blueprint_readwrite)]标记字段。你可以查阅unreal-bindings的文档来探索更多高级用法。

7. 调试、性能优化与避坑指南

集成成功只是第一步, 让它在实际项目中稳定高效地运行, 还需要注意以下关键点。

7.1 调试技巧

  • Rust侧日志:在Rust代码中使用println!logcrate输出日志。在Windows上, 这些输出会打印到启动Unreal Editor的命令行窗口或Visual Studio的输出窗口。这是追踪Rust逻辑执行的最简单方法。
  • UE侧日志:在生成的C++胶水代码中, 可以使用UE_LOG来记录信息。你需要包含相应的日志头文件并在生成工具中确保支持。
  • 崩溃排查:如果调用Rust函数导致UE崩溃, 首先检查所有FFI边界上的数据传递。确保:
    1. Rust函数不会panic(使用catch_unwind或确保逻辑健壮)。
    2. 指针传递有效, 没有出现悬垂指针。
    3. 结构体对齐正确(#[repr(C)]是关键)。
    4. 在Rust中可以使用Box::into_rawBox::from_raw来管理需要跨FFI生命周期的堆内存, 但必须极其小心。

7.2 性能优化考量

  • 减少FFI调用开销:跨语言调用本身有一定开销。避免在每帧的Tick事件中调用大量非常细粒度的Rust函数。应该将计算密集的任务打包成一次调用, 在Rust侧完成所有计算后返回结果。
  • 数据批处理:如上例中的evolve_population, 一次处理多代, 而不是每代调用一次FFI。
  • 内存零拷贝:对于大型数据(如数组、网格), 理想情况是直接在Rust和UE之间共享内存。这可以通过在Rust中分配内存并传递指针来实现, 但管理起来非常复杂且危险。更安全的方式是使用UE提供的容器(如TArray)并通过FFI传递其数据指针, 或者使用共享内存区域。unreal-bindings对某些常见类型可能有高级支持, 需要查看其文档。

7.3 常见问题与解决方案实录

以下是我在多次集成中踩过的坑和解决方案:

  1. 链接错误:LNK2019: unresolved external symbol ...

    • 问题:UE编译时找不到Rust函数的实现。
    • 排查
      • 确认PublicAdditionalLibraries路径和库文件名(rust_lib.lib)完全正确。
      • 确认Rust库是用--target x86_64-pc-windows-msvc编译的。
      • 在Visual Studio的项目属性中, 检查链接器->输入->附加依赖项, 确保包含了你的.lib文件。
    • 解决:最稳妥的方法是在.Build.cs中使用绝对路径, 或者确保相对路径在编译时能被正确解析。
  2. 崩溃:访问冲突(Access Violation)

    • 问题:调用Rust函数时程序崩溃。
    • 排查
      • 数据结构对齐:百分百确认所有跨FFI传递的结构体都加了#[repr(C)]
      • 字符串传递:Rust的String/&str与C的char*不是一回事。传递字符串时, 应使用CStringCStr, 或者直接传递字节数组和长度。
      • 生命周期:确保从Rust返回的指针或引用所指向的数据, 其生命周期长于C++/蓝图使用它的时间。通常需要返回所有权(如通过Box)或进行深拷贝。
    • 解决:对于复杂数据, 优先考虑通过FFI传递简单、平坦的数据结构, 或使用像protobuf这样的序列化方案。
  3. 蓝图节点找不到

    • 问题:在蓝图编辑器中搜索不到生成的函数。
    • 排查
      • 确认UE项目编译成功, 没有错误。
      • 重启Unreal Editor。有时需要重启才能加载新编译的模块。
      • 检查Rust函数上的#[unreal(blueprint_callable)]属性拼写是否正确。
      • 在“类设置”中查看父类是否正确。
  4. “Rust基因计算器”与性能

    • 场景:如果你的Rust组件真的是一个复杂的遗传算法或物理模拟器。
    • 建议
      • 将Rust库本身设计为无状态的、纯计算的函数集合, 或者将状态封装在Rust侧, 通过一个不透明的句柄(如*mut c_void)在蓝图中传递。这比尝试在蓝图中直接操作复杂的Rust数据结构要简单安全得多。
      • 考虑使用多线程。Rust的优秀并发模型可以在Rust侧并行处理任务, 但计算结果返回UE主线程时需要小心同步。可以使用通道(std::sync::mpsc)将结果发送回一个由UE Tick驱动的查询接口。

将Rust集成到虚幻引擎蓝图, 初看像是一项复杂的技术缝合手术, 但一旦打通了整个流程, 你会发现它开辟了一片新的可能性领域。它允许你在保持UE编辑器强大工作流的同时, 将性能关键、安全性要求高的模块用Rust来实现, 享受其严格的编译期检查带来的稳定性红利。

这套方案目前仍需要一定的配置和调试成本, 但随着unreal-rs等工具的不断成熟, 这个过程会越来越平滑。我个人在几个实验性项目中采用此方案后, 最深的体会是“边界清晰”带来的好处:Rust负责核心算法和数据处理, 生成清晰定义的API;蓝图负责游戏逻辑编排、状态管理和用户交互。两者各司其职, 通过一个自动生成的、类型安全的接口进行通信, 极大地减少了跨模块的Bug。

如果你正在为一个性能瓶颈发愁, 或者单纯想尝试一种更安全的系统级编程语言与游戏引擎的结合, 不妨按照这份指南动手试一试。从一个小函数开始, 比如用Rust重写一段昂贵的数学计算, 体验一下在蓝图里调用它并立刻看到效果的那种顺畅感。这或许会成为你技术栈中又一个有力的工具。