Unlua編程基礎


UnLua是Tencent針對UE4的腳本解決方案,其目標是使用lua腳本來代替藍圖來編寫業務邏輯,提升開發效率和降低維護成本。目前已在github上開源。

 

主要功能特性

1. 可在lua中通過UE4反射系統零膠水代碼訪問UCLASS, UPROPERTY, UFUNCTION, USTRUCT, UENUM

local UMG = UClass.Load("/Game/Global/UI/UMG/MessagePanel/HelloWorldUMG")
local Widget = UE4.UWidgetBlueprintLibrary.Create(_G.GetCurrentWorld(), UMG)
Widget:AddToViewport(50000)

Widget.centerText:SetText("It's a test. "..UE4.ENetRole.ROLE_Authority)  -- 打印ENetRole.ROLE_Authority枚舉值   輸出:It's a test. 3

local PkgInfo = UE4.FFullyLoadedPackagesInfo()  --定義一個FFullyLoadedPackagesInfo結構體
PkgInfo.FullyLoadType = UE4.EFullyLoadPackageType.FULLYLOAD_Game_PostLoadClass --將枚舉FULLYLOAD_Game_PostLoadClass賦給PkgInfo.FullyLoadType
Widget.centerText:SetText("It's a test. "..PkgInfo.FullyLoadType)  -- 輸出:It's a test. 2

2. 可使用unlua提供的宏來靜態導出反射體系外的類、成員函數、成員變量、全局函數和枚舉

3. 可在lua中重寫(Override )c++中帶有BlueprintImplementableEvent、BlueprintNativeEvent修飾的成員函數

帶有BlueprintImplementableEvent、BlueprintNativeEvent修飾的C++成員函數

/** Event when play begins for this actor. */
UFUNCTION(BlueprintImplementableEvent, meta=(DisplayName = "BeginPlay"))
void ReceiveBeginPlay();

/** On Jumped */
UFUNCTION(BlueprintNativeEvent, Category=Character)
void OnJumped();

/** Get Character Info */
UFUNCTION(BlueprintImplementableEvent,  Category="Character")
bool GetCharacterInfo(int32 &HP,  FVector &Position,  FString &Name);

在綁定的lua中重寫

function BP_PlayerCharacter_C:ReceiveBeginPlay()
    print("ReceiveBeginPlay in Lua!")
    self.Overridden.ReceiveBeginPlay(self)  --通過Overridden來調用被覆蓋的c++函數
end

function BP_PlayerCharacter_C:OnJumped()
    print("OnJumped in Lua!")
    self.Overridden.OnJumped(self) --通過Overridden來調用被覆蓋的c++函數
end

function BP_PlayerCharacter_C:GetCharacterInfo(HP, Position, Name) -- 方式① 效率高
    Position.X = 128.0
    Position.Y = 128.0
    Position.Z = 0.0
    return 99, nil, "Marcus", true
end

function BP_PlayerCharacter_C:GetCharacterInfo(HP, Position, Name) -- 方式② return 99, FVector(128.0, 128.0, 0.0), "Marcus", true
end

4. 可在lua中重寫(Override )藍圖中的所有事件(Event)和函數(Function)

5. 可在lua中重寫(Override )Replication Notify函數

6. 可在lua中重寫(Override )Animation Notify函數

7. 可在lua中重寫(Override )Input Event事件

8. lua中高效調用UE4引擎UFUCNTION(持久化參數緩存、參數傳遞、非常量引用參數和返回值處理)

-- UFUNCTION() void GetPlayerBaseInfo(int32& Level, float& Health, FString& Name) const;
local Level, Health, Name = self:GetPlayerBaseInfo()

-- UFUNCTION() void GetHitResult(FHitResult &HitResult) const;
-- 方式①
local HitResult = FHitResult()
self:GetHitResult(HitResult) -- 更高效
-- 方式②
local HitResult = self:GetHitResult() -- 會產生拷貝,效率低
-- 方式③ 
local HitResult = FHitResult()
local HitResultCopy = self:GetHitResult(HitResult) -- 更高效 等價於self:GetHitResult(HitResult); local HitResultCopy = HitResult;

9. lua中高效使用容器(TArray、TSet、TMap)

TArray用法

local arr = UE4.TArray(0);
arr:Add(1);
arr:Add(2);

local arr2 = UE4.TArray(0);
arr2:Add(3);
arr:Append(arr2);

local len = arr:Length(); // len3為3


--為保持類似table訪問的一致性,TArray的Index從1開始
for i = 1, len do
    arr:Get(i)
end

local t = arr:ToTable();

local arr3 = UE4.TArray(FVector);
local idx1 = arr3:AddUnique(UE4.FVector());
local idx2 = arr3:AddUnique(UE4.FVector()); -- AddUnique失敗。因為數組arr3中已經有一個UE4.FVector()
local idx3 = arr3:AddUnique(UE4.FVector(128.0, 128.0, 0.0));
local v1 = FVector()
v1.X = 256.0
local idx4 = arr3:AddUnique(v1);

local len2 = arr3:Length(); // len2為3

local arr4 = TArray(UE4.AActor)
local tac = UE4.UClass.Load("/Game/MyActor");
local a1 = _G.GetCurrentWorld():SpawnActor(tac, UE4.FTransform(), UE4.ESpawnActorCollisionHandlingMethod.AlwaysSpawn);
arr4:Add(a1);
local len3 = arr4:Length(); // len3為1

TSet用法

local set = UE4.TSet(0);
set:Add(1);
set:Add(2);
set:Add(3);
local len1 = set:Length(); -- len1為3
local ret1 = set:Remove(1); -- ret1為true
local ret2 = set:Remove(10); -- ret2為false

local len2 = set:Length(); -- len2為2

local arr = set:ToArray();
local t = set:ToTable();

set:Clear();
    
local len3 = set:Length(); -- len3為0

TMap用法

local map = UE4.TMap("",0);
map:Add("a", 1);
map:Add("b", 2);
map:Add("c", 3);

local len1 = map:Length(); -- len1為3

local arr1 = map:Keys(); 
local t1 = arr1:ToTable(); -- {"a", "b", "c"}

local arr2 = map:Values(); 
local t2 = arr2:ToTable(); -- {1, 2, 3}

local t = map:ToTable(); -- {1, 2, 3}

local ret1 = map:Remove("a"); -- ret1為true
local ret2 = map:Remove("d"); -- ret2為false

local len2 = map:Length(); -- len2為2

local ret3 = map:Find("b"); -- ret3為2
local val = map:FindRef("b"); -- val為2

map:Clear();

local len3 = map:Length(); -- len3為0

 

10. lua中支持delegate的使用

動態代理(DECLARE_DYNAMIC_DELEGATE*) 

local FloatTrack = UE4.FTimelineFloatTrack();

FloatTrack.InterpFunc:Bind(self, BP_PlayerCharacter_C.OnZoomInOutUpdate);
FloatTrack.InterpFunc:Execute(0.5);
FloatTrack.InterpFunc:Unbind();

動態多播代理(DECLARE_DYNAMIC_MULTICAST_DELEGATE_*)

self.ExitButton.OnClicked:Add(self, UMG_Main_C.OnClicked_ExitButton)
self.ExitButton.OnClicked:Broadcast()
self.ExitButton.OnClicked:Remove(self, UMG_Main_C.OnClicked_ExitButton)
self.ExitButton.OnClicked:Clear()

 

11. lua中高效訪問結構體(struct)

12. 支持修飾符BlueprintCallable、BlueprintPure、Exec的UFUNCTION的缺省參數

13. 支持自定義碰撞(collision)枚舉

Project Settings的Engine --  Collision標簽下

Lua代碼如下:

-- 自定義EObjectTypeQuery枚舉碰撞類型
local ObjectTypes = TArray(EObjectTypeQuery)
ObjectTypes:Add(EObjectTypeQuery.Player)
ObjectTypes:Add(EObjectTypeQuery.Enemy)
ObjectTypes:Add(EObjectTypeQuery.Projectile)
local bHit1 = UKismetSystemLibrary.LineTraceSingleForObjects(self, Start, End, ObjectTypes, false, nil, EDrawDebugTrace.None, HitResult, true)

-- 自定義ETraceTypeQuery枚舉碰撞類型
local bHit2 = UKismetSystemLibrary.LineTraceSingle(self, Start, End, ETraceTypeQuery.Weapon, false, nil, EDrawDebugTrace.None, HitResult, true)

 

14. 支持編輯器server/clients模擬

15. 支持從藍圖生成Lua template模板lua文件

16. 支持協程(coroutine)實現的UE4的Latent函數,同步寫法完成異步邏輯

--//=============================================================================
--// Latent Actions
--
--/** 
-- * Perform a latent action with a delay (specified in seconds).  Calling again while it is counting down will be ignored.
-- * 
-- * @param WorldContext    World context.
-- * @param Duration         length of delay (in seconds).
-- * @param LatentInfo     The latent action.
-- */
--UFUNCTION(BlueprintCallable, Category="Utilities|FlowControl", meta=(Latent, WorldContext="WorldContextObject", LatentInfo="LatentInfo", Duration="0.2", Keywords="sleep"))
--static void    Delay(const UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo );

-- 在lua中通過協程來實現Delay延遲函數
coroutine.resume(coroutine.create(function(GameMode, Duration) UKismetSystemLibrary.Delay(GameMode, Duration) end), self, 5.0)

 

C++訪問lua

UnLua::Call  // 調用全局函數

lua_State* L = UnLua::GetState();
if (L != NULLULL)
{
    float DeltaTime = 2.0f;
    UnLua::Call(L, "G6AppTick", DeltaTime); // 調用在G表中的G6AppTick函數
}

UnLua::CallTableFunc // 調用表中函數

lua_State* L = UnLua::GetState();
if (NULL != L)
{
    UnLua::CallTableFunc(L, "G6AppMain", "ApplicationWillEnterBackground"); // G6AppMain在G表中,調用G6AppMain表key為ApplicationWillEnterBackground的函數
    
    UnLua::FLuaRetValues LuaRetValues = UnLua::Call(L, "require", "MyGame.Services.MFVoice"); // lua所在目錄:Content/Script/MyGame/Services/MFVoice.lua
    if (LuaRetValues.IsValid())
    {
        UnLua::FLuaTable LuaTable(LuaRetValues[0]);
        UnLua::CallTableFunc(L, LuaTable, "OnAndroidPermissionRequestReturn");
    }
}

 

模塊

 

模塊名 模塊類型 說明
UnLua Runtime UnLua核心邏輯
UnLuaDefaultParamCollector Program 手機所有ue4中修飾符BlueprintCallable、BlueprintPure、Exec的UFUNCTION的缺省參數
UnLuaEditor Editor

① 藍圖生成Lua Template文件

② UUnLuaIntelliSense靜態導出(供IDE智能感知使用)

③ PIE或Standalone游戲運行時,按Ctrl+L執行Hotfix

UnLuaInfiniteLoopWatcher EditorNoCommandlet

檢查Lua死循環(infinite loop detection)

UnLuaIntelliSense Program 編譯代碼時,UHT會生成ue4引擎反射lua文件(供IDE智能感知使用)
UnLuaIntelliSenseBP Editor 執行編輯器菜單Window - Update BP IntelliSense,可導出項目所有藍圖的lua文件(供IDE智能感知使用)

 

導出IDE用的IntelliSense(智能感知)lua文件

在vscode中,可通過快捷鍵ctrl+shift+p,然后執行reload window來加載最新的IntelliSense(智能感知)lua文件

① 開啟UnLuaIntelliSense.Build.cs中的宏ENABLE_INTELLISENSE=1,在編譯代碼時,UHT會生成ue4引擎反射lua文件

注:文件UnLua\Intermediate\IntelliSense\UE4.lua中會記錄所有ue4反射類型

② 手動執行commandlet命令來生成靜態導出的lua文件 %EngineDir%\Engine\Binaries\Win64\UE4Editor.exe %GameDir%\MyGame.uproject -run=UnLuaIntelliSense

    lua文件保存在UnLua\Intermediate\IntelliSense\StaticallyExports目錄中

③ 在編輯器中執行菜單Window - Update BP IntelliSense,可導出項目所有藍圖IntelliSense(智能感知)用的lua文件

 注:文件UnLua\Intermediate\IntelliSense\UE4_BP.lua中會記錄所有藍圖類型

 

藍圖靜態綁定lua

(1)創建一個藍圖

(2)為藍圖添加UnLuaInterface接口

 (3)給GetModuleName函數返回一個lua文件(相對於項目的Content/Script目錄)

 (4)在藍圖文件的工具欄上點擊“Lua Template”按鈕

創建BP_Game_C.lua文件(注:會生成在項目的Content/Script目錄中)

 (5)在Lua Template文件中編寫業務代碼

 

多個BP不能綁定同一個lua

UnLua的設計是使用LUA腳本擴展BP,一個BP是一個邏輯模塊,析構的時候也會釋放LUA的Module,所以不支持多個BP綁定同一個LUA文件。
這個需求可以通過建立基類BP,綁定LUA,其他BP從基類BP繼承來實現。

 

C++中UObject靜態綁定lua

UObject從IUnLuaInterface接口上派生,然后重寫GetModule_Implementation虛函數返回lua代碼路徑

注:TPSGameMode.lua在項目的Content/Script目錄中

 

為生成的AActor、UObject動態綁定lua

-- ProjClass為要創建的Actor的UClass
-- Transform為UE4.FTransform類型,為在世界空間的Location、Rotation和Scale3D
-- ESpawnActorCollisionHandlingMethod.AlwaysSpawn表示不管出生點周圍有沒有碰撞都出生
-- self為其Owner,可以為nil
-- self.Instigator為其Instigator,可以為nil
-- Weapon.BP_DefaultProjectile_C為要綁定的lua文件。注:全路徑為項目的Content/Script/Weapon/BP_DefaultProjectile_C.lua
local Proj = _G.GetCurrentWorld():SpawnActor(ProjClass, Transform, ESpawnActorCollisionHandlingMethod.AlwaysSpawn, self, self.Instigator, "Weapon.BP_DefaultProjectile_C")

-- ObjClass為要創建的Object的UClass
-- 第2個參數為Outer,可以為nil
-- 第3個參數為Object的Name,可以為nil
-- Objects.ProxyObject為要綁定的lua文件。注:全路徑為項目的Content/Script/Objects/ProxyObject.lua
local ProxyObj = NewObject(ObjClass, nil, nil, "Objects.ProxyObject")

 

參考

[UnrealCircle]騰訊 羅謙 | UnLua-UE4下的Lua腳本插件

UnLua_Programming_Guide_EN

UnLua-UE4下的Lua腳本插件

UE 熱更新:基於 UnLua 的 Lua 編程指南

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM