创建类
这章将涉及以下内容:
- 制作UCLASS—继承自UObject
- 从你自定义的UCLASS创建蓝图
- 创建用户可编辑的UPROPERTY
- 从蓝图方位UPROPERTY
- 将UCLASS指定为UPROPERTY的类型
- 实例化继承自UObject的类(使用ConstructObject<>和NewObject)
- 销毁继承自UObject类的对象
- 创建USTRUCT结构体
- 创建UENUM枚举
1、介绍
这章重点在于创建可以与UE4蓝图编辑器交互的C++类和结构体。
我们将要创建的类是在常规C++类的基础上做了一层封装,称为UCLASS。
UCLASS其实只是一个被很多UE4宏修饰的类。这些宏会生成额外的C++头文件代码,用于与UE4编辑器做交互。
使用UCLASS简直不要太妙。UCLASS宏如果配置正确,可以让你自定义的C++对象在虚幻可视化脚本语言——蓝图中使用。如果你的团队中有设计师,这将非常有用,因为他们可以访问和调整项目的各个方面,而无需深入研究代码。
我们可以使用可视化可编辑的属性(UPROPERTY),在蓝图编辑器中会借助ui小部件表现出来,比如文本字段、滑块和模型选择框。还可以使用那些能够在蓝图图表中调用的函数(比如UFUNCTION)。
左侧显示了两个用UPROPERTY修饰的类成员(一个UTexture引用和一个FColor),它们在C++源码中被设置了在蓝图中可编辑属性。在右侧,C ++ 函数GetName在蓝图图表中可调用,也是因为在C++代码中设置了蓝图可调用的。
UCLASS宏生成的代码位于 ClassName.generated.h文件,该文件需要被包含在UCLASS类的头文件ClassName.h的最后一个#include中。
你会发现即使这些类的函数蓝图可调用、属性蓝图可编辑,它们却无法放置到关卡中。这是因为只有继承自Actor基类,或者它的子类的类,才能放置到关卡中。这些内容将会在第四章涉及到。
只要知道了既定的套路和模式,UE4代码通常来说,是非常容易编写和管理的。我们继承另一个UCLASS的代码,或者创建UPROPERTY变量的代码,或者创建UFUNCTION函数的代码,都是高度一致的。本章提供了一些常见的UE4编码的内容,这些内容围绕基本的UCLASS派生、属性和引用声明、构造函数、析构函数以及常规功能而展开。
2、技术要求
这章需要使用到Unreal Engine 4引擎和Visual Studio 2017 集成开发环境,至于它们应该如何安装配置,请查看:https://blog.csdn.net/u012801153/article/details/109901877
3、继承自UObject的UCLASS类
使用C++进行编码时,你可以用常规C++代码进行编译和运行,并适时调用操作符new和delete来创建和销毁你的自定义对象。常规C++代码在UE4项目里也能很好地运行,只要你正确调用new和delete,就不会发生内存泄露的问题。
当然,你也可以使用UCLASS宏来声明自定义类,让它表现得就像是UE4自身的类。UCLASS修饰的类表示该类将使用UE4内置的智能指针,以及依据智能指针规则进行的对象分配和销毁的内存管理机制。
注意,使用UCLASS宏修饰类后,对象的创建和销毁将由UE4完全控制。你必须使用ConstructObject函数创建对象实例(对应着常规C++的new),然后调用UObject:ConditionBeginDestroy() 函数销毁对象实例(对应着常规C++的delete)。
准备
这部分,我们会重点讲解如何创建有UCLASS宏修饰的C++类,以托管内存的分配和解除分配,并允许从UE4编辑器和蓝图进行访问。为了达成这一目标,请准备好UE4项目以添加新代码。
步骤
创建继承自 UObject 的类,通常有以下几个步骤:
熟悉UE4编码以后,复制粘贴才是最为便捷的。
-
项目打开后,在编辑器内,依次点击 文件|新建C++类。
-
在添加C++类对话框里的右上处,勾选显示所有类的复选框。
-
选择Object(继承的最顶层)作为我们要继承的父类,然后点击下一步。
1)请注意,虽然对话框中显示的是Object,但是在C ++代码中,我们继承的C ++类实际上是带有大写U的UObject。这是UE4的命名约定;
2)继承自UObject的UCLASS类,需要以U开头来命名;
3)继承自AActor的,必须以A开头来命名;
4)未继承任何类的C++类(非UCLASS类)没有命令规则,但是最好以前缀F开头(例如,FAssetData);
5)直接继承自UObject的类不能放置到关卡中,即使该类包含了像UStaticMeshes的可视化元素。如果要将你的对象放置到UE4关卡中,那么必须的继承自AActor类,或者它的子类。更多内容,请阅读第四章。这一章的示例代码都不能放置到编辑器关卡中,但是你可以基于C++类在蓝图图表中创建和使用对象。
-
根据你正在创建的Object派生类的对象类型来给其命名。这里就叫它 UserProfile:
UE4为我们生成的C++文件中,类的名称将是UUserProfile,以遵循UE4的命名规范。 -
点击创建类按钮,编译完成后,文件就会被创建出来。之后,Visual Studio应该会弹出来,并打开我们刚创建的类的实现文件UserProfile.cpp(如果Visual Studio没有自动打开,可以通过 文件|打开Visual Studio,手动启动一下)。打开头文件UserProfile.h,确保内容如下:
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "UObject/NoExportTypes.h" #include "UserProfile.generated.h" /** * */ UCLASS() class COOKBOOK_API UUserProfile : public UObject { GENERATED_BODY() };
-
编译并运行你的项目。你现在可以在Visual Studio中使用你的UCLASS类了。
关于如何去创建和销毁继承自UObject的类对象的事情,在本章节后面的部分会提到。
总结
UE4为UCLASS类生成并管理了大量代码。这些代码是由于使用了UE4宏(例如UPROPERTY,UFUNCTION和UCLASS宏本身)而生成的。生成的代码都在 UserProfile.generated.h文件中,想要编译成功,你必须在头文件中**#include**该文件。这也就是为什么,编辑器会自动包含这个文件。不包含的话,编译会报错。
另一个需要重点注意的是,该文件需要放置在UCLASS类头文件包含列表中的最后。
正确的示例:
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include <list> // Newly added include
// 正确: generated 文件 是最后一个包含进来的
#include "UserProfile.generated.h"
错误的示例:
#pragma once
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "UserProfile.generated.h"
// 错误:.generate.h文件后不能再#include
#include <list> // 最新包含的
如果UCLASSNAME.generated.h文件不是#include语句列表的最后一个,编译代码就会报错。
更多
这里我们来讨论几个用于修改UCLASS类表现的关键词,UCLASS可以被一下几个关键词修饰:
- Blueprintable:这个意味着你能够通过UE4编辑器里的类查看器构造一个蓝图类(右键点击C++类,发现有创建基于C++类的蓝图类的选项)。没有这个关键词的话,就没有创建基于C++类的蓝图类选项了,即使你能在类查看器找到它并右键。
- 只有当你为你的UCLASS宏类指定Blueprintable,创建蓝图类… 选项才会出现。
- BlueprintType:使用这个关键词,表示在蓝图中该类可以用作变量类型。你可以打开任何蓝图类编辑器,在左侧变量面板创建一个基于此的蓝图变量。
- NotBlueprintType:使用该关键词表明,不能使用该类作为蓝图变量的类型,在类查看器中右键类名也不会出现**创建蓝图类…**选项。
你可能不确定是否将C ++类声明为UCLASS。一般的经验是使用UCLASS,除非您有充分的理由不这样做。UE4的代码写得非常好,这一点已经全面测试过。如果你喜欢智能指针,你可能会发现UCLASS不仅使代码更安全,而且使整个代码库连贯如一。
更多的更多
想要了解更多关于UCLASS、UPROPERTY的资料,以类似的宏如何在UE4中使用,请阅读:https://www.unrealengine.com/zh-CN/blog/unreal-property-system-reflection?sessionInvalidated=true&lang=zh-CN
https://www.cnblogs.com/ghl_carmack/p/5698438.html
4、基于C++类创建蓝图
蓝图化只是一个由C++类派生出蓝图类的过程。从UE4对象派生出的蓝图类,使你能够在编辑器中编辑可视化的UPROPERTY属性。这样就可以避免将啥资源都硬编码到您的C ++代码中。另外,你的C++类要放置到关卡中,必须首先蓝图化,当然,这只在构造蓝图类的C++类是Actor的派生类才行。
使用FStingAssetReferences和StaticLoadObject,是一种加载资源(比如贴图)的方法,但这种方法是将资源路径字符串硬编码到C++中,通常是不推荐使用的。而使用UPROPERT() 提供可编辑的值供蓝图使用,然后C++中获取资源引用,才是值得推荐的。
准备
你需要构造好一个可供蓝图类继承的UCLASS类,来练习本部分的内容。也就是说,你的C++类需要以Blueprintable修饰UCLASS宏,使得在引擎中蓝图化C++类成为可能。
步骤
-
要蓝图化你的UserProfile类,首先要保证的是UCLASS宏中有Blueprintable的标签,看起来就像下面这样:
UCLASS(BlueprintType) class COOKBOOK_API UUserProfile : public UObject
-
编译代码。
-
在类查看器(窗口 | 开发者工具 | 类查看器)中找到UserProfile类,因为之前创建C++类的时候不是继承自Actor,想要找到我们自定义的类,需要关闭 过滤器 | 仅Actor,这玩意儿默认是勾选着的。
如果取消勾选的话,我们自定义的类就找不到了。注意:使用类查看器的搜索功能,很快就能搜索到我们要的。
-
在类查看器中找到UserProfile类后,右键它,然后通过选择创建蓝图… 创建蓝图。
-
给蓝图命名。BP_ 是一个不错的前缀。当然了,你也可以遵循其他的命名规则,你要你懂我懂大家懂就行。
-
你可以编辑任何你为蓝图类创建的字段。
如果蓝图编辑器没有自动打开,你可以在内容浏览器中双击蓝图类打开它。
总结
只要你创建的C++类的UCLASS宏中有Blueprintable标签,它就可以在UE4编辑器中蓝图化(被蓝图继承)。蓝图赋予你在UE4可视化GUI窗口自定义C++类属性。
5、创建可编辑属性UPROPERTY
UCLASS 类中可以声明任意数量的 UPROPERTY 属性。每个 UPROPERTY 属性可以是可视化可编辑的字段,也可以是类的可访问的数据成员。
有非常多的限定词可以添加到 UPROPERTY 中,每一个限定词都会影响属性在UE4编辑器中的表现,比如 EditAnywhere (标明属性可以被代码或蓝图编辑器修改),BlueprintReadWrite (标明变量在任何时候都可以被蓝图读写,C++代码也能这么干)。
准备
开始这部分前,你应该有个能添加C++代码的项目了。此外,你已经完成了上一个部分。
步骤
-
首先,我们给类的UCLASS宏加上Blueprintable,然后添加如下成员到类的声明中。代码像下面的情况:
/** * UCLASS macro options sets this C++ class to be * Blueprintable within the UE4 Editor */ UCLASS(Blueprintable) class COOKBOOK_API UUserProfile : public UObject { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) float Armor; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) float HpMax; };
-
返回UE编辑器,然后点击 编辑 按钮来热更新代码。
-
热更新完成后,基于UserProfile创建蓝图类。
可以直接使用上一个小节中的方法,也就是打开类查看器,搜索到UserProfile后,右键创建蓝图。
-
在 内容浏览器 选项卡里,点击 内容 。
-
然后点击上方的 新增 按钮,选择 蓝图 ,再点击蓝图里面的 蓝图类,就会弹出 选取父类 的对话框。
-
在选取父类的对话框中,点击 所有类,弹出搜索输入框和其他更多的类,我们在此输入 UserProfile ,选中 UserProfile ,然后点击 选择 按钮。
-
点击 选择 按钮后,在内容目录下就会生成 UserProfile 的蓝图类,我们将其命名为 BP_UserProfile。
-
创建完成后,我们双击它就可以打开蓝图类的编辑器。
-
现在,可以在蓝图中改变这些新的UPROPERTY字段的默认值:
由于蓝图是空的,双击打开的时候,蓝图上可能只有数据而没有中间和左边的部分。想要看到完成的蓝图菜单,你需要点击 菜单上方的 打开完整蓝图编辑器 。虽然只有数据部分,但它也是完整的可以修改的。
-
通过编辑蓝图类的默认值,可以改变由蓝图类创建的每个实例的默认值。
总结
传递给 UPROPERTY()宏 的参数指定了一些有关变量的重要信息。在之前的例子中,我们使用了下面限定符中的三个:
- EditAnywhere:表示每个变量属性不仅可以蓝图编辑器中编辑,也可以在每个拖到游戏关卡中的实例中编辑。与之相关的有以下两个:
- EditDefaultOnly:表示蓝图中可以编辑,实例中不行。
- EditInstanceOnly:表示拖放到游戏关卡中的蓝图实例可以编辑,但是蓝图类自身无法编辑。
- BlueprintReadWrite:表示属性在蓝图图表可读可写。带有BlueprintReadWrite标签的 UPROPERTY()属性必须是共有成员。否则编译会报错。与之相关的有:
- BlueprintReadOnly:表示这个属性只能从C++代码中修改,蓝图只能读。
- Category:你应该始终记得为UPROPERTY()指定一个类别,这会让C++代码和蓝图变量看起来井然有序。类别决定了UPROPERTY()归纳在属性编辑器的哪个子菜单中。所有指定 Category=Stats 的 UPROPERTY() 将会出现在蓝图编辑器的同一区域。如果没有指定类别,UPROPERTY()将归纳进UserProfile(或者你自己定义的其他的类)的默认类别中。
更多
了解整个流程很重要,这就是我们在这里进行所有操作的原因,你也通过内容浏览器找到找到C++类文件夹,然后在里面找到我们的项目,直至找到UserProfile。接着选中它,右键,在弹出的对话框中选择 创建基于UserProfile的蓝图类。
更多的更多
完整的UPROPERTY属性说明符请查看:https://docs.unrealengine.com/zh-CN/Programming/UnrealArchitecture/Reference/Properties/Specifiers/index.html
6、从蓝图中访问C++变量
从蓝图访问C++变量非常容易。要做到这一点,成员变量必须使用UPROPERTY()宏修饰以暴露出来。你必须往UPROPERTY()宏中添加 BlueprintReadOnly 或 BlueprintReadWrite 属性说明符,这样就能在蓝图中访问可读或者可读写的变量了。
你也可以使用特殊的属性说明符 BlueprintDefaultsOnly ,来指定变量只能在程序运行前通过蓝图编辑器修改。BlueprintDefaultsOnly表示数据成员无法在蓝图运行时修改(不能在事件图表中修改变量)。
步骤
-
创建继承自UObject的类,并指定Blueprintable 和BlueprintType,像下面的代码,不过这里用的还是之前的,只是新增了一些内容。
UCLASS(Blueprintable,BlueprintType) class COOKBOOK_API UUserProfile : public UObject { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) float Armor; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) float HpMax; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) FString Name; };
UCLASS 宏中声明了BlueprintType,表明该类在蓝图中将作为类型使用。
-
保存并编译代码。
-
在UE4编辑器中,从C++类 UserProfile 继承一个蓝图类。具体操作查看之前的内容。
-
双击打开刚创建的蓝图类,并修改变量Name的默认值,比如,Peter。之后,点击编译按钮,再保存。
-
在允许函数调用的蓝图中(比如关卡蓝图,可以通过点击编辑器的 蓝图 | 关卡蓝图以打开关卡蓝图),现在我们可以使用我们刚才添加的变量。我们可以在游戏开始的时候打印变量Name的值。
-
为此,我们需要在关卡蓝图中创建事件BeginPlay ,可以在蓝图中单击右键,输入BeginPlay,并选择 Event BeginPlay
现在,需要创建BP_UserProfile的实例。因为是从UObject继承过来的,所以我们不能通过拖拽实例化它,但是我们可以通过蓝图节点Construct Object from Class 来达到目的。 -
在我们刚才创建的节点右侧单击右键,然后在搜索栏中输入 construct,选择检索出来的Construct Object from Class 节点:
-
接下来,单击Event Begin节点右下角的箭头,按住拖动至Construct左上角的箭头处释放鼠标。
浏览蓝图也很简单。按住右键并拖动鼠标可以移动蓝图,而按住Alt键的同时按住鼠标右键拖动,或者滚动鼠标中键都可以缩放蓝图。你可以用鼠标左键单击一个节点,然后拖动至任何位置,也可以按住鼠标左键选择多个节点,一起移动多个节点。
-
在类的选择部分,单击下拉三角,输入我们刚创建的蓝图类(BP_Profile),并从列表中选中它。
-
你还需要给节点的Outer属性赋值,单击蓝色的圆圈,然后移动鼠标到节点的左侧后松开鼠标,在弹出的菜单里,输入单词self,然后选择 “Get a reference to self”,都整好后,应该是下面这个样子:
这会根据我们之前创建的蓝图类返回一个变量,然而,除非我们将其变成一个变量,否则没法使用它。拖拽 Return Value 属性到节点右侧后松开,选择提升至一个变量。这将自动创建一个名为NewVar_0的变量并创建一个Set节点,但是可以在之后将其重命名。 -
在Set节点的右侧,拖拽出一条白色的线,然后创建并连接Print Text节点。
-
我们现在把 Name 属性的值打印出来,在 Set 节点的右侧,拖拽出一条蓝色的线松开鼠标后,选择 Variables | Stats | Get Name节点。
-
最后,将Name值连接至 Print Text节点的In Text 上,它会自动创建一个节点,以将string类型的变量转化为Text类型的变量。
完整的如下:
一切正常的话,当你单击Play按钮,屏幕上会打印出Peter:
总结
UE4 会为UPROPERTY属性自动加上Get/Set方法,这两个方法可以在蓝图中获取或者设置属性。
7、将UCLASS指定为UPROPERTY的类型
你应该已经会创建自定义的UCLASS类了,在上面的部分,我们也使用编辑器创建了蓝图类的对象,但是你该如何在C++中实例化它们呢?UE4中的对象是引用计数和内存管理的对象,所以你不应该使用C++关键字new来为它们分配内存。你应调用ConstructObject来实例化你的UObject对象。
ConstructObject不使用C++类的名字作为参数,而是继承自C++类的类图类(一个UClass的引用)。UClass的引用只是一个指向蓝图对象的指针。
我们如何从C ++代码实例化特定蓝图的实例?C ++代码不知道也不应该知道具体的UCLASS名称,因为这些名称是在UE4编辑器中创建和编辑的,只有在编译后才能访问。我们需要一种方法去获取蓝图类的名称,以在C++代码中实例化。
方法是让UE4程序员从一个简单的下拉菜单中选择C ++代码要使用的UClass,该菜单列出了UE4编辑器中所有可用的蓝图(继承自特定的C ++类)。我们只需要提供一个用户可编辑的UPROPERTY,它带有一个TSubclassOf <C ++ ClassName>类型变量。另外,还可以使用FStringClassReference达到相同的目的。
UCLASS应该被当为C ++代码的资源,并且它们的名称绝不能硬编码到代码库中。
准备
在UE4代码中,你经常会在工程里引用到不同的UCLASS,例如,如果你知道玩家对象的UCLASS,你就能在代码中使用SpawnObject。从C++代码中指定UCLASS非常不得力,因为C++代码根本就不了解派生的UCLASS在蓝图编辑器中创建的具体实例。正如我们不想将特定资产名称烘焙到C ++代码中一样,我们也不想将派生的蓝图类名称硬编码到C ++代码中。
因此,我们使用C ++变量(例如UClassOfPlayer),然后从UE4编辑器的蓝图对话框中选择该变量。你可以使用TSubclassOf成员或FStringClassReference成员来这么干。
步骤
-
导航至你要添加UCLASS成员变量的C++类处。
-
在UCLASS内部,使用以下格式的代码声明两个UPROPERTY。
UCLASS(Blueprintable,BlueprintType) class COOKBOOK_API UUserProfile : public UObject { GENERATED_BODY() public: //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) // float Armor; //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) // float HpMax; //UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Stats) // FString Name; // 在蓝图的下拉菜单中,会列出所有继承自UObject的UClass UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Unit) TSubclassOf<UObject> UClassOfPlayer; // 显示从GameMode C ++基类派生的UCLASS的字符串名称 UPROPERTY(EditAnywhere, meta = (MetaClass = "GameMode"), Category = Unit) FStringClassReference UClassGameMode; };
-
蓝图化C ++类,然后打开该蓝图:
-
点击你的UClassOfPlayer菜单的下拉三角。
-
从列出的UClass的下拉菜单中选择适当的UClassOfPlayer成员:
总结
虚幻引擎4为我们提供了多种方法来指定UClass或期望的类类型。
TSubclassOf
当你在编辑任何具有TSubclassOf< >成员的蓝图类时,你可以使用编辑器在该成员的下拉菜单中选择需要指定的UCLASS。
FStringClassReference
MetaClass标签指的是你希望UClassName派生的C++类,这将下拉菜单的内容限制为仅从该C ++类派生的UCLASS,和其自身。如果希望在下拉菜单中显示所有UCLASS类,则可以将标签忽略。
8、实例化UObject系的类(ConstructObject<>和NewObject)
在原生C++中创建类的实例需要使用 new 关键词,但在UE4中,创建UCLASS类的实例,实际上是调用工厂方法生成UCLASS的副本。你不仅可以实例化C++类,还可以实例化蓝图类。当你创建了继承自UObject的类后,通常是调用UE4引擎自带的函数来实例化它。
工厂方法赋予UE4管理对象内存、销毁对象的能力,也让UE4能够追踪一个对象被引用的所有情况,如此,当对象销毁时,所有针对该对象的引用都容易被断开。这样一来,就确保了程序中不会存在引用到无效内存的野指针。而这个过程通常被称为垃圾回收。
ConstructObject在UE4.25的时候早已被弃用了,现在只用NewObject
准备
实例化UObject系的类,不能和实例化AActor系的类调用 UWorld::SpawnActor<> 一样,而应该调用全局函数 ConstructObject< > 或 NewObject< > 。注意,不能使用原生C++的 new 关键词给UE4系统的UObject继承关系的类实例化。
至少需要以下两个内容,才能正确实例化UCLASS实例:
- 要实例化的类类型需要是C ++类型的UClass引用(蓝图类)
- 蓝图类派生自的原生的C ++基类
步骤
在一个全局可访问的对象上(比如GameMode),添加 TSubclassOf< YourC++ClassName > UPROPERTY() 以指定UCLASS变量到C++代码中。写到GameMode类中,如下所示:
-
在Visual Studio里,打开文件 CookBookGameModeBase.h ,修改代码如下:
#pragma once #include "CoreMinimal.h" #include "GameFramework/GameModeBase.h" #include "UserProfile.h" #include "CookBookGameModeBase.generated.h" /** * */ UCLASS() class COOKBOOK_API ACookBookGameModeBase : public AGameModeBase { GENERATED_BODY() public: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = UClassNames) TSubclassOf<UUserProfile> UPBlueprintClassName; };
-
保存并编译代码;
-
从打开的UE4编辑器里,创建一个继承自该类的蓝图。双击打开蓝图,在蓝图编辑器里的默认值处,找到上面创建的变量,点击下拉菜单,选择我们之前创建的BP_UserProfile。接着保存并退出编辑器。
-
在C++代码中,找到你想要去实例化这个UCLASS实例的地方。(然鹅,下面的例子中并没有使用到变量 UPBlueprintClassName ,搞不懂…)
-
使用以下格式实例化对象:
ObjectType* Object = ConstructObject< ObjectType >(UClassReference);
例如,使用在上一个部分中指定的UserProfile对象,我们将获得如下代码
// 获取GameMode对象,它里头存在着我们想要实例化的对象的名称。 ACookBookGameModeBase *gm = Cast<ACookBookGameModeBase>( GetWorld()->GetAuthGameMode()); if (gm) { UUserProfile* newobject = NewObject<UUserProfile>((UObject*)GetTransientPackage(),UUserProfile::StaticClass()); }
总结
使用NewObject实例化UObject类很简单。ConstructObject将实例化蓝图类对象,并返回正确类型的C ++指针。
NewObject有一个不讨人喜欢的第一个参数,每次调用时需要传递GetTransientPackage()。
不要使用new关键字实例化UE4 UObject类,这将无法正确进行内存管理。
更多
NewObject函数是面向对象世界称为工厂的函数,是一种常见的设计模式。你使用工厂方法创建对象,而不是自己去构造对象。使用工厂模式可使引擎在创建对象时轻松跟踪它们。
9、回收UObject系的类对象
UE4里移除UObject对象很简单,只需调用该对象的 ConditionalBeginDestroy() 函数就行了。
准备
想要删除UObject对象,你得先创建它。
步骤
-
直接调用 ObjectInstance->ConditionalBeginDestroy() 。
-
调用ConditionalBeginDestroy()后,需要将ObjectInstance置空。
if (nobject) { nobject->ConditionalBeginDestroy(); nobject = nullptr; }
总结
ConditionalBeginDestroy() 首先会移除所有对要删除对象的引用,然后就被引擎标记为可删除。实际销毁对象的时候,会首先销毁对象内部定义的一些属性,然后才会销毁对象自身。
调用了一个对象的ConditionalBeginDestroy函数后,编写之后的代码就需要考虑到该对象已经被移除,不能再在其他地方引用了。
调用对象的ConditionalBeginDestroy函数后,实际的内存回收要过一会儿才发生。垃圾回收会按固定的时间间隔清除游戏程序不再引用的对象的内存。 C:\Program Files (x86)\Epic Games\Launcher\Engine\Config\BaseEngine.ini 中有垃圾回收具体执行的时间间隔,默认为61.1秒。
gc.TimeBetweenPurgingPendingKillObjects=61.1。
也可以调用 GetWorld()->ForceGarbageCollection(true),强制内存清理即可执行。
通常,不用太担心垃圾回收和间隔时间,除非你急切需要清理内存,也不要调用ForceGarbageCollection(true)太频繁,不然会导致本不会发生的卡顿。
10、结构体
在单独的文件中自定义结构体
#pragma once
#include "CoreMinimal.h"
#include "ColoredTexture.generated.h"
USTRUCT(BlueprintType)
struct COOKBOOK_API FColoredTexture
{
GENERATED_USTRUCT_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = HUD)
UTexture* Texture;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = HUD)
FLinearColor Color;
};
在GameMode类中使用该结构体
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "ColoredTexture.h"
#include "CookBookGameModeBase.generated.h"
/**
*
*/
UCLASS()
class COOKBOOK_API ACookBookGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
// 在其他类中使用自定义的结构体
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = HUD)
FColoredTexture Texture;
};
蓝图类中的情况:
11、枚举
在单独的头文件中声明枚举
#pragma once
#include "CoreMinimal.h"
#include "EnumName.generated.h"
UENUM(BlueprintType)
enum Status
{
Stopped UMETA(DisplayName = "Stopped"),
Moving UMETA(DisplayName = "Moving"),
Attacking UMETA(DisplayName = "Attacking"),
};
或者加上uint8
#pragma once
#include "CoreMinimal.h"
#include "EnumName.generated.h"
UENUM(BlueprintType)
enum class Status : uint8
{
Stopped UMETA(DisplayName = "Stopped"),
Moving UMETA(DisplayName = "Moving"),
Attacking UMETA(DisplayName = "Attacking"),
};
在GameMode中定义
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "EnumName.h"
#include "CookBookGameModeBase.generated.h"
/**
*
*/
UCLASS()
class COOKBOOK_API ACookBookGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Status)
TEnumAsByte<Status> status;
};
在蓝图中的情况