sluaunreal

repository·master·Indexed 24 days ago

https://github.com/tencent/sluaunreal

An Unreal Engine plugin that enables game logic development and hot-fixing using Lua. It provides high-performance integration between Lua and C++/Blueprint, supporting RPCs, property replication, class inheritance, and three binding methods: Blueprint Reflection, C++ Templates (CppBinding), and Static Code Generation via libclang.

Tokens
4.1K
Snippets
7
Records
14
Agent score
34%

What's inside sluaunreal

  1. Overview of Slua-unreal

    master

    Slua-unreal is an Unreal Engine plugin that enables game logic development and hot-fixing using the Lua language. It provides three ways to wrap C++ interfaces to Lua:

    1. Reflection by Blueprint: Automatically exports Blueprint APIs to Lua.
    2. C++ Template: Exports normal C++ functions and classes.
    3. Static Code Generation: Uses libclang to analyze C++ and automate the export of Blueprint and static C++ interfaces.

    Key benefits include significantly faster development cycles by avoiding C++ recompilation for logic changes and supporting hot-updates for live games.

  2. What is lua-wrapper and when to use it

    master

    In slua-unreal, there are three ways to export interfaces to Lua:

    1. Reflection: Any type that supports Blueprints can be accessed directly in Lua via reflection.
    2. LuaCppbinding: Uses C++ template automatic derivation to export Lua interfaces.
    3. lua-wrapper: A static code export tool (written in C#) used to generate interfaces for types that are not supported by the first two methods.

    When to use lua-wrapper: Use it as a supplement when Reflection or LuaCppBinding cannot handle a specific type. However, note its limitations:

    • It cannot export custom types (unless manually added via config).
    • It cannot export types that are already reflective.
    • It is specifically limited to USTRUCT types within the engine.
    • If a type exported via lua-wrapper causes compilation errors, it likely means the type is unsupported.
  3. Understand the three Lua-to-C++ binding methods in slua-unreal

    master

    slua-unreal provides three different technical methods for binding Lua interfaces to C++, allowing you to choose between ease of use and performance:

    1. Blueprint Reflection (蓝图反射方法): Uses Unreal's built-in reflection system. It is the easiest to use but has the lowest performance.
    2. Static Code Generation (静态代码生成): Uses dot-clang (based on libclang) to automatically generate C++ code for bindings. This is highly performant.
    3. CppBinding (模板展开): Involves manually writing binding code using template expansion. This offers performance equivalent to static code generation.

    Performance Comparison Summary:

    • CppBinding is an order of magnitude faster than Blueprint Reflection.
    • Static Code Generation and CppBinding have nearly identical performance characteristics.
    • For common classes like FVector, static generation code is already included in the repository.
  4. Core Features of Slua-unreal

    master

    Slua-unreal provides a comprehensive suite of features for integrating Lua with Unreal Engine:

    • API Exporting: Automatic export of Blueprint APIs, support for Enums, and non-blueprint classes like FVector (with operator overloading).
    • Functionality: Support for RPC (Remote Procedure Call) functions, overriding Blueprint functions with Lua, and using Lua functions as callbacks for Blueprint events.
    • C++ Integration: Exporting normal C++ functions/classes via templates or code generation, and manual addition of non-blueprint functions to UObject (e.g., GetWidgetFromName).
    • Communication: Two-way calling between Blueprint and Lua, including support for out parameters (C++ non-const references).
    • Robustness & Performance: Dead loop detection/reporting, multi-state support for different environments, CPU profiling, and multi-threaded Lua Garbage Collection (GC).
    • Debugging: Specialized VS Code debugger support for in-device debugging, breakpoints, variable inspection, and IntelliSense.
  5. Export custom types using lua-wrapper

    master

    To export additional types (Unreal engine types or your own custom types) that are not included in the default generated files, you must modify the configuration files located in the Tools directory.

    1. Locate the config*.json file in the Tools directory.
    2. Find the "Customs" field.
    3. Specify the target type and the file where that type is defined.
    4. Run the export process.

    Note: If the expected results are not generated, verify that your configuration is correct.

  6. Setup dependencies for lua-wrapper

    master

    The lua-wrapper tool requires the following dependencies to be downloaded and installed before running:

    • Newtonsoft.Json 11.0.2 (.NET Framework 4.6.2)
    • libclang 5.0.0 (32-bit version)

    The tool targets .NET Framework 4.6.2 and runs on both Windows and macOS.

  7. Basic Lua Usage Patterns

    master

    Use the import function to bring Blueprint classes into Lua. You can then instantiate classes, load UI, and handle events.

    -- import blueprint class to use
    local Button = import('Button');
    local ButtonStyle = import('ButtonStyle');
    local TextBlock = import('TextBlock');
    local SluaTestCase=import('SluaTestCase');
    
    -- call static function of uclass
    SluaTestCase.StaticFunc()
    
    -- create Button
    local btn=Button();
    local txt=TextBlock();
    
    -- load panel of blueprint
    local ui=slua.loadUI('/Game/Panel.Panel');
    
    -- add to show
    ui:AddToViewport(0);
    
    -- find sub widget from the panel
    local btn2=ui:FindWidget('Button1');
    local index = 1
    
    -- handle click event
    btn2.OnClicked:Add(function() 
        index=index+1
        print('say helloworld',index) 
    end);
    
    -- handle text changed event
    local edit=ui:FindWidget('TextBox_0');
    local evt=edit.OnTextChanged:Add(function(txt) print('text changed',txt) end);
    
    -- use FVector and operator overloading
    local p = actor:K2_GetActorLocation()
    local h = HitResult()
    local v = FVector(math.sin(tt)*100,2,3)
    local offset = FVector(0,math.cos(tt)*50,0)
    local ok,h=actor:K2_SetActorLocation(v+offset,true,h,true)
    -- import blueprint class to use
    local Button = import('Button');
    local ButtonStyle = import('ButtonStyle');
    local TextBlock = import('TextBlock');
    local SluaTestCase=import('SluaTestCase');
    -- call static function of uclass
    SluaTestCase.StaticFunc()
    -- create Button
    local btn=Button();
    local txt=TextBlock();
    -- load panel of blueprint
    local ui=slua.loadUI('/Game/Panel.Panel');
    -- add to show
    ui:AddToViewport(0);
    -- find sub widget from the panel
    local btn2=ui:FindWidget('Button1');
    local index = 1
    -- handle click event
    btn2.OnClicked:Add(function() 
        index=index+1
        print('say helloworld',index) 
    end);
    -- handle text changed event
    local edit=ui:FindWidget('TextBox_0');
    local evt=edit.OnTextChanged:Add(function(txt) print('text changed',txt) end);
    
    -- use FVector
    local p = actor:K2_GetActorLocation()
    local h = HitResult()
    local v = FVector(math.sin(tt)*100,2,3)
    local offset = FVector(0,math.cos(tt)*50,0)
    -- support Operator
    local ok,h=actor:K2_SetActorLocation(v+offset,true,h,true)
    -- get referenced value
    print("hit info",h)
  8. Extending Actors with Lua

    master

    You can extend an Actor by defining its logic in Lua and overriding Blueprint events like BeginPlay or Tick.

    -- LuaActor.lua
    local LuaActor={}
    
    -- override event from blueprint
    function LuaActor:BeginPlay()
        self.bCanEverTick = true
        print("LuaActor:BeginPlay")
    end
    
    function LuaActor:Tick(dt)
        print("LuaActor:Tick",self,dt)
        -- call LuaActor function
        local pos = self:K2_GetActorLocation()
        -- can pass self as Actor*
        local dist = self:GetHorizontalDistanceTo(self)
        print("LuaActor pos",pos,dist)
    end
    
    return Class(nil, nil, LuaActor)
  9. Calling Lua functions from Blueprint

    master

    To allow a Blueprint to call a Lua function, define the function in your Lua script. The function can accept multiple parameters of various types.

    -- this function called by blueprint
    function bpcall(a,b,c,d)
        print("call from bp",a,b,c,d)
    end
  10. Advanced Slua 2.0: Class Inheritance and Super Calls

    master

    Slua 2.0 supports class inheritance in Lua. You can use __super to call functions from a parent class, and self.Super to call functions from the Blueprint parent.

    -- LuaActor.lua
    local LuaActor ={}
    
    -- override event from blueprint
    function LuaActor:ReceiveBeginPlay()
        self.bCanEverTick = true
        -- set bCanBeDamaged property in parent
        self.bCanBeDamaged = false
        print("actor:ReceiveBeginPlay")
    end
    
    function LuaActor:ReceiveEndPlay(reason)
        print("actor:ReceiveEndPlay",reason)
    end
    
    return Class(nil, nil, LuaActor)
    
    -- LuaBpActor.lua
    local LuaBpActor = {}
    
    -- override event from blueprint
    function LuaBpActor:ReceiveBeginPlay()
        print("bpactor:ReceiveBeginPlay")
        -- call LuaActor super ReceiveBeginPlay
        LuaBpActor.__super.ReceiveBeginPlay(self)
        
        -- call blueprint super ReceiveBeginPlay
        self.Super:ReceiveBeginPlay()
    end
    
    local CLuaActor = require("LuaActor")
    -- CLuaActor is base class
    return Class(CLuaActor, nil, LuaBpActor)
  11. Advanced Slua 2.0: Defining RPC Functions in Lua

    master

    You can define RPC (Remote Procedure Call) functions directly in Lua by specifying them in ServerRPC, ClientRPC, or MulticastRPC tables. You must define the Params list using EPropertyClass types.

    -- LuaActor.lua
    local LuaActor = 
    {
        ServerRPC = {},     -- C2S RPC list
        ClientRPC = {},     -- S2C RPC list
        MulticastRPC = {},  -- NetMulticast RPC list
    }
    
    local EPropertyClass = import("EPropertyClass")
    
    LuaActor.ServerRPC.TestServerRPC = {
        Reliable = true, 
        Params = 
        {
            EPropertyClass.Int, 
            EPropertyClass.Str, 
            EPropertyClass.bool, 
        }
    }
    
    -- ... (ClientRPC and MulticastRPC follow same pattern)
    
    function LuaActor:TestServerRPC(ArgInt, ArgStr, ArgBool)
        -- implementation
    end
    
    function LuaActor:TestClientRPC(ArgInt, ArgStr, ArgBool)
        -- implementation
    end
    
    function LuaActor:TestMulticastRPC(ArgInt, ArgStr, ArgBool)
        -- implementation
    end
    -- LuaActor.lua
    local LuaActor = 
    {
        ServerRPC = {},     -- C2S类RPC列表,类似UFUNCTION宏中的Server
        ClientRPC = {},     -- S2C类RPC列表,类似UFUNCTION宏中的Client
        MulticastRPC = {},  -- 多播类RPC列表,类似UFUNCTION宏中的NetMulticast
    }
    
    local EPropertyClass = import("EPropertyClass")
    
    LuaActor.ServerRPC.TestServerRPC = {
        -- 是否可靠RPC
        Reliable = true, 
        -- 定义参数列表
        Params = 
        {
            EPropertyClass.Int, 
            EPropertyClass.Str, 
            EPropertyClass.bool, 
        }
    }
    
    LuaActor.ClientRPC.TestClientRPC = {
        -- 是否可靠RPC
        Reliable = true, 
        -- 定义参数列表
        Params = 
        {
            EPropertyClass.Int, 
            EPropertyClass.Str, 
            EPropertyClass.bool, 
        }
    }
    
    LuaActor.MulticastRPC.TestMulticastRPC = {
        -- 是否可靠RPC
        Reliable = true, 
        -- 定义参数列表
        Params = 
        {
            EPropertyClass.Int, 
            EPropertyClass.Str, 
            EPropertyClass.bool, 
        }
    }
    
    function LuaActor:TestServerRPC(ArgInt, ArgStr, ArgBool)
        
    end
    
    function LuaActor:TestClientRPC(ArgInt, ArgStr, ArgBool)
        
    end
    
    function LuaActor:TestMulticastRPC(ArgInt, ArgStr, ArgBool)
        
    end
  12. Advanced Slua 2.0: Property Replication in Lua

    master

    Slua 2.0 supports defining property replication information (for networking) in Lua via GetLifetimeReplicatedProps. You can specify replication conditions (e.g., COND_InitialOnly, COND_OwnerOnly) and types (including arrays).

    -- LuaActor.lua
    local LuaActor ={}
    
    function LuaActor:GetLifetimeReplicatedProps()
        local ELifetimeCondition = import("ELifetimeCondition")
        local FVectorType = import("Vector")
        return {
            { "Name", ELifetimeCondition.COND_InitialOnly, EPropertyClass.Str},
            { "HP", ELifetimeCondition.COND_OwnerOnly, EPropertyClass.Float},
            { "Position", ELifetimeCondition.COND_SimulatedOnly, FVectorType},
            { "TeamateNameList", ELifetimeCondition.COND_None, EPropertyClass.Array, EPropertyClass.Str},
            { "TeamatePositions", ELifetimeCondition.COND_None, EPropertyClass.Array, FVectorType},
        }
    end
    
    return Class(nil, nil, LuaActor)