UE4编辑器主界面的各个Tab页

在LevelEditor.h中定义了LevelEditorTabIds类,该类每个静态属性对应一个Tab页。

 Place Actors
在PlacementModeModule.h里实现了IPlacementModeModule接口。
调用该接口的Register系列函数即可实现向Place Actors中添加自定义分类及其子项。
具体步骤如下:

关于这方面的具体实例,可以参考插件

FAssetData对象的创建

#480
 FAssetData ShadowCaptureActorAssetData(
 		TEXT("/SURAPlugin/ARShadow/ShadowGenerator/BluePrints/ShadowCapture"),
 		TEXT("/SURAPlugin/ARShadow/ShadowGenerator/BluePrints"),
 		TEXT("ShadowCapture"),
 		TEXT("Blueprint")
 ); // asset路径在编辑器里通过Reference Viewer可以获得
调用的是如下构造函数:
FAssetData(InPackageName, InPackagePath, InAssetName, InAssetClass)
UE5.1版的新API形式:
 // Run test on a single TestPlan asset
 FName PackageName = FName(FPaths::GetBaseFilename(Path, false));
 FName PackagePath = FName(FPaths::GetPath(Path));
 FName AssetName = FName(FPaths::GetBaseFilename(Path, true));
 FTopLevelAssetPath ClassName = UInterchangeImportTestPlan::StaticClass()->GetClassPathName();
 FInterchangeImportTestData& TP = TestPlans.AddDefaulted_GetRef();
 TP.AssetData = FAssetData(PackageName, PackagePath, AssetName, ClassName);

UObject类型获取其Asset Path

FTopLevelAssetPath ClassName =  UObject::StaticClass()->GetClassPathName();


//
// 注意这里UObject类型的AssetPath都是【/Script/Package.Class】的固定格式
// 之所以是这个固定格式,是因为其.generated.h里有DECLARE_CLASS宏调用,其注册的该UObject的PackagePath就是【/Script/Package】的格式
// 例如:/Script/Engine.Blueprint表示Engine模块的UBlueprint
// 
UClass* USuraControlPanelCommonBPLibrary::LoadCppClass( const FString& AssetPath )
{
	return ::StaticLoadClass(UObject::StaticClass(), nullptr, *AssetPath);
}

UObject Paths

Every Asset and Actor that the Unreal Editor has loaded in memory has a path that uniquely identifies it.
In general, these object paths follow the format:
 /path/PackageName.ObjectName:SubObjectName.SubObject
Programmers who work with the Engine in C++ may recognize this format as the same one accepted by functions like FindObject() and StaticFindObject().
For example, to deconstruct an Actor path shown in the request examples above:
/Game/ThirdPersonBP/Maps/The path to the Asset in the Content Browser.
ThirdPersonExampleMap.ThirdPersonExampleMap:The name of the package and the object it contains (for many Assets, these will be the same).
PersistentLevel.CubeMesh_5.StaticMeshComponent0The path through a hierarchy of sub-objects to the object you want to affect.
如上表所示,Path部分很好理解,在Content Browser里可以直接看到。而PackageName通常就是资产文件名,资产里包含若干Object,它们各有ObjectName,即资产文件其实是个Package

正确引用第三方module

假设已有一个第三方module,要在你自己的module中使用它。
第三方module命名为M3,自己的module命名为M1. 即M1中要使用M3.
正确的引用方法是在M1的M1.build.cs文件中设定如下字段:
 PublicDependencyModuleNames或PrivateDependencyModuleNames中添加M3,如果引用M3中功能的代码是在M1的Public源码中,则加到PublicDependency里,反之,如果是在M1的Private源码中引用M3中的功能,则加到PrivateDependency里。如果在M1的Public和Private源码中均引用了M3中的功能,则应该把M3同时添加到PublicDependency和PrivateDependency中。
上面对DependencyModule的设置对应toolchain里的静态链接,即保证M1对M3中的对象或功能的引用能找到实现。但是它不完全解决头文件包含层面的问题。不完全的意思是有时候不用处理头文件包含的事情也没问题发生,有时候不处理头文件包含的事情就有问题发生。不用处理也没有问题发生的情形是:M3中的功能实现,其对应的头文件都加到了M3的PublicIncludePaths中,那么M1中引用M3的该功能时,不用额外处理头文件包含事宜。相应的,有问题的情形是:M3中实现了某个功能,但其相关头文件没有加入到M3的PublicIncludePaths中。此时M1的某个cpp中调用M3里的一个函数时,编译器会报告找不到函数声明。这种有问题的情况,可以通过将M3中相应的路径加到M1的PublicIncludePaths或PrivateIncludePaths中来解决
 假设M3的某项功能实现在M3/Private中。当在M1的Public源码中使用M3的这项功能时,可将M3/Private添加到M1的PublicIncludePaths中。当在M1的Private源码中使用M3的这项功能时,可将M3/Private添加到M1的PrivateIncludePaths中。同时,在cpp中需用#include "M3/Private/xxx.h"来引用头文件。
 PrivateIncludePaths.AddRange(
			new string[] {
				// ... add other private include paths required here ...
				System.IO.Path.Combine(GetModuleDirectory("LevelEditor"), "Private"),
			}
			);
还有一种需求:引用第三方module里的C++模板定义或抽象接口等,一般就是一些类模板和函数模板的C++头文件。这些头文件由于只包括模板或抽象接口,所以是功能完备且独立的,此种需求下,我们只需要访问这些模板或抽象接口,不需要除此之外的整个第三方module,那就可以设置PrivateIncludePathModuleNamesPublicIncludePathModuleNames这两个属性。这两个属性给予对第三方module里某些路径的代码访问权限,同时不会将第三方module的dll或lib链接到最终的exe中。

module的build.cs文件里的bPrecompile要设置为true → ...

当以二进制方式发布插件时,对于4.22.0(不包括4.22.0)之后的版本,需要显式的设置
 bPrecompile = true
然后对插件进行package操作,这样在插件的Intermediate目录中会生成对应的.precompiled文件,该文件用于指明obj文件。 发布后,还需要设置:
 bUsePrecompiled = true
这样,用户端就能直接Link precompiled文件中指明的obj了,不依赖插件源码来打包。
 在UE4.27里经过实测:package出来的Plugins,可以删除Intermediate目录,可以删除Binaries目录下的pdb文件,在module.build.cs里需要添加bUsePrecompiled=true;如此操作后,即便在visual studio里rebuild工程,也不会将插件rebuild,因此是安全的,但是rebuild操作会将插件Binaries目录下的dll文件删除,因此rebuild后需要将这些被删除的dll还原,无需其他操作。且整个过程中bPrecompile字段不是必须的。但与此同时,在保持这些设置的前提下,对整个工程进行打包时,又会提示找不到precompiled manifest文件,因此针对打包工程的需求,仍然需要插件的Intermediate目录。

module.build.cs文件常见字段 → ...

string path = Path.Combile(p1, p2, p3);
string absolutePath = Path.GetFullPath(path);
//https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/BuildTools/UnrealBuildTool/ThirdPartyLibraries/
RuntimeDependencies.Add(Path.GetFullPath(absolutePath), StagedFileType.NonUFS);
if (Target.bBuildEditor)判断当前build配置是否包含Editor

Public/PrivateDependencyModuleNames属于静态链接,通常其Public/PrivateIncludePaths会自行处理正确。
Public/PrivateIncludePathModuleNames与DynamicallyLoadedModuleNames配合属于动态链接,因此如果你使用了非抽象函数,则会提示无法找到链接符号。这一对配置的典型用法参考 #474,一般就是LoadModuleChecked,然后调用接口的抽象函数。

Details视图定制

一个Actor或Property的Details信息的展示界面,可以定制。这项工作涉及几个基本的接口:
以上需要引用【PropertyEditor】模块。
代码示例:
针对特定Class类型的所有实例定制其Details面板的情形:
 StarupModule() {
	 FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));
	 // Custom Class Layouts
	 auto AddCustomClass = [this, InPropertyModule = &PropertyModule](FName Name, FOnGetDetailCustomizationInstance InstanceGetter)
	 {
		InPropertyModule->RegisterCustomClassLayout(Name, InstanceGetter);
		CustomClassLayoutNames.Add(Name);
	 };
	 AddCustomClass(ASURACameraDriver::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FSURACameraDriverDetailsCustomization::MakeInstance));
 }
 ShutdownModule() {
	 if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
	 {
		FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
		for (FName ClassName : CustomClassLayoutNames)
		{
			PropertyModule.UnregisterCustomClassLayout(ClassName);
		}
		CustomClassLayoutNames.Reset();
	 }
 }
 // .h
 TArray<FName> CustomClassLayoutNames;
针对特定Class类型的某个实例定制Details面板。需要在该实例对应的IDetailsView中设置,详见代码 Customizations/SURACameraDriverDetailsCustomization代码
IDetailsView面板与UObject建立关联:
 TSharedRef<SWidget> SSURAAR_MainWnd::MakeSURACameraDriverDetails( )
 {
	FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));
	FDetailsViewArgs Args;
        Args.bAllowSearch = false; // 隐藏Details面板的搜索框
	Args.NameAreaSettings = FDetailsViewArgs::HideNameArea; // 隐藏Details面板的角色名框
	TSharedRef<IDetailsView> View = PropertyModule.CreateDetailView(Args);
	View->RegisterInstancedCustomPropertyLayout(ASURACameraDriver::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FSURACameraDriverDetailsCustomization::MakeInstance));
	TArray<AActor*> OutActors;
	if (GWorld)
	{
		UGameplayStatics::GetAllActorsOfClass(GWorld, ASURACameraDriver::StaticClass(), OutActors);
	}
	if (OutActors.Num() > 0)
	{
		View->SetObject(OutActors[0], true);
	}
	return View;
 }