Unreal Engine 4 之 Slua入门(2)
如和关闭SLua的调试?–slua_profile.start(“127.0.0.1”,8081)
1、SLua中的继承和与蓝图交互
self就是对应的c++类的lua封装,你可以使用self来调用c++的函数,访问属性等,同时self也是lua table;
调用蓝图中的方法,self:MyFunc();
蓝图调用Lua:在lua中重新写一个和蓝图中同样的方法,返回参数确保一致即可,而且可以覆盖掉蓝图中的方法;
如果想在lua中只调用蓝图中的方法使用:self.Super:MyFunc();
如果没有在lua中重写蓝图方法,那么self.Super:MyFunc()和self:MyFunc()是等价的;
2、Slua中调用UE的方法
local WBL = import(“WidgetBlueprintLibrary”)
WBL.Handled()
3、Slua中的工具
local DragOPCls = slua.loadClass("/Game/MyDragOP.MyDragOP")
function panel:OnDragDetected(Geometry,PointerEvent,Operation)
return WBL.CreateDragDropOperation(DragOPCls)
end
c++代码路径处理
path = FString::Format(TEXT(“Blueprint’{0}_C’”), { path });
最终路径
“Blueprint’/Game/MyDragOP.MyDragOP_C’”
使用方法:LoadObject or StaticLoadClass
4、工程C++调试
第一种,直接运行
第二章,Debug->Attach Process
5、源码调试
打开Epic Games,虚幻引擎->库中选择调试库下载即可
6、Slua调试
slua 支持运行时分析lua使用内存,可以查看内存分配细节,方便检查内存泄漏。
Dump UObjects
在unreal控制台输入slua.DumpUObjects,会在控制台里输出所有被lua引用的UOjbect对象,通过观察这些对象,你可以快速知道还有那些UObject被lua使用而无法回收,你需要根据情况去lua代码里置空来避免lua引用,从而被unreal回收。
在lua代码里也可以直接,调用slua.dumpUObjects函数来得到这些UObject的table返回值。
DumpMemoryDetail
如果你想详细分析lua内存的使用情况,你可以开启Memory Profile,通过在控制台输入slua.StartMemoryTrack, 开启Memory Profile,默认是关闭的。
开启Memory Profile后,可以通过slua.DumpMemoryDetail控制台命令输出,从开启后到现在所有的未被内存使用信息,slua会报告内存分配的大小和具体位置,例如:
通过这个Dump List,你可以分析那些内存尚未释放,占用多少内存,从而帮助你解决内存泄漏的问题。
7、Slua Profiler
工程参数配置
在大家完成sluaunreal的配置可以正常使用后,如果需要开启 profiler,首先需要修改目标工程的xxx.Build.cs文件(参考democpp.Build.cs,如下),和sluaunreal的工程配置文件相比,需要在依赖库和搜索路径中加入slua_profile 模块
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { “Core”, “CoreUObject”, “Engine”, “InputCore”, “Http” });
PrivateDependencyModuleNames.AddRange(new string[] { “slua_unreal”, “slua_profile”, “Slate”, “SlateCore”, “UMG”, “Http” });
PrivateIncludePathModuleNames.AddRange(new string[] { “slua_unreal” });
PublicIncludePathModuleNames.AddRange(new string[] { “slua_unreal”,“slua_profile” });
在Lua中开启slua_profiler
在完成工程参数配置后,就是需要在代码中调用slua_profilerd的start()方法来开启profiler了
start(host, port)
host: 是大家需要传递的目标IP地址, 在 demopp 中,host默认是回环地址(127.0.0.1)。因此如果在真机上调试时,profiler是无法接收到socket发送的数据的,需要更改host。
port: 是目标IP地址监听的端口信息, 在 demopp 中,监听端口默认是8081。如果更改监听端口,需要将Plugins文件夹下的"slua_unreal/Source/slua_profile/Privite/slua_remote_profile.cpp"文件中的Server端监听端口更改至start()中填入端口号
调用start()方法代码示例
– 判断是否有成功引入slua_profiler模块
if slua_profile then
– 根据实际的情况填入对应的 host 和 port
slua_profile.start(“127.0.0.1”, 8081)
end
在以上所有配置都完成后,此时编译运行大家的UE,可以在UE顶部的工具栏中选择“窗口” -> “slua Profile” 来开启profiler。
===========================================================================
UE4的根目录是以Cotent为基础的,Content所有目录下的资源前缀加入/Game/
SLua中加载方式:
1、如果是Blueprint(资源点击后打开是编辑器)使用方法如下
slua.loadClass("/Game/MyDragOP.MyDragOP")
解析:
“Blueprint’/Game/MyDragOP.MyDragOP_C’”
_C代表是Blueprint Class
MyDragOP代表是文件名字
2、资源,那么需要使用全地址,“资源类型’/Game/资源相对根路径.资源名”
slua.loadObject(“Texture2D’/Game/boy.boy’”)
“Texture2D’/Game/boy.boy’”
C/C++中加载方式:
同步加载方式1:
UTexture2D* Tex = LoadObject<UTexture2D>(NULL,TEXT("Texture2D'/Game/Textures/UI/tex_test001.tex_test001'"));
UClass* Test = LoadClass<AActor>(NULL,TEXT("Blueprint'/Game/Blueprints/MyGameMode.MyGameMode_C'"));同步加载方式2:
UTexture2D* Tex = LoadObject<UTexture2D>(NULL, TEXT("/Game/Textures/UI/tex_test001.tex_test001"));
UClass* pClass = LoadClass<AActor>(NULL, TEXT("/Game/Blueprints/MyGameMode.MyGameMode_C"));同步加载方式3:
FStreamableManager streamableManager;
UStaticMesh* pStaticMesh = streamableManager.LoadSynchronous<UStaticMesh>(FSoftObjectPath("/Game/Geometry/Meshes/1M_Cube.1M_Cube"));
UClass* pClass = streamableManager.LoadSynchronous<UClass>(FSoftObjectPath("/Game/ThirdPersonCPP/Blueprints/TestActor.TestActor_C"));eg
AssetLoader =newFStreamableManager();
UObject* obj = AssetLoader->LoadSynchronous(FStringAssetReference(TEXT("/Game/AsyncLoad/NewDataAsset.NewDataAsset")));
ItemDatabase = Cast<UItemInfoDatabase>(obj);异步加载:
void ATestLoadObjectCharacter::BeginPlay(){
Super::BeginPlay();
FStreamableManager streamableManager;
FString strMeshFileName ="/Game/Geometry/Meshes/1M_Cube.1M_Cube";
FStreamableDelegate streamableDelegate;
FSoftObjectPath strMeshObjectFileName =FSoftObjectPath(strMeshFileName);
streamableDelegate.BindUObject(this,&ThisClass::LoadFinish, strMeshObjectFileName);
streamableManager.RequestAsyncLoad(strMeshObjectFileName, streamableDelegate);}void ATestLoadObjectCharacter::LoadFinish(FSoftObjectPath meshFilePath){
FSoftObjectPtr meshObjectPtr =FSoftObjectPtr(meshFilePath);
UObject* pObject = meshObjectPtr.Get();if(nullptr== pObject)return;
UStaticMesh* pStaticMesh = Cast<UStaticMesh>(pObject);if(pStaticMesh){UE_LOG(LogTemp, Error,TEXT("UStaicMesh name is %s"),*pStaticMesh->GetName());}}void AMyProject7Character::BeginPlay(){
Super::BeginPlay();
FStreamableManager streamableManager;
FString strBPClassPath ="/Game/testActor.testActor_C";
FStreamableDelegate streamableDelegate;
FSoftClassPath SoftBPClassPathName =FSoftClassPath(strBPClassPath);
streamableDelegate.BindUObject(this,&ThisClass::LoadFinish, SoftBPClassPathName);
streamableManager.RequestAsyncLoad(SoftBPClassPathName, streamableDelegate);}void AMyProject7Character::LoadFinish(FSoftClassPath SoftBPClassPathName){
TSoftClassPtr<AActor> ActorClassPtr = TSoftClassPtr<AActor>(SoftBPClassPathName);
UClass* pClass = ActorClassPtr.Get();if(pClass){UE_LOG(LogTemp, Error,TEXT("UStaicMesh name is %s"),*pClass->GetName());}}自动回收
用上面的方式加载资源以后,如果对象失去引用,就会被自动释放,如果是异步加载,对象只在回调函数中有效,回调函数执行完毕就会被标记为可回收。那么如何保存异步加载的资源呢?
在项目里看到并没有回调中保存加载的对象,而是保存了异步资源加载的Handle,通过Handle来获得的GetLoadedAssets来获得加载的对象。在保存到对象池之后再释放Handle。
手动回收
在加载资源的时候如果讲bManageActiveHandle设置为true,对象会一直常驻内存直到手动释放。
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
UParticleSystem* AimObj = AssetLoader.LoadSynchronous(FSoftObjectPath(AssetPath), true);
如果对象不需要,手动执行unload
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
AssetLoader.Unload(FSoftObjectPath(AssetPath));
在编辑器模式里,上述两种方式都无法生效,只有打包版本才可以。如果在编辑器模式下强制Destroy或者MarkPendingKill(),可以把对象从内存销毁,但是也无法再次Load,除非重启编辑器,(等待验证)
注意
FStreamableManager::Unload()会Release掉和当前资源相关的所有FStreamableHandle。比如在三处位置加载了同一个资源,即使bManageActiveHandle设置为true,那么只要调用Unload一次,就可以将这三个FStreamableHandle对象全部Release掉,即从内存中释放该对象;如果对这3个FStreamableHandle对象分别执行Release,那么只有当最后一个Handle被Release之后,该资源才会从内存中释放。
异步加载时,谁先请求则谁的回调函数先执行,不会存在回调函数执行顺序乱序的问题(除非修改TAsyncLoadPriority),因为引擎内部接收回调请求的容器使用的是TArray,且每次执行索引为0的回调,然后RemoveAt(0)。
异步加载时,如果资源还没加载完成就执行ReleaseHandle()(假设加载时bManageActiveHandle为true),比如在执行回调函数之前执行ReleaseHandle,那么当资源加载完成后(回调函数执行之后),会自动从内存中回收。不过该对象在回调函数中仍然有效,除非在回调函数内ForceGC。
UPROPERTY()修饰的成员变量,可以让其保持的资源对象常驻内存,如果不再需要驻留内存,将该成员变量值为NULL,等到下次GC时就会被自动回收
GEngine->ForceGarbageCollection();执行后,内存回收至少要等到下一帧才会执行。在当前帧内,即使一个对象执行ConditionalBeginDestroy()且执行了ForceGarbageCollection,当前帧内该对象仍然有效。
ConditionalBeginDestroy()是所有UObject都有的API,其对象销毁是异步执行且对象在当前帧内持续有效;AActor::Destroy()是AActor特有的API,其对象回收发生在当前帧结束时。
Reference:
https://www.w3xue.com/exp/article/201912/70569.html
https://github.com/Tencent/sluaunreal
页:
[1]