目录一蓝图函数库二蓝图编译器术语编译过程三向蓝图公开游戏元素使类可蓝图化可读和可写属性可执行和可覆盖函数四将C暴露给蓝图速度复杂度范例创建蓝图 API提示和技巧蓝图是UE4中引入的一个强大新功能是创建新UClasses的一种方法无需书写或编译任何代码当创建一个蓝图时可以选择扩展C类或另一个蓝图类然后可添加、排列及自定义Components使用可视化的脚本语言实现自定义的逻辑对Event及交互做出反应定义变量处理输入及创建一种完全自定义的对象类型每个蓝图都具有一个Construction ScriptC中的构造函数类似创建对象时运行它该脚本可动态地基于多个因素构建Actor实例如一个可以自动调整大小来填充建筑物间空隙的篱笆从这个角度来说蓝图也可以看成是一个非常强大的预制系统一蓝图函数库在开发过程中通常会发现需要通过函数集来简化项目开发这些函数通常无所属可在多种游戏代码之间重复使用通过蓝图编译时通常也会需要这些函数如需要公开共享效用函数不想将它们和一种特定的游戏对象类型捆绑在一起对于这类情况使用蓝图函数库蓝图函数库是一个静态函数的合集提供不与特定游戏对象绑定的功能这些库可群组化为逻辑函数集如 AI 蓝图库或包含提供多种不同功能区域如系统蓝图库访问的函数创建蓝图函数库与使用 UFUNCTION() 宏对蓝图公开函数十分相似所有蓝图类均继承自 UBlueprintFunctionLibrary而非派生自 Actor 或 UObject只应包含静态方法UCLASS() class UAnalyticsBlueprintLibrary : public UBlueprintFunctionLibrary { GENERATED_UCLASS_BODY() /** Starts an analytics session without any custom attributes specified */ UFUNCTION(BlueprintCallable, Category Analytics) static bool StartSession();蓝图函数库为 UObject 非直接派生需要标准 UCLASS() 和 GENERATED_UCLASS_BODY() 宏还将在可从蓝图进行调用的函数上添加 UFUNCTION() 宏蓝图函数库中的函数可被设计为 BlueprintCallable 或 BlueprintPure取决于调用是否存在副作用//函数实现 bool UAnalyticsBlueprintLibrary::StartSession() { TSharedPtrIAnalyticsProvider Provider FAnalytics::Get().GetDefaultConfiguredProvider(); if (Provider.IsValid()) { return Provider-StartSession(); } else { UE_LOG(LogAnalyticsBPLib, Warning, TEXT(StartSession: Failed to get the default analytics provider. Double check your [Analytics] configuration in your INI)); } return false; }AAIController* UAIBlueprintHelperLibrary::GetAIController(AActor* ControlledActor) { APawn* AsPawn CastAPawn(ControlledActor); if (AsPawn ! nullptr) { return CastAAIController(AsPawn-GetController()); } return CastAAIController(ControlledActor); }二蓝图编译器与普通C类一样蓝图在使用前需要进行编译当单击编译Compile按钮时系统将把蓝图资源的属性和图表转换为类术语$ FKismetCompilerContext执行编译工作的类系统为每次编译生成一个新实例存储正在编译的类、蓝图等的引用$ FKismetFunctionContext保存用于编译单个函数的信息如对关联图表、属性和生成的UFunction的引用$ FNodeHandlingFunctor一个辅助工具类用于处理编译器中的一个节点类单件包含用于注册引脚连接和生成编译语句的函数$ FKismetCompiledStatement编译器中的工作单元编译器将节点转换为一组已编译语句后端将这些语句转换为字节码操作$ FKismetTerm图表中的终端文字、常量或变量引用每个数据引脚连接都与其中一个终端关联还可在NodeHandlingFunctor中为Scratch变量、中间结果等创建自己的术语编译过程清理类类是现场编译的意味着相同的UBlueprintGeneratedClass会被一次又一次地清理和重用 因此指向类的指针不必固定CleanAndSanitizeClass()将属性和函数从类中移到临时包中的垃圾类中 然后清除类中的任何数据创建类属性编译器在蓝图的NewVariables以及其他一些地方构造脚本等上进行迭代 以查找类所需的所有UProperty然后在函数CreateClassVariablesFromBlueprint()中创建UClass作用域上的UProperty创建函数列表编译器通过处理事件图表处理函数图表和预编译函数如为每个context调用PrecompileFunction() 来为类创建函数列表处理事件图表由CreateAndProcessUberGraph()函数执行此函数将所有事件图表复制到一个大图表中之后节点有机会扩展然后 此函数为图表中的每个事件节点创建一个函数存根并为每个事件图表创建一个FKismetFunctionContext处理函数图表函数图表的处理由ProcessOneFunctionGraph()函数完成 此函数将每个图表复制到一个临时图表中其中节点将获得机会而展开此函数还将为每个函数图表都创建一个FKismetFunctionContext预编译函数函数的预编译由每个context的PrecompileFunction()处理绑定和链接类现在编译器已经了解类的所有UProperty和UFunction因此可以绑定和链接该类这包括填充属性链、属性大小、函数图等此时从本质上看它具有一个类标头 -减去最终的标记和元数据 - 以及一个类默认对象(CDO)编译函数下一步是为剩余的节点生成FKismetCompiledStatment对象 此操作使用AppendStatementForNode()通过节点处理器的Compile()函数完成此函数可以在编译函数中创建FKismetTerm对象但前提是这些对象仅在本地使用完成编译类为了完成编译类编译器将确定类标记并从父类传播标记和元数据最后执行一些最终检查以确保编译过程中一切正常后端发出生成的代码后端将每个函数上下文中的语句集合转换为代码FKismetCompilerVMBackendFKismetCppBackend复制类默认对象属性编译器使用一个特殊的函数CopyPropertiesForUnrelatedObjects()将类的旧CDO中的值复制到新CDO中属性通过标记序列化复制 因此只要名称一致它们就应当会被正确地传输在此阶段CDO的组件将被重新实例化并进行适当的修复操作时以GeneratedClass CDO为准重新实例化由于类可能已经更改了大小且属性可能已经过添加或删除 因此编译器需要用刚编译的类重新实例化所有对象这个过程使用TObjectIterator查找类的所有实例生成一个新实例然后使用CopyPropertiesForUnrelatedObjects()函数将旧实例复制到新实例三向蓝图公开游戏元素使用蓝图可以使代码非常灵活从而提供许多好处如 游戏设计人员可能希望在游戏中实现一种新型武器作为程序员现在编写武器代码的方式与传统方式几乎完全相同而不同之处仅在于公开了一些重要的功能 比如射速和Fire()函数在游戏测试后设计师确定需要改变枪支的射速以呈曲线射击设计师可以简单地进入蓝图并直接更改射速而不是对射速重新编码并重新编译游戏从而节省设计师和程序员的时间使类可蓝图化为创建从某个类扩展而来的蓝图必须将该类定义为Blueprintable 这涉及到在类定义之前的UCLASS()宏中添加此关键字此关键字使蓝图系统能够感知该类 以便显示在New Blueprint对话框的类列表中且可选择它作为所创建蓝图的父类//最简单的声明形式 UCLASS(Blueprintable) class AMyBlueprintableClass : AActor { GENERATED_BODY() }关键字说明Blueprintable将该类公开为可接受的用于创建蓝图的基类默认值为NotBlueprintable但继承时除外由子类继承BlueprintType将该类公开为可用于蓝图中的变量的类型NotBlueprintable指定此类不是用于创建蓝图的可接受基类否定指定可蓝图化关键字的父类的效果可读和可写属性为了将C类中定义的变量公开给从该类扩展而来的蓝图必须使用UPROPERTY()宏中列出的下列关键字之一来定义该变量 这些关键字使蓝图系统能够感知该变量以便在My Blueprint面板中显示该变量 并且可以设置或访问该变量的值//Characters Health UPROPERTY(EditAnywhere, BlueprintReadWrite, CategoryCharacter) float health;关键字说明BlueprintReadOnly此属性可以由蓝图读取但不能修改BlueprintReadWrite此属性可以通过蓝图读取或写入Multicast Delegate Keywords说明BlueprintAssignable应公开属性以在蓝图中赋值BlueprintCallable公开属性以在蓝图图表中调用可执行和可覆盖函数为了从蓝图调用本地函数必须使用UFUNCTION()宏中列出的下列关键字之一来定义该函数这些关键字使蓝图系统能够感知该函数以便在上下文菜单或控制板中显示该函数并可将其添加到图表并执行 - 或在事件的情况下可覆盖和执行这些函数//最简单的声明形式 UFUNCTION(BlueprintCallable, CategoryWeapon) void Fire();在创建函数签名时注意通过引用传递某一参数将使该参数成为蓝图节点上的输出引脚若要使参数通过引用传递并仍然显示为输入请使用UPARAM()宏UFUNCTION(BlueprintCallable, Category Example Nodes) static void HandleTargets(UPARAM(ref) TArrayFVector InputLocations, TArrayFVector OutputLocations);还可使用UPARAM()来更改引脚的显示名称/** 根据采用度数的旋转值制作一个旋转体{绕Z轴的旋转、绕X轴的旋转、绕Y轴的旋转} */ UFUNCTION(BlueprintPure, CategoryMath|Rotator, meta(Keywordsconstruct build rotation rotate rotator makerotator, NativeMakeFunc)) static FRotator MakeRotator( UPARAM(DisplayNameX (Roll)) float Roll, UPARAM(DisplayNameY (Pitch)) float Pitch, UPARAM(DisplayNameZ (Yaw)) float Yaw);关键字Blueprint to Native Communication说明BlueprintCallable可从蓝图中调用的本地函数执行本地代码将更改所调用对象的某些内容或一些其他全局状态意味着必须调度此函数或者明确地告诉它相对于其他节点执行的顺序用白色的执行线来执行此操作所有蓝图可调用函数将按照它们在白色执行行上出现的顺序调用BlueprintPure可从蓝图中调用的本地函数执行本地代码不会更改它所调用对象的任何内容或任何其他全局状态意味着调用这个节点不会改变任何东西只需要输入然后告诉输出它们是诸如数学节点、-、*等、或变量获取器或任何永远都不会改变任何内容之类的东西不需要调度也没有白色的执行线的连接由编译器根据哪个BlueprintCallable节点需要这些节点生成的数据而自动计算出来关键字Native to Blueprint Communication说明BlueprintImplementableEvent这是允许本地函数调用蓝图的主要方式就像在蓝图中实现的虚拟函数如不存在实现则该函数调用将被忽略必须注意的是如BlueprintImplementableEvent没有返回值或输出参数那么它将显示为一个事件可通过右击并在蓝图事件图表中选择它来使用如有返回值或任何输出参数将在My Blueprints选项卡中列出然后可通过右单并选择实现的函数来覆盖注意BlueprintImplementableEvents没有函数的本地实现BlueprintNativeEvent除了如果蓝图没有覆盖此函数则调用此函数的本地默认实现之外此函数与上述函数别无二致如想要某种默认行为而蓝图没有实现它但又希望蓝图能够在需要时覆盖功能那么此函数将大有帮助这些函数的使用成本更高所以只将它们用在需要功能的地方当覆盖BlueprintNativeEvent时如需要仍然可以调用本地实现方法是在事件或函数条目节点上右击并选择Add call to parent四将C暴露给蓝图可从两个主要方面考虑使用 C 或蓝图速度表达式复杂度这两因素之外便主要取决于游戏的复杂度和团队的构成如团队中美术师的数量多于程序员则蓝图的优越性大于 C 代码相反如程序员数量较多则最好使用 C还需预计开发过程中人员的缺席在 Epic普遍的工作流程是 - 内容设计师创建十分复杂的蓝图后程序员编写新蓝图节点的代码明确如何将大量工作压缩到 C 中这样就能将功能块移入一个新的 C 函数中较好的方法是大量使用蓝图复杂度达到一定程度转入 C使功能的表达更加简洁否则对非程序人员而言过于复杂速度实际上蓝图执行的速度比 C 执行慢这并不意味着会影响性能但需大量计算或进行高频率操作时最好使用 C也可根据团队状况和项目性能对二者进行完美结合如蓝图功能较多可将部分功能移入 C 使速度加快蓝图中剩余的部分功能可保留灵活性如分析表明蓝图中的一项操作耗时极长考虑只将该部分移入 C其他部分仍然保留在蓝图中如控制上千个 Actor 的群组系统通过蓝图可视化脚本执行时间极长在这类情况下在 C 中处理决定、路径、和其他群组功能性能更佳之后可将部分调整参数和控制函数公开到蓝图复杂度就表达式复杂度而言部分操作在 C 中更容易执行蓝图可良好地执行大量操作但部分内容不易在节点中表达操作大型数据集、执行字符串操作在大量数据集上执行复杂算术等内容均十分复杂不易在视觉系统中进行操作这些内容更适合于 C可轻松进行查看了解具体细节群组系统更适合 C 代码的另一个原因在于表达式复杂度范例部分功能块适合在 C 中处理部分适合在蓝图中处理如程序员可在 C 中创建自定义事件的角色类之后蓝图可用于该角色类的延展实际指定模型并进行默认设置可在 C 中实现能力系统的基础类之后设计师再创建实际执行操作的蓝图收集或重生函数为可在蓝图中实现的事件这样的可拾取道具可被覆写以便设计师生成不同粒子发射器和声效创建蓝图 API提示和技巧创建对蓝图公开的 API 时需要考虑以下几点可选参数便于在蓝图中处理/** * 将字符串显示到日志中也可选择显示到屏幕上。 * 如 Print To Log 为 true它将显示在 Output Log 窗口中。否则它将被记录为Verbose通常不会显示。 * * param InString 登出字符串 * param bPrintToScreen 是否将输出显示到屏幕上 * param bPrintToLog 是否将输出保存到日志中 * param bPrintToConsole 是否将输出显示到控制台 * param TextColor 是否将输出显示到控制台 */ UFUNCTION(BlueprintCallable, meta(WorldContextWorldContextObject, CallableWithoutWorldContext, Keywords log print, AdvancedDisplay 2), CategoryUtilities|String) static void PrintString(UObject* WorldContextObject, const FString InString FString(TEXT(Hello)), bool bPrintToScreen true, bool bPrintToLog true, FLinearColor TextColor FLinearColor(0.0,0.66,1.0));带有大量返回参数的函数和返回结构体的函数之间优先前者UFUNCTION(BlueprintCallable, Category Example Nodes) static void MultipleOutputs(int32 OutputInteger, FVector OutputVector);可在现有函数上添加新参数但如要进行移除或变更则需要否决原始函数并添加一个新函数必须使用否决元数据使新函数的信息显示在蓝图中UFUNCTION(BlueprintCallable, CategoryCollision, meta(DeprecatedFunction, DeprecationMessage Use new CapsuleOverlapActors, WorldContextWorldContextObject, AutoCreateRefTermActorsToIgnore)) static ENGINE_API bool CapsuleOverlapActors_DEPRECATED(UObject* WorldContextObject, const FVector CapsulePos, float Radius, float HalfHeight, EOverlapFilterOption Filter, UClass* ActorClassFilter, const TArrayAActor* ActorsToIgnore, TArrayclass AActor* OutActors);如函数需要接受枚举考虑将expand enum as execs用作元数据可使节点更易于使用UFUNCTION(BlueprintCallable, Category DataTable, meta (ExpandEnumAsExecsOutResult, DataTablePinCurveTable)) static void EvaluateCurveTableRow(UCurveTable* CurveTable, FName RowName, float InXY, TEnumAsByteEEvaluateCurveTableResult::Type OutResult, float OutXY);许多完成耗时较长的操作均为隐藏函数/** * 执行带延迟的隐藏操作。 * * param WorldContext 世界背景。 * param Duration 延迟长度。 * param LatentInfo 隐藏操作。 */ UFUNCTION(BlueprintCallable, CategoryUtilities|FlowControl, meta(Latent, WorldContextWorldContextObject, LatentInfoLatentInfo, Duration0.2)) static void Delay(UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo );如有可能考虑将函数放入共享库便于在多个类之间使用避开target引脚class DOCUMENTATIONCODE_API UTestBlueprintFunctionLibrary : public UBlueprintFunctionLibrary尽可能将节点标记为纯可避免在节点上使用连线的执行引脚/* 在 0 和 最大 - 1 之间返回一致分配的随机数 */ UFUNCTION(BlueprintPure, CategoryMath|Random) static int32 RandomInteger(int32 Max);将一个函数标记为const也可使蓝图节点不带执行引脚/** * 获得 actor 到世界的转换。 * return 从 actor 空间转换到世界空间的转换。 */ UFUNCTION(BlueprintCallable, meta(DisplayName GetActorTransform), CategoryUtilities|Transformation) FTransform GetTransform() const;