Jul 5, 2026
I recently integrated UnLua into a UE 5.8 project. The main problem was not “how to use UnLua”, but that older UnLua source code runs into a set of compile-time compatibility issues under UE 5.8. This article organizes the full setup process: why UnLua is useful, what changed in UE 5.8, and a practical configuration flow that can actually be applied.
This article is based on a UE 5.8 compatibility pass for Tencent UnLua. It is not an official release. It is only a record of one UE 5.8 integration practice.
Original project: https://github.com/Tencent/UnLua
UnLua connects Lua scripts to Unreal Engine, so part of the gameplay logic can be moved out of C++ or Blueprints and organized as scripts.
The most direct benefit is faster iteration. C is well suited for low-level systems, performance-sensitive logic, and stable frameworks. But if every gameplay flow, UI behavior, quest script, and trigger rule is written in C, every small change has to go through compilation, hot reload, or even an editor restart. That slows down the workflow. Lua files are plain text, so they are cheap to edit and organize, which makes them suitable for frequently changing business logic.
Another important value of UnLua is that it can connect to UE’s object system. Lua code can access reflected objects such as UObject, UClass, UFunction, and UProperty, and can also bind to Blueprint classes. This allows a project to keep a relatively clear division of responsibility:
If the project may need hot updates in the future, Lua is also a more natural fit. Hot updates still require a complete system for asset management, version control, download validation, and so on, but script-layer logic is more suitable for runtime loading and replacement than C++ modules.
There is also a more practical point: Lua files are text, which makes them friendly to agent-based or AI-assisted development. Compared with directly modifying Blueprint assets, generating, reviewing, and rolling back Lua logic is much lighter.
The official UnLua version is not directly prepared for UE 5.8. UE 5.8 changes parts of the build toolchain and engine APIs. If old plugin source code is dropped directly into a project, a common result is seeing this prompt when opening the project:
Missing Modules: UnLua, UnLuaEditor
This prompt does not mean the project is broken. It means .uproject has enabled UnLua, but UE cannot find plugin DLLs that are usable with the current engine version. The usual cause is that the plugin has not been compiled yet, or it was compiled for another UE version.
During this UE 5.8 adaptation, the main changes were:
UE 5.8 uses a newer build toolchain
In local verification, UE 5.8 used its bundled .NET 10 SDK, the VS2022 MSVC toolchain, and Windows SDK. Some older UnLua UBT/UHT helper projects were still written for an older framework, so they ran into incompatibilities around net6.0, old enum values, and old APIs.
The UHT plugin API changed
UnLua’s default parameter collector depends on the UHT export flow. In UE 5.8, modules and packages need to be traversed through Session.Modules. The old approach does not compile directly.
More UObject field links use TObjectPtr
In UE 5.8, fields such as UStruct::Children and UField::Next are already TObjectPtr<UField>. Old code that manipulates those linked lists as UField** will hit type mismatches.
The Delegate API changed
Multicast delegate calls need to adapt to the newer ProcessDelegate<UObject>(Params) form.
The metadata API changed
UMetaData::CopyMetadata needs to become FMetaData::CopyMetadata, with the corresponding include added.
Format string checks are stricter
FString::Printf now favors format strings that can be checked at compile time. Old code that passes a variable as the format string can fail under UE 5.8.
Lua internal type names conflict with UE type names
Lua’s internal TString can conflict with UE 5.8 types or aliases. When including Lua internal headers, the name needs to be isolated.
So the key to setting up UnLua in UE 5.8 is not simply “copy the plugin into the project”. The first requirement is making sure the UnLua source code can be fully compiled by the UE 5.8 toolchain.
You need:
If the project is Blueprint-only, it is recommended to add an empty C class in UE first, so the project generates C project structure such as Source, .Target.cs, and .Build.cs. UnLua is a C++ plugin, and it eventually needs to be compiled through Unreal Build Tool.
After configuration, the project structure should roughly look like this:
YourProject/
Config/
DefaultUnLuaEditor.ini
Content/
Script/
Main.lua
Plugins/
UnLua/
UnLua.uplugin
Source/
Content/
Config/
Source/
YourProject.uproject
In this structure:
Plugins/UnLua is the plugin itself.Config/DefaultUnLuaEditor.ini configures UnLua editor behavior and the startup module.Content/Script/Main.lua is the default Lua startup entry.Copy the UE 5.8-adapted UnLua plugin into the project:
YourProject/Plugins/UnLua
If the project already has an old Plugins/UnLua directory, back it up first, then place the new plugin there.
Do not treat Binaries or Intermediate generated by an old version as reliable artifacts. UE plugins are best recompiled on the current machine and current engine version.
Open the project’s .uproject file and add UnLua to the Plugins array:
{
"Name": "UnLua",
"Enabled": true
}
A simplified example:
{
"FileVersion": 3,
"EngineAssociation": "5.8",
"Modules": [
{
"Name": "YourProject",
"Type": "Runtime",
"LoadingPhase": "Default"
}
],
"Plugins": [
{
"Name": "UnLua",
"Enabled": true
}
]
}
If the project already has other plugins, do not overwrite the original Plugins array. Just append the UnLua entry.
Create or modify:
Config/DefaultUnLuaEditor.ini
Write:
[/Script/UnLuaEditor.UnLuaEditorSettings]
bAutoStartup=True
bEnableDebug=True
HotReloadMode=Manual
LuaVersion=lua-5.4.3
[/Script/UnLua.UnLuaSettings]
StartupModuleName=Main
The key setting here is:
StartupModuleName=Main
It means UnLua will load Content/Script/Main.lua when it starts.
Create this directory:
Content/Script
Then create:
Content/Script/Main.lua
Write a minimal startup module:
local M = {}
function M.Initialize()
print("UnLua initialized")
end
return M
This file is only used to verify the UnLua startup path. Once the project starts implementing real logic, you can split modules here, initialize systems, and load other Lua files.
Open PowerShell and run:
& "C:\Program Files\Epic Games\UE_5.8\Engine\Build\BatchFiles\Build.bat" -projectfiles -project="C:\YourProject\YourProject.uproject" -game -rocket -progress
Replace C:\YourProject\YourProject.uproject with your own project path.
This step lets UE recognize the plugin, modules, and project structure again.
Run:
& "C:\Program Files\Epic Games\UE_5.8\Engine\Build\BatchFiles\Build.bat" YourProjectEditor Win64 Development -Project="C:\YourProject\YourProject.uproject" -WaitMutex -FromMsBuild -NoHotReloadFromIDE
You need to replace two parts:
YourProjectEditor: the project name plus EditorC:\YourProject\YourProject.uproject: the full path to the project’s .uprojectIt is recommended to keep:
-NoHotReloadFromIDE
This can avoid compile failures caused by Live Coding or Hot Reload locking modules. When configuring plugins, it is best to close UE Editor and the Live Coding Console before running a full compilation.
After compilation succeeds, the project should generate:
Plugins/UnLua/Binaries/Win64/UnrealEditor-UnLua.dll
Plugins/UnLua/Binaries/Win64/UnrealEditor-UnLuaEditor.dll
After these two DLLs exist, opening .uproject usually should no longer show:
Missing Modules: UnLua, UnLuaEditor
If you are adapting the official UnLua source code to UE 5.8 by yourself, these are the actual areas changed during this pass.
Location:
Source/ThirdParty/Lua/Lua.Build.cs
The old code used enum checks such as WindowsCompiler.VisualStudio2019. This enum no longer applies in UE 5.8. It can be changed to a string check:
Target.WindowsPlatform.Compiler.ToString() == "VisualStudio2019"
Location:
Source/UnLuaDefaultParamCollectorUbtPlugin/UnLuaDefaultParamCollectorUbtPlugin.ubtplugin.csproj
Change:
<TargetFramework>net6.0</TargetFramework>
to:
<TargetFramework>net10.0</TargetFramework>
Also remove the unnecessary Microsoft.CSharp package reference so it matches UE 5.8’s bundled .NET 10.
Location:
Source/UnLuaDefaultParamCollectorUbtPlugin/UnLuaDefaultParamCollectorUbtPlugin.cs
In UE 5.8, modules need to be traversed from:
Session.Modules
Then packages are traversed from each module. The old UHT export approach cannot be used directly.
Location:
Source/UnLua/Public/UnLuaSettings.h
Change:
MetaClass="Object"
to:
MetaClass="/Script/CoreUObject.Object"
Location:
Source/UnLua/Public/UnLuaTemplate.h
Add compatibility definitions for:
TChooseClass
TIsTriviallyDestructible
TIsTriviallyCopyConstructible
These are template types used by older UnLua code that need compatibility definitions under UE 5.8.
Locations:
Source/UnLua/Private/LuaCore.cpp
Source/UnLua/Private/LuaEnv.cpp
When including Lua internal headers, isolate the name like this:
#define TString LuaTString
// include Lua internal headers
#undef TString
Location:
Source/UnLua/Private/LuaEnv.cpp
Remove the incompatible:
AsyncLoading
Keep:
Async
Location:
Source/UnLua/Private/LuaFunction.cpp
Add:
#include "UObject/MetaData.h"
Then change:
UMetaData::CopyMetadata(...)
to:
FMetaData::CopyMetadata(...)
Location:
Source/UnLua/Private/ReflectionUtils/FunctionDesc.cpp
Change multicast delegate invocation to:
ScriptDelegate->ProcessDelegate<UObject>(Params);
Location:
Source/UnLua/Private/UnLuaConsoleCommands.cpp
Do not pass a variable as the format argument to FString::Printf. Use a literal such as TEXT(R"(... )") directly instead.
Location:
Source/UnLua/Private/UnLuaBase.cpp
One log format string requires two %s arguments but only passes one argument. Add:
ANSI_TO_TCHAR(__FUNCTION__)
Location:
Source/UnLua/Private/LuaOverridesClass.cpp
In UE 5.8:
UStruct::Children
UField::Next
are TObjectPtr<UField>, so linked-list operations need to use:
TObjectPtr<UField>*
instead of the old UField**.
This means UE still cannot find plugin modules that are usable with the current engine version. Check:
Plugins/UnLua exists..uproject enables UnLua.YourProjectEditor has been compiled successfully.UnrealEditor-UnLua.dll and UnrealEditor-UnLuaEditor.dll have been generated.It is recommended to close the editor and Live Coding Console, then compile again.
Add this to the build command:
-NoHotReloadFromIDE
If it still fails, fully close UE Editor and the Live Coding Console, then run Build.bat again.
Add an empty C class in the UE editor first, generate the C project structure, and then configure UnLua. Otherwise, many C++ plugin-related build steps will be incomplete.
In general, it is not recommended to commit:
Plugins/UnLua/Binaries/
Plugins/UnLua/Intermediate/
They are build artifacts generated for the current machine and current engine version. It is better to commit source code and configuration, then let each machine compile its own binaries.
Recommended .gitignore:
Binaries/
Intermediate/
Saved/
DerivedDataCache/
*.pdb
*.dll
*.modules
*.target
When setting up UnLua in UE 5.8, three things matter most.
First, use UnLua source code that has already been adapted for UE 5.8. If old source code is copied directly into a project, it is likely to fail around UBT/UHT, Delegate APIs, TObjectPtr, metadata APIs, and similar areas.
Second, connect the plugin to the project: copy it to Plugins/UnLua, enable it in .uproject, add DefaultUnLuaEditor.ini, and create Content/Script/Main.lua.
Third, use UE 5.8’s Build.bat to compile YourProjectEditor Win64 Development, so the current engine version generates its own UnLua and UnLuaEditor DLLs.
Once the plugin modules compile successfully, prompts such as Missing Modules: UnLua, UnLuaEditor should disappear. After that, gameplay, UI, or level logic can gradually be moved into Lua.