How to Create a Rust Plugin for Unreal Engine
Introduction
In this post I’ll explain how to call Rust code in Unreal Engine. The idea is to build a static library, use cbindgen to autogenerate a C/C++11 header file, and load it up using Unreal’s plugin system.
Caveat
You may have a more difficult time getting Rust working on consoles, though I hear it’s possible. Also, it sounds like extra work is needed to use std, so you may want to go with a no_std strategy.
Create the Unreal project
There are two places plugins can exist where UE5 will automatically find them:
- <Unreal Engine Root Directory>/Engine/Plugins/
- <Project Root Directory>/Plugins/
I used the project root.
- Create a new Unreal C++ project. I used the Top Down starter project with default settings and called it MyProject.
- In the MyProjectdirectory (C:\Users\username\Documents\Unreal Projects\MyProject), add aPluginsfolder.
Create the Rust library
- In - Pluginsrun- cargo new --lib TestPlugin. Unreal likes to use Pascal case.
- Open the - TestPluginfolder.
- In - cargo.tomlchange the package name to- test-plugin.
- In - cargo.tomladd:- [lib] crate-type = ["staticlib"]- This tells cargo to build a - .libfile.
- Replace - src/lib.rswith the following content:- #[no_mangle] pub extern fn add_5(num: u32) -> u32 { num + 5 } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let result = add_5(2); assert_eq!(result, 7); } }
- Install - cbindgen:- cargo install --force cbindgen- --forcewill update it if it’s already installed.
- In - TestPluginadd a- cbindgen.tomlwith the following content:- autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" namespace = "test_plugin"
- Now generate the C header file: - cbindgen --config cbindgen.toml --crate test-plugin --output Source/TestPlugin/Public/TestPlugin.h- This creates a header file in - Source/TestPlugin/Public, the default location for header files.
- Run - cargo build --releaseto build the project in- releasemode. You may run into “unresolved external symbol” errors if it’s a- debugbuild. More on that later.
Add the Unreal wiring
- In - TestPluginadd- TestPlugin.upluginwith the following content:- { "FileVersion": 3, "Version": 1, "VersionName": "1.0", "FriendlyName": "Test Plugin", "Description": "", "Category": "Other", "CreatedBy": "", "CreatedByURL": "", "DocsURL": "", "MarketplaceURL": "", "SupportURL": "", "CanContainContent": true, "IsBetaVersion": false, "IsExperimentalVersion": false, "Installed": false }- We need this file so that when we “Generate Visual Studio Project Files” in a later step, our plugin will show in the solution.
- If you generate a new C++ plugin from within the Unreal Editor, it will also add a section called “Modules”, however we can omit it. It’s only needed if your plugin is a C++ module.
 
- In - TestPluginadd- Source/TestPlugin/TestPlugin.Build.cswith the following content:- using UnrealBuildTool; using System.IO; public class TestPlugin : ModuleRules { public TestPlugin(ReadOnlyTargetRules Target) : base(Target) { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; Type = ModuleType.External; string lib = Path.Combine(PluginDirectory, "target\\release\\test_plugin.lib"); string includes = Path.Combine(ModuleDirectory, "Public"); PublicAdditionalLibraries.Add(lib); PublicIncludePaths.Add(includes); } }- With this folder structure, Unreal will find our build file automatically.
- UseExplicitOrSharedPCHsensures our module complies with Unreal’s IWYU conventions.
- PluginDirectorypoints to- C:\Users\username\Documents\Unreal Projects\MyProject\Plugins\TestPlugin.
- ModuleDirectorypoints to- C:\Users\username\Documents\Unreal Projects\MyProject\Plugins\TestPlugin\Source\TestPlugin\.
- Normally, Unreal tries to build C++ modules when you build your project. Since we don’t want it to build anything, we set the TypetoExternal.
- We also tell Unreal where our static library and C header files are. In the future, you could get fancy with this and use Target.Platformto point to a different library file depending on the platform.
 
- In the - MyProjectfolder, right click the- .uprojectfile and click “Generate Visual Studio Project Files”.
- Open - MyProject.sln.
- In - MyProject.Build.cs(C:\Users\username\Documents\Unreal Projects\MyProject\Source\MyProject\MyProject.Build.cs), append “TestPlugin” to- PublicDependencyModuleNames.- PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "NavigationSystem", "AIModule", "Niagara", "EnhancedInput", "TestPlugin" });
- Rebuild the project. 
Calling our Rust code from a C++ class
At this point, you should be able to type #include "TestP in one of your source files and IntelliSense should list your header file you generated earlier. Note that I was having issues with IntelliSense on one of my attempts. You may have to reopen the solution and/or file. The real test is if you can include the header file and build the project successfully.
- In MyProjectPlayerController.cppadd the following includes:#include "Engine/Engine.h" #include "TestPlugin.h"
- In the BeginPlaymethod, add the following:if (GEngine) { int num = test_plugin::add_5(3); FString msg = FString::FromInt(num); GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, *msg); }
Now when you build and run the game, you should see a glorious 8 appear on screen.
Fixing unresolved external symbols
If you use std, you may see errors like the ones below when you try to build your Unreal project:
1>test_plugin.lib(std-e493bcbfdc66a475.std.9ab95dd99822253f-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __imp_GetUserProfileDirectoryW referenced in function _ZN3std3env8home_dir17h01329e00848f730aE
1>test_plugin.lib(std-e493bcbfdc66a475.std.9ab95dd99822253f-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __imp_NtCreateFile referenced in function _ZN3std3sys7windows2fs20open_link_no_reparse17hee3358b6bcfc697eE
1>test_plugin.lib(std-e493bcbfdc66a475.std.9ab95dd99822253f-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __imp_RtlNtStatusToDosError referenced in function _ZN3std3sys7windows2fs20open_link_no_reparse17hee3358b6bcfc697eE
1>test_plugin.lib(std-e493bcbfdc66a475.std.9ab95dd99822253f-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __imp_NtReadFile referenced in function _ZN3std3sys7windows6handle6Handle16synchronous_read17h4a41a152df556564E
1>test_plugin.lib(std-e493bcbfdc66a475.std.9ab95dd99822253f-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __imp_NtWriteFile referenced in function _ZN3std3sys7windows6handle6Handle17synchronous_write17h4888e2c67110ede7E
1>test_plugin.lib(std-e493bcbfdc66a475.std.9ab95dd99822253f-cgu.0.rcgu.o) : error LNK2019: unresolved external symbol __imp_BCryptGenRandom referenced in function _ZN3std3sys7windows4pipe9anon_pipe17h0951b5613efec006E
These methods are from system libraries. To find out what system libraries your crate uses, run this command:
cargo rustc -q -- --print=native-static-libs
-q = “Do not print cargo log messages”
The output should look something like this:
note: Link against the following native artifacts when linking against this static library. The order and any duplication can be significant on some platforms.
note: native-static-libs: kernel32.lib advapi32.lib bcrypt.lib kernel32.lib ntdll.lib userenv.lib ws2_32.lib kernel32.lib ws2_32.lib kernel32.lib ntdll.lib kernel32.lib msvcrt.lib
Now that we know the names, we can add them to TestPlugin.Build.cs, and Unreal will check the default paths to find them.
PublicSystemLibraries.Add("kernel32.lib");
PublicSystemLibraries.Add("advapi32.lib");
PublicSystemLibraries.Add("bcrypt.lib");
PublicSystemLibraries.Add("kernel32.lib");
PublicSystemLibraries.Add("ntdll.lib");
PublicSystemLibraries.Add("userenv.lib");
PublicSystemLibraries.Add("ws2_32.lib");
PublicSystemLibraries.Add("kernel32.lib");
PublicSystemLibraries.Add("ws2_32.lib");
PublicSystemLibraries.Add("kernel32.lib");
PublicSystemLibraries.Add("ntdll.lib");
PublicSystemLibraries.Add("kernel32.lib");
PublicSystemLibraries.Add("msvcrt.lib");
PublicAdditionalLibraries.Add(lib);
Your project should now build successfully.
Support the blog! Buy a t-shirt or a mug! Or just buy me a beverage🧋. Thanks!
Check out my other projects:
- Emerald Geography - Learn geography on a 3D globe.
- Word Rummage - Random word generator, word finder, and dictionary.
- Color Changer web extension for Firefox and Chrome - Change colors of websites to make them easier to read.