为什么Editor类型模块中导出的蓝图接口在蓝图编辑器中无法使用—及代码位于引擎不同文件夹的意义

一、问题

在UE中添加一些简单的蓝图接口,然后在蓝图编辑器中始终无法找到对应节点,无论是否勾选"Context Sensitive"。因为实现代码非常简单,简单到没有出错的可能,所以就觉得很神奇。好在尽管有很多乱七八糟的干扰信息,但是网上还是有答案https://forums.unrealengine.com/t/c-blueprint-function-not-showing-up-in-blueprint-editor/46359/8。对于编辑器实现来说,它是怎么做到一个蓝图可调用接口(BlueprintCallable)在不同的编辑器中可见的。这里讨论的问题不在于找到解决这个问题的方法,而是在茫茫UE代码中找到它对应代码的具体实现位置。

二、一个模块是Editor/Runtime对生成代码的影响

在.uplugin文件中对模块的描述中会声明文件是Editor类型还是Runtime类型,这些标志位将会对UHT生成的文件产生影响:通过对比不同配置下UHT生成的Package文件可以发现:如果一个模块配置的类型是Editor,则为该模块生成的Package描述中设置了在PKG_EditorOnly标志位,也就是说一个模块是否是EditorOnly在运行时可以判断的(而不仅仅是构建时)。
我们以UnLua代码的UnLuaEditor模块为例,该模块配置的类型为Editor,在该模块对应的包(Package)描述文件UnLua\Intermediate\Build\Win64\UE4Editor\Inc\UnLuaEditor\UnLuaEditor.init.gen.cpp中可以看到:
void EmptyLinkFunctionForGeneratedCodeUnLuaEditor_init() {}
UPackage* Z_Construct_UPackage__Script_UnLuaEditor()
{
static UPackage* ReturnPackage = nullptr;
if (!ReturnPackage)
{
static const UE4CodeGen_Private::FPackageParams PackageParams = {
"/Script/UnLuaEditor",
nullptr,
0,
PKG_CompiledIn | 0x00000040,
0xB9DBA038,
0x096E84BD,
METADATA_PARAMS(nullptr, 0)
};
UE4CodeGen_Private::ConstructUPackage(ReturnPackage, PackageParams);
}
return ReturnPackage;
}
注意其中的
PKG_CompiledIn | 0x00000040,
标志位,其中的0x00000040对应的标志标识这是一个只在编辑器中使用的模块
PKG_EditorOnly = 0x00000040, ///< This is editor-only package (for example: editor module script package)

三、Engine\Source文件夹下模块的类型是什么

细心的同学会发现:引擎内部源代码并没有一个地方描述根文件夹下模块的类型,那么它们的类型是如何确定的?
在UnrealBuildTool\System\RulesCompiler.cs:UnrealBuildTool.RulesCompiler.CreateEngineOrEnterpriseRulesAssembly函数中可以看到,其中对于引擎内部代码的分类是通过所在文件来区分:在Runtime文件夹下的模块都是Runtime类型,Editor文件夹下的是Editor类型
private static RulesAssembly CreateEngineOrEnterpriseRulesAssembly(RulesScope Scope, List<DirectoryReference> RootDirectories, string AssemblyPrefix, IReadOnlyList<PluginInfo> Plugins, bool bReadOnly, bool bSkipCompile, RulesAssembly Parent)
{
……
AddEngineModuleRulesWithContext(SourceDirectory, "Runtime", DefaultModuleContext, UHTModuleType.EngineRuntime, ModuleFileToContext);
AddEngineModuleRulesWithContext(SourceDirectory, "Developer", DefaultModuleContext, UHTModuleType.EngineDeveloper, ModuleFileToContext);
AddEngineModuleRulesWithContext(SourceDirectory, "Editor", DefaultModuleContext, UHTModuleType.EngineEditor, ModuleFileToContext);
AddEngineModuleRulesWithContext(SourceDirectory, "ThirdParty", DefaultModuleContext, UHTModuleType.EngineThirdParty, ModuleFileToContext);
……
}

四、蓝图编辑器如何判断哪些接口可以在编辑器中使用

经过各种猜测、调试、验证,确定判断一个接口是否可以在一个蓝图中使用的接口通过BlueprintActionFilterImpl::IsHiddenInNonEditorBlueprint函数来判断。这里判断调用接口是否是编辑器特有模块还是比较直观的。流程是先判断一个接口是不是一个编辑器特有接口(函数),如果是的话判断当前编辑的蓝图对象的父类(Blueprint->ParentClass,通常是Native C++类)是在哪个模块,如果不是Editor模块(IsEditorOnlyObject函数完成判断)则不显示。由于UBlueprint本身在引擎的Runtime文件夹,所以如果一个Function是Editor类型的,它是没办法在运行时蓝图编辑器中可见的。
static bool BlueprintActionFilterImpl::IsHiddenInNonEditorBlueprint(FBlueprintActionFilter const& Filter, FBlueprintActionInfo& BlueprintAction)
{
const UFunction* Function = BlueprintAction.GetAssociatedFunction();
bool bVisible = true;
if (Function)
{
const bool bIsEditorOnlyFunction = IsEditorOnlyObject(Function) || Function->HasAnyFunctionFlags(FUNC_EditorOnly);
const bool bIsUncookedOnlyFunction = Function->GetOutermost()->HasAnyPackageFlags(PKG_UncookedOnly);
if (bIsEditorOnlyFunction)
{
for (const UBlueprint* Blueprint : Filter.Context.Blueprints)
{
const UClass* BlueprintClass = Blueprint->ParentClass;
const bool bIsEditorBlueprintClass = (BlueprintClass != nullptr) && IsEditorOnlyObject(BlueprintClass);
bVisible &= bIsEditorBlueprintClass;
}
}
if (bIsUncookedOnlyFunction)
{
for (const UBlueprint* Blueprint : Filter.Context.Blueprints)
{
const UClass* BlueprintClass = Blueprint->ParentClass;
const bool bIsEditorBlueprintClass = (BlueprintClass != nullptr) && IsEditorOnlyObject(BlueprintClass);
const bool bIsUncookedBlueprintClass = (BlueprintClass != nullptr) && BlueprintClass->GetOutermost()->HasAnyPackageFlags(PKG_UncookedOnly);
bVisible &= (bIsEditorBlueprintClass || bIsUncookedBlueprintClass);
}
}
}
return !bVisible;
}

 

posted on 2021-08-09 20:57  tsecer  阅读(388)  评论(0编辑  收藏  举报

导航