赞
踩
发表于 2020-03-24 10:50 | 更新于 2020-05-19 16:57 | 分类于 UE4 , Lua | 字数统计 4.2k
UE使用的是C++这种编译型语言,在编译之后就成了二进制,只有通过玩家重新安装才能打到更新游戏的目的。但是对于游戏业务而言,对于需求调整和bug修复时间要求非常迫切,频繁地让玩家更新App是不能接受的,游戏项目一般使用Lua作为游戏业务的脚本语言,是为了把运行时不可变的C++代码变成运行时可更新的Lua代码。
UE官方没有提供Lua的支持,但是腾讯开源了UnLua,在我当前的项目使用了,这两天我梳理了一下UnLua的资料(主要是官方文档、issus、宣讲PPT),加上自己测试UnLua写了一个小Demo的感悟,形成了本篇UE结合UnLua的编程指南,主要是总结使用UnLua来写业务的一些基本方法和坑,方便查看,本篇文章会持续更新。
另外,Lua文件打包成Pak可以用我之前开源的工具:hxhb/HotPatcher,而且我基于UnLua自己修改了一个版本,添加了一些额外的优化,源码集成了Luasocket/Luapanda/
lpeg
/Sproto
/Luacrypt
库,可以直接使用LuaPanda调试,Github地址为:hxhb/debugable-unlua.
参考资料:
这些是UnLua官方仓库里我摘录出来的一些可能有坑的地方。
BlueprintNativeEvent
/BlueprintImplementationEvent
/Replication Notify
/Animation Notify
/Input Event
。self.Super
不会递归向下遍历继承层次中的所有基类。issus/131UE4.TArray
的下标规则是从1开始的,与引擎中TArray
以0开始不同.issues/41GetActorTransform
其实在lua里要用GetTransform
,在lua里使用的名字都是C++真实定义的函数名字,而不是DisplayName
的名字。导出符号
UnLua提供了对引擎内反射符号的导出,也可以自己静态导出非反射类的符号,并且提供了一个Commandlet
类UUnLuaIntelliSenseCommandlet
导出这些符号。
下面简单介绍一下Commandlet
的调用方法:
UE4Editor-cmd.exe PROJECT_PATH.uproject -run=COMMANDLET |
那么UnLua的Commandlet用法为:
D:\UnrealEngine\Epic\UE_4.23\Engine\Binaries\Win64\UE4Editor-cmd.exe C:\Users\imzlp\Documents\UnrealProjectSSD\MicroEnd_423\MicroEnd_423.uproject -run=UnLuaIntelliSense |
执行完毕之后,会生成UnLua/Interamate/IntelliSense
这个目录,里面有引擎导出到lua的符号。
VSCode中使用
在VSCode中安装Emmylua插件,安装之后把上一步生成的IntelliSense
目录添加到vcode的工作区即可。
在UEC++中,当我们重写了一个父类的虚函数,可以通过调用Super::
来指定调用父类实现,但是在Lua中不同。
self.Super.ReceiveBeginPlay(self) |
在Lua使用self.Super
来调用父类的函数。
注意:UnLua里的Super只是简单模拟了“继承”语义,在继承多层的情况下会有问题,Super不会主动向下遍历Super。“父类”不是Class()返回的表的元表,只设置在Super这个field上(元表在插件的c++代码里定义了,这个过程已经固化)。
对于基类的基类
的Super调用:
a.lua local a = Class() function a:test() end return a -------------------------------------- b.lua local b = Class("a") -------------------------------------- c.lua local c = Class("b") function a:test() c.Super.Super.test(self) end |
该问题摘录于UnLua的issus:Class内的Super的使用问题
注意:Lua和原来的类并不是继承关系,而是依附关系,lua依赖于蓝图或者C++的类。Lua覆写的类的函数,相当于给当前类的函数换了一个实现。
当在Lua中重写了一个附属类的UFUNCTION函数时,可以通过下列方法调用,有点类似于Super
但要写成Overridden
:
function BP_Game_C:ReceiveBeginPlay() self.Overridden.ReceiveBeginPlay(self) end |
注意一定要传self
进去,不然Unlua调用的时候执行的参数检查会Crash,在UnLua中调用UE函数有点类似于拿到成员函数的原生指针,必须要手动传this进去。
UnLua在编译时可以开启是否启用UE的namespace(也就是UE的函数都需要加UE4前缀)。
调用方法为:
UE4.UKismetSystemLibrary.PrintString(self,"HelloWorld") |
参数与C++调用的相匹配(具有默认参数的同样可以不写):
UKismetSystemLibrary::PrintString(this,TEXT("Hello")) |
蓝图
覆写的lua代码:
function LoadingMap_C:GetName(InString) UE4.UKismetSystemLibrary.PrintString(self,InString) return true,"helloworld" end |
C++
因为C++的多返回值是通过传递引用参数进去实现的,所以在Lua中这个不太一样。
如下面的C++函数:
// .h UFUNCTION(BlueprintCallable, Category = "GameCore|Flib|GameFrameworkStatics", meta = (CallableWithoutWorldContext, WorldContext = "WorldContextObject")) static bool LuaGetMultiReturnExample(UObject* WorldContextObject, FString& OutString, int32& OutInt, UObject*& OutGameInstance); // .cpp bool UFlibGameFrameworkStatics::LuaGetMultiReturnExample(UObject* WorldContextObject, FString& OutString, int32& OutInt, UObject*& OutGameInstance) { bool bStatus = false; if (WorldContextObject) { OutString = TEXT("HelloWorld"); OutInt = 1111; OutGameInstance = UGameplayStatics::GetGameInstance(WorldContextObject); bStatus = true; } return bStatus; } |
这个函数接收WorldContextObject
的参数,并接收FString
/int32
/UObject*
这三个类型的引用类型,并返回一个bool,那么这个函数该怎么在lua中接收这些参数值的?
local ret1,ret2,ret3,ret4 = UE4.UFlibGameFrameworkStatics.LuaGetMultiReturnExample(self,nil,nil,nil) |
这种local ret1,ret2=func()
的这种写法是Lua里的规则,可以看Programming in Lua,4th的第六章。
注意:接收引用参数的返回值和真正函数返回值的顺序是:ret1,ret2,ret3都是引用参数,最终函数的返回bool是最后一个ret4。
非const引用参数作为返回值需要注意的问题
注意:当调用UFUNCTION函数时,非const引用参数可以忽略,但是非UFUNCTION而是静态导出的函数则不行,因为UnLua对静态导出的函数有参数个数检查。
而且,使用引用作为返回参数的的使用方式需要考虑到下面两种情况:
void GetPlayerBaseInfo(int32 &Level, float &Health, FString &Name) { Level = 7; Health = 77; Name = "Marcus"; } |
这种情况下返回值和传入值是没有任何关系的。在lua中可以这么使用:
local level,heath,name = self:GetPlayerBaseInfo(0,0,""); |
void GetPlayerBaseInfo(int32 &Level, float &Health, FString &Name) { Level += 7; Health += 77; Name += "Marcus"; } |
在这种情况下,返回值和输入是有直接关系的,所以不能像情况1中那样使用:
local level,heath,name level,heath,name = self:GetPlayerBaseInfo(level,heath,name); |
在这种情况下,在lua里调用传入的参数和返回的参数是都必须要传递和接收的,这样才会有正常的行为,如果不接收返回值:
local level,heath,name self:GetPlayerBaseInfo(level,heath,name); |
level,heath,name
这些传进去的对象的值并不会像C++中传递的引用那样值会改变。
所以函数怎么调用还是要看函数里是怎么写的。
这个在UnLua的issus里有提到:issus/25
C++里使用UKismetSystemLibrary::DoesImplementInterface
,Lua里也一样,不过区别是接口类型的传入:
local CubeClass = UE4.UClass.Load("/Game/Cube_Blueprint.Cube_Blueprint") local World = self:GetWorld() local Cube_Ins = World:SpawnActor(CubeClass,self:GetTransform(),UE4.ESpawnActorCollisionHandlingMethod.AlwaysSpawn, self, self) if UE4.UKismetSystemLibrary.DoesImplementInterface(Cube_Ins,UE4.UMyInterface) then print(fmt("{1} inheritanced {2}",CubeClass,UE4.UMyInterface)) end |
要使用UE4.U*Interface
这种形式。
当我们在C++中获得一个接口时,获得的类型是TScriptInterface<>
类型,本来以为还要自己导出TScriptInterface
才可以拿到接口,但是发现并不是这样,UnLua里可以直接拿到TScriptInterface<>
就像普通的UObject对象:
如下面这样一个函数:
UFUNCTION(BlueprintCallable, Category = "GameCore|Flib|GameFrameworkStatics", meta = (CallableWithoutWorldContext,WorldContext="WorldContextObject")) static TScriptInterface<IINetGameInstance> GetNetGameInstance(UObject* WorldContextObject); |
在Lua里调用:
local GameInstance = UE4.UFlibGameFrameworkStatics.GetNetGameInstance(self); |
这个得到的类型在C++里是TScriptInterface<>
但在Lua里得到的就是该接口的UObject对象。
上面讲了怎么得到TScriptInterface
的接口(在lua里得到的其实就是该接口的UObject),那么怎么通过它来调用接口的函数呢?有三种方法。
// This class does not need to be modified. UINTERFACE(BlueprintType,MinimalAPI) class UINetGameInstance : public UIBaseEntityInterface { GENERATED_BODY() }; class GWORLD_API IINetGameInstance { GENERATED_BODY() public: UFUNCTION(Category = "GameCore|GamePlayFramework") virtual bool FindSubsystem(const FString& InSysName,TScriptInterface<IISubsystem>& OutSubsystem)=0; }; |
通过对象调用函数
:
操作符): local GameInstance = UE4.UFlibGameFrameworkStatics.GetNetGameInstance(self); local findRet1,findRet2 = GameInstance:FindSubsystem("TouchController") |
指定类和函数名调用
local GameInstance = UE4.UFlibGameFrameworkStatics.GetNetGameInstance(self); local findRet1,findRet2 = UE4.UNetGameInstance.FindSubsystem(GameInstance,"TouchController") |
指定接口的类和函数名调用
local GameInstance = UE4.UFlibGameFrameworkStatics.GetNetGameInstance(self); local findRet1,findRet2 = UE4.UINetGameInstance.FindSubsystem(GameInstance,"TouchController") |
lua中获取uclass可以用于创建对象,其方法为:
local uclass = UE4.UClass.Load("/Game/Core/Blueprints/AI/BP_AICharacter.BP_AICharacter_C") |
Load的路径是该类的PackagePath
。
如,在lua中加载UMG的类然后创建并添加至视口:
function LoadingMap_C:ReceiveBeginPlay() local UMG_C = UE4.UClass.Load("/Game/Test/BPUI_TestMain.BPUI_TestMain_C") local UMG_TestMain_Ins = UE4.UWidgetBlueprintLibrary.Create(self,UMG_C) UMG_TestMain_Ins:AddToViewport() end |
注意:UnLua官方的UE4.UClass.Load
实现是默认只能创建蓝图类的,具体实现看LuaLib_Class.cpp
中的UClass_Load
的实现。
可以修改一下支持C++的类:
int32 UClass_Load(lua_State *L) { int32 NumParams = lua_gettop(L); if (NumParams != 1) { UE_LOG(LogUnLua, Log, TEXT("%s: Invalid parameters!"), ANSI_TO_TCHAR(__FUNCTION__)); return 0; } const char *ClassName = lua_tostring(L, 1); if (!ClassName) { UE_LOG(LogUnLua, Log, TEXT("%s: Invalid class name!"), ANSI_TO_TCHAR(__FUNCTION__)); return 0; } FString ClassPath(ClassName); bool IsCppClass = ClassPath.StartsWith(TEXT("/Script")); if (!IsCppClass) { const TCHAR *Suffix = TEXT("_C"); int32 Index = INDEX_NONE; ClassPath.FindChar(TCHAR('.'), Index); if (Index == INDEX_NONE) { ClassPath.FindLastChar(TCHAR('/'), Index); if (Index != INDEX_NONE) { const FString Name = ClassPath.Mid(Index + 1); ClassPath += TCHAR('.'); ClassPath += Name; ClassPath.AppendChars(Suffix, 2); } } else { if (ClassPath.Right(2) != TEXT("_C")) { ClassPath.AppendChars(TEXT("_C"), 2); } } } FClassDesc *ClassDesc = RegisterClass(L, TCHAR_TO_ANSI(*ClassPath)); if (ClassDesc && ClassDesc->AsClass()) { UnLua::PushUObject(L, ClassDesc->AsClass()); } else { lua_pushnil(L); } return 1; } |
C++类的路径为:
/Script/GWorld.GWorldGameEngine |
以/Script
开头,其后是该C++类所属的模块名,.
之后的是类名(不包含U
/A
之类的开头)。
把资源加载到内存:
local Object = LoadObject("/Game/Core/Blueprints/AI/BT_Enemy") |
比如加载某个材质球给模型:
function Cube3_Blueprint_C:ReceiveBeginPlay() local MatIns = LoadObject("/Game/TEST/Cube_Mat_Ins") UE4.UPrimitiveComponent.SetMaterial(self.StaticMeshComponent,0,MatIns) end |
注:LuaObject在UnLua里对应的是LoadObject<Object>
:
int32 UObject_Load(lua_State *L) { // ... UObject *Object = LoadObject<UObject>(nullptr, *ObjectPath); // ... } |
注意:Lua中创建的对象使用动态绑定是绑定到该类的UCLASS上,并不是绑定到该New出来的实例。
UnLua中对NewObject
处理的代码为Global_NewObject
:
FScopedLuaDynamicBinding Binding(L, Class, ANSI_TO_TCHAR(ModuleName), TableRef); UObject *Object = StaticConstructObject_Internal(Class, Outer, Name); |
SpawnActor
Lua中SpawnActor以及动态绑定:
local World = self:GetWorld() local WeaponClass = UE4.UClass.Load("/Game/Core/Blueprints/Weapon/BP_DefaultWeapon.BP_DefaultWeapon") local NewWeapon = World:SpawnActor(WeaponClass, self:GetTransform(), UE4.ESpawnActorCollisionHandlingMethod.AlwaysSpawn, self, self, "Weapon.BP_DefaultWeapon_C") |
NewObject
lua中调用NewObject以及动态绑定:
local ProxyObj = NewObject(ObjClass, self, nil, "Objects.ProxyObject") |
UnLua中的NewObject
可以接收四个参数,依次是:创建的UClass、Outer、Name,以及动态绑定的Lua脚本。
Component
注意:原版UnLua只可以加载BP的UClass,这个需要做改动(修改
LuaLib_Class.cpp
中的UClass_Load
函数,检测传入是C++类时把添加_C
后缀的逻辑去掉), 而且创建Component时也需要对其调用OnComponentCreated
和RegisterComponent
,这两个函数不是UFUNCTION,需要手动导出。
导出ActorComponent中OnComponentCreated
和RegisterComponent
等函数:
// Export Actor Component BEGIN_EXPORT_REFLECTED_CLASS(UActorComponent) ADD_FUNCTION(RegisterComponent) ADD_FUNCTION(OnComponentCreated) ADD_FUNCTION(UnregisterComponent) ADD_CONST_FUNCTION_EX("IsRegistered",bool, IsRegistered) ADD_CONST_FUNCTION_EX("HasBeenCreated",bool, HasBeenCreated) END_EXPORT_CLASS() IMPLEMENT_EXPORTED_CLASS(UActorComponent) |
则使用时与C++的使用方法一致:
local StaticMeshClass = UE4.UClass.Load("/Script/Engine.StaticMeshComponent") local MeshObject = LoadObject("/Engine/VREditor/LaserPointer/CursorPointer") local StaticMeshComponent= NewObject(StaticMeshClass,self,"StaticMesh") StaticMeshComponent:SetStaticMesh(MeshObject) StaticMeshComponent:RegisterComponent() StaticMeshComponent:OnComponentCreated() self:ReceiveStaticMeshComponent(StaticMeshComponent) -- StaticMeshComponent:K2_AttachToComponent(self.StaticMeshComponent,"",EAttachmentRule.SnapToTarget,EAttachmentRule.SnapToTarget,EAttachmentRule.SnapToTarget) UE4.UStaticMeshComponent.K2_AttachToComponent(StaticMeshComponent,self.StaticMeshComponent,"",EAttachmentRule.SnapToTarget,EAttachmentRule.SnapToTarget,EAttachmentRule.SnapToTarget) |
UMG
创建UMG首先需要获取到UI的UClass,然后使用UWidgetBlueprintLibrary::Create
来创建,与C++一致:
local UMG_C = UE4.UClass.Load("/Game/Test/BPUI_TestMain.BPUI_TestMain_C") local UMG_TestMain_Ins = UE4.UWidgetBlueprintLibrary.Create(self,UMG_C) UMG_TestMain_Ins:AddToViewport() |
动态多播代理
在C++代码中写了一个动态多播代理:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FGameInstanceDyDlg, const FString&,InString); // in class UPROPERTY() FGameInstanceDyDlg GameInstanceDyDlg; |
在lua中绑定,可以绑定到lua的函数:
local GameInstance = UE4.UFlibGameFrameworkStatics.GetNetGameInstance(self); GameInstance.GameInstanceDyMultiDlg:Add(self,LoadingMap_C.BindGameInstanceDyMultiDlg) -- test bind dynamic multicast delegate lua func function LoadingMap_C:BindGameInstanceDyMultiDlg(InString) UE4.UKismetSystemLibrary.PrintString(self,InString) end |
同样也可以对该代理进行调用、清理、移除:
-- remove GameInstance.GameInstanceDyMultiDlg:Remove(self,LoadingMap_C.BindGameInstanceDyDlg) -- Clear GameInstance.GameInstanceDyMultiDlg:Clear() -- broadcast GameInstance.GameInstanceDyMultiDlg:Broadcast("66666666") |
动态代理
C++中有如下动态代理声明:
DECLARE_DYNAMIC_DELEGATE_OneParam(FGameInstanceDyDlg,const FString&,InString); // in class UPROPERTY() FGameInstanceDyDlg GameInstanceDyDlg; |
在lua中绑定:
GameInstance.GameInstanceDyDlg:Bind(self,LoadingMap_C.BindGameInstanceDyMultiDlg) -- test bind dynamic delegate lua func function LoadingMap_C:BindGameInstanceDyDlg(InString) UE4.UKismetSystemLibrary.PrintString(self,InString) end |
动态代理支持Bind
/Unbind
/Execute
操作:
-- bind GameInstance.GameInstanceDyDlg:Bind(self,LoadingMap_C.BindGameInstanceDyMultiDlg) -- UnBind GameInstance.GameInstanceDyDlg:Unbind() -- Execute GameInstance.GameInstanceDyDlg:Execute("GameInstanceDyMultiDlg") |
不支持非Dynamic Delegate
因为BindStatic
/BindRaw
/BindUFunction
这些都是模板函数,UnLua的静态导出方案不支持将他们导出。
官方issus:如何正确静态导出继承自FScriptDelegate的普通委托
Delay
如果想要使用类似Delay的函数:
/** * Perform a latent action with a delay (specified in seconds). Calling again while it is counting down will be ignored. * * @param WorldContextWorld 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 voidDelay(UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo ); |
在lua中可以通过协程(coroutine)
来实现:
function LoadingMap_C:DelayFunc(Induration) coroutine.resume(coroutine.create( function(WorldContectObject,duration) UE4.UKismetSystemLibrary.Delay(WorldContectObject,duration) UE4.UKismetSystemLibrary.PrintString(WorldContectObject,"Helloworld") end ), self,Induration) end |
就是通过coroutine.create
绑定上一个函数,可以直接在coroutine.create
里写,或者绑定上一个已有的函数:
function LoadingMap_C:DelayFunc(Induration) coroutine.resume(coroutine.create(LoadingMap_C.DoDelay),self,self,Induration) end function LoadingMap_C:DoDelay(WorldContectObject,duration) UE4.UKismetSystemLibrary.Delay(WorldContectObject,duration) UE4.UKismetSystemLibrary.PrintString(WorldContectObject,"Helloworld") end |
但是要注意一点:在绑定已有的lua函数时,传递的参数需要多一个self
,标识调用指定函数的调用者。
coroutine.resume(coroutine.create(LoadingMap_C.DoDelay),self,self,Induration) |
这里的第一个self
,就是在通过self
调用LoadingMap_C.DoDelay
,后面的两个参数才作为传递给协程函数的参数。
调用代码为:
function LoadingMap_C:ReceiveBeginPlay() -- 5s后输出HelloWorld self:DelayFunc(5.0) end |
注意:对于直接是UFUNCTION但是带有
FLatentActionInfo
的函数可以直接使用上面的方法,但是对于UE封装的异步节点,不是函数而是一个类的节点需要自己导出。
AsyncLoadPrimaryAsset
在C++里可以使用UAsyncActionLoadPrimaryAsset::AsyncLoadPrimaryAsset
来异步加载资源:
/** * Load a primary asset into memory. The completed delegate will go off when the load succeeds or fails, you should cast the Loaded object to verify it is the correct type. * If LoadBundles is specified, those bundles are loaded along with the asset */ UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true", Category = "AssetManager", AutoCreateRefTerm = "LoadBundles", WorldContext = "WorldContextObject")) static UAsyncActionLoadPrimaryAsset* AsyncLoadPrimaryAsset(UObject* WorldContextObject, FPrimaryAssetId PrimaryAsset, const TArray<FName>& LoadBundles); |
想要在Lua中使用的话需要把FPrimaryAssetId
这个结构导出:
#include "UnLuaEx.h" #include "LuaCore.h" #include "UObject/PrimaryAssetId.h" BEGIN_EXPORT_CLASS(FPrimaryAssetId,const FString&) ADD_FUNCTION_EX("ToString",FString, ToString) ADD_STATIC_FUNCTION_EX("FromString",FPrimaryAssetId, FromString,const FString&) ADD_FUNCTION_EX("IsValid", bool, IsValid) END_EXPORT_CLASS() IMPLEMENT_EXPORTED_CLASS(FPrimaryAssetId) |
然后就可以在Lua中使用了,如异步加载关卡资源,加载完成后打开:
function Cube_Blueprint_C:ReceiveBeginPlay() local Map = UE4.FPrimaryAssetId("Map:/Game/Test/LoadingMap") local AsyncActionLoadPrimaryAsset = UE4.UAsyncActionLoadPrimaryAsset.AsyncLoadPrimaryAsset(self,Map,nil) AsyncActionLoadPrimaryAsset.Completed:Add(self,Cube_Blueprint_C.ReceiveLoadedMap) AsyncActionLoadPrimaryAsset:Activate() end function Cube_Blueprint_C:ReceiveLoadedMap(Object) UE4.UGameplayStatics.OpenLevel(self,"/Game/Test/LoadingMap",true) end |
SetTimer
有些需求需要Timer循环调用,在C++里可以使用UKismetSystemLibrary::K2_SetTimerDelegate
,在蓝图中对应的是SetTimerByEvent
,因为它是UFUNCTION
的函数,所以在lua中也可以调用。
绑定代理和清理操作:
function LoadingMap_C:ReceiveBeginPlay() UpdateUILoopCount = 0; UpdateUITimerHandle = UE4.UKismetSystemLibrary.K2_SetTimerDelegate({self,LoadingMap_C.UpdateUI},0.3,true) end function LoadingMap_C:UpdateUI() if UpdateUILoopCount < 10 then print("HelloWorld") UpdateUILoopCount = UpdateUILoopCount + 1 else UE4.UKismetSystemLibrary.K2_ClearAndInvalidateTimerHandle(self,UpdateUITimerHandle) end end |
{self,FUNCTION}会创建出来一个Delegate,本来还以为要自己导出一个创建Dynamic Delegate的方法,其实不用。
如果要绑定类似UButton
的OnPressed
/OnClicked
/OnReleased
/OnHovered
/OnUnhovered
等事件,它们都是多播代理,所以要用Add
来添加:
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnButtonClickedEvent); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnButtonPressedEvent); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnButtonReleasedEvent); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnButtonHoverEvent); |
在lua中绑定:
function UMG_LoadingMap_C:Construct() self.ButtonItem.OnPressed:Add(self,UMG_LoadingMap_C.OnButtonItemPressed) end function UMG_LoadingMap_C:OnButtonItemPressed() print("On Button Item Pressed") end |
Unlua里使用UE4.TArray
的下标规则是从1开始的,而不是与引擎中相同的0.issues/41
UnLua里的类型转换语法为:
Obj:Cast(UE4.AActor) |
如:
local Character = Pawn:Cast(UE4.ABP_CharacterBase_C) |
在bp里创建一个蓝图结构:
在UnLua里可以通过下列方式访问:
local bp_struct_ins = UE4.FBPStruct() bp_struct_ins.string = "123456" bp_struct_ins.int32 = 12345 bp_struct_ins.float = 123.456 print(fmt("string: {},int32: {},float: {}",bp_struct_ins.string,bp_struct_ins.int32,bp_struct_ins.float)) |
可以像C++的结构那样使用,需要注意的地方为,需要在蓝图结构类型的名字前加F
,如上面的例子里,在蓝图中的名字为BPStruct
,在UnLua中访问时则为FBPStruct
.
未完待续,欢迎指出问题和交流意见。
扫描二维码,分享此文章
本文标题:UE4热更新:基于UnLua的Lua编程指南
文章作者:ZhaLiPeng
发布时间:2020年03月24日 10时50分
更新时间:2020年05月19日 16时57分
本文字数:本文一共有4.2k字
原始链接:https://imzlp.me/posts/36659/
专栏链接:https://zhuanlan.zhihu.com/p/117236078
许可协议: CC BY-NC-SA 4.0
捐赠BTC:1CbUgUDkMdy6YRmjPJyq1hzfcpf2n36avm
转载请保留原文链接及作者信息,谢谢!
您的捐赠将鼓励我继续创作!
选择语言▼
4%
© 2014 - 2020 | 418.9k
Github Pages & Hexo Deploy
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。