UE 小妙招(长期更新)
随记
外部访问私有成员
模板加友元访问未被UPROPERTY标记的私有成员
//宏定义
#define ACCESS_MEMBER(ClassName, Member,MemberType) \
template<auto M>\
struct Accessor_##ClassName##Member;\
template<class T, class U, T U::*M>\
struct Accessor_##ClassName##Member<M> {\
friend T& Get##Member##From##ClassName(U& u) {\
return u.*M;\
}\
};\
\
template struct Accessor_##ClassName##Member<&ClassName::Member>;\
MemberType& Get##Member##From##ClassName(ClassName&);\
//在项目的某个地方,我们要取出UGameInstance类的SubsystemCollection成员,它是个private 属性,它的类型是FObjectSubsystemCollection<UGameInstanceSubsystem>
//直接写这么一行
ACCESS_MEMBER(UGameInstance,SubsystemCollection,FObjectSubsystemCollection<UGameInstanceSubsystem>)
//调用,这其实是UGameInstance::Init()的内容,但是我想要自定义初始化,只能这样了
GetSubsystemCollectionFromUGameInstance(*this).Initialize(this);
在代码中定义GameplayTags
在代码中使用GameplayTags通常是填字符串既不优雅也不便维护,直接在代码中声明GameplayTag才是正解
//比如我们可与在一个插件的某个模块中声明
//.h文件
XXXXXXXXXX_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Cheat_GodMode)
//.cpp文件
UE_DEFINE_GAMEPLAY_TAG_COMMENT(TAG_Cheat_GodMode, "Cheat.GodMode", "GodMode 作弊功能已在owner身上激活");
//如此一来,不需要在编辑器中设置任何Gameplay Tag,将所有标签定义到代码里,其他地方直接使用TAG_Cheat_GodMode。添加松散Tags
添加:UAbilitySystemBlueprintLibrary::AddLooseGameplayTags
删除:UAbilitySystemBlueprintLibrary::RemoveLooseGameplayTags
AddLooseGameplayTags与RemoveLooseGameplayTags只在Server段有效,且它们不会复制,AddReplicatedLooseGameplayTags与RemoveReplicatedLooseGameplayTags只会复制到客户端,所以最好是使用UAbilitySystemBlueprintLibrary的方法
引入第三方库
静态库
//xxx.Build.cs
string ThirdPartyPath => Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/"));
public XXXXX(ReadOnlyTargetRules Target) : base(Target)
{
...
...
string includePath = Path.Combine(ThirdPartyPath, "include");
PublicIncludePaths.Add(includePath);
string libPath = Path.Combine(Path.Combine(ThirdPartyPath, "lib"), "lunasvg.lib");
PublicAdditionalLibraries.Add(libPath);
}动态库
//xxx.Build.cs
string ThirdPartyPath => Path.GetFullPath(Path.Combine(ModuleDirectory, "../ThirdParty/"));
public XXXXX(ReadOnlyTargetRules Target) : base(Target)
{
...
...
PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "include"));
string dllPath = Path.Combine(Path.Combine(ThirdPartyPath, "lib"), "wgpu_native.dll");
string libPath = Path.Combine(Path.Combine(ThirdPartyPath, "lib"), "wgpu_native.dll.lib");
PublicAdditionalLibraries.Add(libPath);
PublicDelayLoadDLLs.Add(dllPath);
RuntimeDependencies.Add(dllPath, libPath);
PublicDefinitions.Add(string.Format("__EMSCRIPTEN__={0}", 0));
}除此之外,还需要再模块启动时加载dll
void XXXXX::StartupModule()
{
const FString BasePluginDir = IPluginManager::Get().FindPlugin("XXXXX")->GetBaseDir();
const FString LibExamplePath = FPaths::Combine(*BasePluginDir,TEXT("Source/ThirdParty/lib/wgpu_native.dll"));
DynamicLibHandle_WebGPU = FPlatformProcess::GetDllHandle(*LibExamplePath);
}
void XXXXX::ShutdownModule()
{
if(DynamicLibHandle_WebGPU) FPlatformProcess::FreeDllHandle(DynamicLibHandle_WebGPU);
}覆盖Actor父类的组件
比如Character类有一个CharacterMovementComponent组件,可以通过ObjectInitializer使用自己创建的组件,当然必须得是父组件的子类
ACHA_Base::ACHA_Base(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<UMyMovementComponent>(
ACharacter::CharacterMovementComponentName))
{
......
}原始指针替换为TobjectPtr
转换工具参考:
https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5-migration-guide?application_version=5.0#optionalconversiontool 需要注意的是,对于常规开发环境只需运行
UnrealObjectPtrTool.exe <UHT_LOG_PATH>
TObjectPtr是Epic正在推动的新标准,在未来的版本可能会成为硬性要求,相比于原始指针,它利用64位系统的特性记录了更多信息,支持访问跟踪和懒加载(可选:meta=(LoadBehavior = "LazyOnDemand"))
UE5已经支持ParallelFor并行
详见Engine\Source\Runtime\Core\Public\Async\ParallelFor.h
我遇到过的啸错误😭
UBT编译
修改MSVC工具和Windows Kti版本
通过BuildConfiguration.xml可以对UBT的行为做出设置,例如:
WindowsSdkVersion:使用特定的Windows Kit版本
ToolchainVersion:使用特定的MSVC版本
Windows Kit移动位置导致的报错
别问我为什么会移动Windows Kit的安装位置,问就是VS原地拉屎
修改注册表:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\Windows\v10.0的InstallationFolder
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows Kits\Installed Roots的KitsRoot10
MVVM插件导致的错误
ListView与MVVM在本地化环境下的错误(该问题在UE5.6已被修复)
当你在一个UserWidget上添加ViewModel,如果有ListView类型的控件且开启中文本地化,就会必定崩溃。原因是FMVVMListViewBaseExtensionCustomizationExtender::CustomizeDetails尝试在细节面板添加一个新选项
// Fetch the entry widget class handle.
TArray<TSharedRef<IPropertyHandle>> ListEntryProperties;
MVVMCategory.GetDefaultProperties(ListEntryProperties);
TSharedRef<IPropertyHandle>* EntryClassPtr = ListEntryProperties.FindByPredicate( [](const TSharedRef<IPropertyHandle> Property)
{
return Property->GetPropertyDisplayName().EqualToCaseIgnored(FText::FromString(TEXT("Entry Widget Class")));
});
check(EntryClassPtr);可以发现它使用了直接判断名称,此时只有使用英文本地化才能正常工作。
引擎重启后UserWidget对ViewModel的链接丢失
说明这些Widget在引擎启动时就被加载了,与ViewModel插件的加载阶段设置发生冲突,需要检查所有可能的硬引用,将可能导致该Widget在启动阶段被加载的硬引用更换为软引用。
赛博纸尿裤(函数式宏调用所需的参数不足)
Win标头问题:Unreal重定义了Windows中的各种宏,所以需要检查整个代码项目是否引用了任何Windows头文件,包括项目的第三方库。这些标头文件应按照以下的方式包裹:
#include "Windows/AllowWindowsPlatformTypes.h" THIRD_PARTY_INCLUDES_START // Windows + third-party includes THIRD_PARTY_INCLUDES_END #include "Windows/HideWindowsPlatformTypes.h"
比如not enough arguments for function-like macro invocation 'max'这种,不用看了慢慢把include的Windows和第三方库都穿上纸尿裤吧。
虚幻引擎不能拖入文件了!
这又是一个无关痛痒但是恶心的问题了,不能通过拖入导入任何文件,但是内容浏览器的导入按钮还能用。一开始还是以为哪个插件用AssetTypeActions整了个活。其实这是因为管理员身份的问题
我通常使用Rider启动UE项目,如果Rider是管理员身份那UE也会是管理员身份,但是在很多情况下Rider必须是管理员身份,只能弄一个ps或者bat脚本,让Rider去运行脚本来降权限(但是这样Rider又不会自动附加进程,不知道有没有自动化的方法),或者直接手动启动吧。
在任意地点拓展引擎界面
先来一段神秘代码:
UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateLambda([this]()
{
UToolMenu* ViewportToolbarMenu = UToolMenus::Get()->ExtendMenu("LevelEditor.ViewportToolBar");
FToolMenuSection& Section = ViewportToolbarMenu->FindOrAddSection("MyCustomSection",FText::FromString(L"我是新的工具栏分段"),FToolMenuInsert("Left",EToolMenuInsertType::After));
Section.Alignment = EToolMenuSectionAlign::Middle;
Section.AddSubMenu(
"MyCustomMenu",
FText::FromString(L"点我"),
FText::FromString(L"我是提示"),
FNewToolMenuDelegate::CreateLambda([](UToolMenu* InMenu)
{
FToolMenuSection& EditSection = InMenu->AddSection("MyCustomSectionSubMenu", FText::FromString(L"我是子菜单分段"));
if (UUnrealEdViewportToolbarContext* const Context = InMenu->FindContext<UUnrealEdViewportToolbarContext>())
{
EditSection.AddEntry(FToolMenuEntry::InitWidget(
"MyCustomMenuEntry",
SNew(STextBlock).Text(FText::FromString(L"我是新UI")),
FText::GetEmpty(),
false,
true,
false));
}
}),
false,
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.Settings"));
}));控制台输入ToolMenus.Edit,会发现好多地方多了一个按钮,这些地方就是你可以挂载的地方,点击按钮就可以看到类似"LevelEditor.ViewportToolBar"的路径。