Unreal开发问题总结(7,8月)

1.文件加载
LoadObject、LoadClass、FSoftObjectPath、FSoftClassPath等引擎提供的资产加载工具或函数直接使用引擎中Copy Reference格式的路径,如果不是uasset文件的话就使用相对项目的路径(ProjectDir之后的部分),不要使用完整路径加载

FPaths::ProjectPluginsDir() + "DialogueSystem/Content/Editor/EUW_MainTab.EUW_MainTab"//错误
TEXT("/Script/Blutility.EditorUtilityWidgetBlueprint'/DialogueSystem/Editor/EUW_MainTab.EUW_MainTab'")//正确
TEXT("/Script/Blutility.EditorUtilityWidgetBlueprint'/Plugins/DialogueSystem/Editor/EUW_MainTab.EUW_MainTab'")//正确

但FFileHelper等文件访问类的函数应当使用完整路径加载,比如LoadFileToString等

FString resultString;
FFileHelper::LoadFileToString(resultString, *(FPaths::ProjectDir() + path));

2.Log打印FString的方式(FString通过转TCHAR)

UE_LOG(LogTemp, Log, TEXT("%s"), *DialogueAssetGeneratorPath);

UE_LOG(LogTemp, Log, *DialogueAssetGeneratorPath);//直接这样写的话会报错不是const TCHAR*

3.Editor UMG中有两个控件可以显示UPROPERTY在UI上,支持直接编辑变量,效果就和蓝图DetailPanel中一样(DetailsView和SinglePropertyView)
在这里插入图片描述
可以显示FilePath类型的属性据此实现编辑器的文件选择框效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意需要在控件的PreConstruct中绑定监视的对象
在这里插入图片描述
4.如何将Editor UMG显示在自己的Tab上
OnSpawnPluginTab是你注册在TabManager中的OpenTab回调函数,加载WidgetBlueprint(注意不是Widget)然后调用其提供的SpawnEditorUITab即可,该蓝图会自动处理刷新、关闭等操作
在这里插入图片描述
同时可以在注册TabManager回调函数时根据自己EditorUMG的大小注册tab window的大小
在这里插入图片描述
5.UPROPERTY默认是Transient(关卡退出后不会保存),但Visible和Edit系列的宏(VisibleAnywhere等)会使变量被保存
6.c++创建资产方法

	FString ObjectPath = SaveFolder + Data_Name;
	UPackage* Package = CreatePackage(*ObjectPath);
	TArray<UPackage*> AllPackage;
	AllPackage.Add(Package);
	Package->FullyLoad();

	UDataTable* DataTable = NewObject<UDataTable>(Package, *Data_Name, RF_Public | RF_Standalone);
	FAssetRegistryModule::AssetCreated(DataTable);

	Package->SetDirtyFlag(true);
	FEditorFileUtils::PromptForCheckoutAndSave(AllPackage, false, false);

每个资产对应一个Package,将Package作为Outer就能将UObject作为资产(如果是把世界或者世界中的物体作为Outer的话,那UObject的生命就仅限于游戏中),然后通过FAssetRegistryModule::AssetCreated注册创建的资产,最后保存Packge

UEditorAssetLibrary::DeleteLoadedAsset(DialogueTable->DialogueStateMachine->ClassGeneratedBy);

删除资产直接用EditorAssetLibrary::DeleteAssetEditorAssetLibrary::DeleteLoadedAsset(还可直接删除整个目录),不要用AssetRegistryTool::AssetDeleted,AssetRegistryTool仅起更新注册的作用,不实际删除资产,猜测可能通过直接删除UObject的方法才需要用AssetRegistryTool注册删除的资产
7.Unreal中对Json进行操作

	TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(NPCAssetGenResult);
	TArray<TSharedPtr<FJsonValue>> JsonRows;
	FJsonSerializer::Deserialize(JsonReader, JsonRows);

ue中json格式可以分两种解析,一种是JsonValue代表的是任何类型的Json结构,另一种是JsonObject代表的是用{}围起来的键值对结构,然后利用AsArray方法就可以处理多层嵌套
8.编辑器修改Actor名字需要注意的大坑!
首先Actor在WorldOutliner中的名字也就是ActorLable是一个仅在编辑器中有的属性,SetActorLable这个方法也是仅限编辑器中调用的
通过SetActorLable等方法修改完Actor名字后,继续使用之前的Actor名字访问Actor或者其Component就会失败(错误是Actor Pendingkill),在一些情况下你会不知不觉的使用了之前的Actor名字在进行访问,不信可以看下面这个例子
在这里插入图片描述
TargetNPC是要修改名字的Actor上的一个组件,以参数形式传入到某个函数,它的名字格式大概是ActorName_ComponentName_X(X是个数字),在这个函数内,如果你先修改了Actor名字然后再通过该参数调用这个组件(图中调用了组件上的函数SetDialogue)就会访问到旧的Actor名字上的组件,Log中会显示该Actor已经被删除(TRASH)

Warning: Attempted to access TRASH_DialogueComponent_7 via property TargetNPC, but TRASH_DialogueComponent_7 is not valid (pending kill or garbage)

我目前不确定具体原因是什么,查看SetActorLabel的源码并没有迹象表明会删除原本的Actor重新创建,Component就更不应该会重新创建,所以我的一种猜测是蓝图函数中传入的组件并不是真的“指针”,可能只是基于名称的某种索引,所以当Actor名字修改,Component或许Reregister后这种索引失效了
如果将SetActorLable放在函数体外或者放在组件调用后就可以避免此问题,所以使用SetActorLable时应当小心,尤其是在蓝图函数中
在这里插入图片描述
9.UStruct的GC回收问题
只有UObject能够被ue的GC回收,UStruct如果想要做管理的话需要使用TSharedPtr等智能指针做管理,不要使用裸指针,同时TSharedPtr等智能指针不能被蓝图解析(不支持UPROPERTY),想要蓝图获取的话可以通过函数返回UStruct的引用
10.Unreal的字符集
Unreal中采用的字符统一是TCHAR类型,根据不同平台解析为char或者wchar(两字节),所以当使用Unreal的Log或者Print时,不要使用char或者std::string,打印出来会是乱码,可以使用TEXT宏或者UTF8_TO_TCHAR进行格式转化,像下面这样,尤其注意填充%s模板时不使用TCHAR不会报错只会打印出乱码

UE_LOG(LogAIGameCoreNetwork, Log, TEXT("[SendMessage] %s"), UTF8_TO_TCHAR(ClientMsg.DebugString().c_str()));

11.Unreal的Delegate和Event
Unreal可以通过两种方式实现“监听-回调”的模式,一种是通过事件Event,另一种是通过Delegate,重点来看下平时不怎么用的Event

DECLARE_EVENT_TwoParams(AIGameCoreNetwork, FReceiveAIServerMsgEvent, const void* Data, SIZE_T Size)

Event实际上是一项已经接近弃用的老版本功能,它的作用完全可以通过MulticastDelegate代替,因为它本质上是继承自MulticastDelegate,只是多了一项将OwningClass设置为Friend的操作而已

#define FUNC_DECLARE_EVENT( OwningType, EventName, ReturnType, ... ) \
	class EventName : public TMulticastDelegate<ReturnType(__VA_ARGS__)> \
	{ \
		friend class OwningType; \
	};

而这个友元类的操作实际上完全没有作用,它的本意是只允许OwningType进行激活(调用BroadCast函数),但现在BroadCast函数在TMulticastDelegate中为public,所以Event和MulticastDelegate完全等效;而以前应该是没有MulticastDelegate,只有MulticastDelegateBase(BroadCast函数实际实现的类,为Protected),让Event继承自MulticastDelegateBase并利用友元达到限制激活的效果

//MulticastDelegate中Public版本的Broadcast本质上也是调用MulticastDelegateBase中Protected版本的Broadcast
void Broadcast(ParamTypes... Params) const
{
	Super::template Broadcast<DelegateInstanceInterfaceType, FDelegate, ParamTypes...>(Params...);
}

12.BlueprintImplementableEvent和BlueprintNativeEvent的函数参数中有FString的话必须是引用(不加const表示输出),不然会报错找不到重载函数

    UFUNCTION(BlueprintImplementableEvent, Category = "Cpp_Event")
        void Event_A(const FString &str);
        //这种会报错
        void Event_A(FString str);

13.Unreal的PythonScriptPlugin
(1) 这个插件模块是UncookedOnly,也就是说UE官方提供的Python的插件是仅限编辑器使用的
(2) c++和蓝图都可以调用执行python,但如果想要获得python的返回值(通过全局变量)的话就必须要用蓝图的ExecutePythonScript节点,通过在Detail面板添加Input和Output来实现C++与python的变量传递

在这里插入图片描述
在这里插入图片描述
14.一般ActorComponent的BeginPlay要优先于Actor的BeginPlay,但对于Runtime生成的Component或者Replicated Component则顺序不能保证,所以在Actor的BeginPlay中配置Component的BeginPlay中需要的一些参数往往不是一个好主意!
15.关于编辑器Debug模式运行的问题
一些错误在Debug模式下会导致引擎崩溃,但函数堆栈全部是UnknownError,导致无法排查问题。这时可以考虑用Development模式启动,这些错误的堆栈会以Error打印在log中但不会导致引擎崩溃
这些错误一般都是一些不致命、规定警告性质的错误,比如尝试实例化一个标记为Abstract的类型

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值