UE4 C++ 创建类

8 篇文章 1 订阅

在这里插入图片描述


这章将涉及以下内容:

  • 制作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++代码进行编译和运行,并适时调用操作符newdelete来创建和销毁你的自定义对象。常规C++代码在UE4项目里也能很好地运行,只要你正确调用newdelete,就不会发生内存泄露的问题。

当然,你也可以使用UCLASS宏来声明自定义类,让它表现得就像是UE4自身的类。UCLASS修饰的类表示该类将使用UE4内置的智能指针,以及依据智能指针规则进行的对象分配和销毁的内存管理机制。

注意,使用UCLASS宏修饰类后,对象的创建和销毁将由UE4完全控制。你必须使用ConstructObject函数创建对象实例(对应着常规C++的new),然后调用UObject:ConditionBeginDestroy() 函数销毁对象实例(对应着常规C++的delete)。

准备

这部分,我们会重点讲解如何创建有UCLASS宏修饰的C++类,以托管内存的分配和解除分配,并允许从UE4编辑器和蓝图进行访问。为了达成这一目标,请准备好UE4项目以添加新代码。

步骤

创建继承自 UObject 的类,通常有以下几个步骤:

熟悉UE4编码以后,复制粘贴才是最为便捷的。

  1. 项目打开后,在编辑器内,依次点击 文件|新建C++类

  2. 添加C++类对话框里的右上处,勾选显示所有类的复选框。
    在这里插入图片描述

  3. 选择Object(继承的最顶层)作为我们要继承的父类,然后点击下一步。

    1)请注意,虽然对话框中显示的是Object,但是在C ++代码中,我们继承的C ++类实际上是带有大写U的UObject。这是UE4的命名约定;
    2)继承自UObjectUCLASS类,需要以U开头来命名;
    3)继承自AActor的,必须以A开头来命名;
    4)未继承任何类的C++类(非UCLASS类)没有命令规则,但是最好以前缀F开头(例如,FAssetData);
    5)直接继承自UObject的类不能放置到关卡中,即使该类包含了像UStaticMeshes的可视化元素。如果要将你的对象放置到UE4关卡中,那么必须的继承自AActor类,或者它的子类。更多内容,请阅读第四章。

    这一章的示例代码都不能放置到编辑器关卡中,但是你可以基于C++类在蓝图图表中创建和使用对象。

  4. 根据你正在创建的Object派生类的对象类型来给其命名。这里就叫它 UserProfile
    在这里插入图片描述
    UE4为我们生成的C++文件中,类的名称将是UUserProfile,以遵循UE4的命名规范。

  5. 点击创建类按钮,编译完成后,文件就会被创建出来。之后,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()
    
    };
    
  6. 编译并运行你的项目。你现在可以在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的派生类才行。

使用FStingAssetReferencesStaticLoadObject,是一种加载资源(比如贴图)的方法,但这种方法是将资源路径字符串硬编码到C++中,通常是不推荐使用的。而使用UPROPERT() 提供可编辑的值供蓝图使用,然后C++中获取资源引用,才是值得推荐的。

准备

你需要构造好一个可供蓝图类继承的UCLASS类,来练习本部分的内容。也就是说,你的C++类需要以Blueprintable修饰UCLASS宏,使得在引擎中蓝图化C++类成为可能。

步骤

  1. 要蓝图化你的UserProfile类,首先要保证的是UCLASS宏中有Blueprintable的标签,看起来就像下面这样:

    UCLASS(BlueprintType)
    class COOKBOOK_API UUserProfile : public UObject
    
  2. 编译代码。

  3. 在类查看器(窗口 | 开发者工具 | 类查看器)中找到UserProfile类,因为之前创建C++类的时候不是继承自Actor,想要找到我们自定义的类,需要关闭 过滤器 | 仅Actor,这玩意儿默认是勾选着的。
    在这里插入图片描述
    如果取消勾选的话,我们自定义的类就找不到了。

    注意:使用类查看器的搜索功能,很快就能搜索到我们要的。

  4. 在类查看器中找到UserProfile类后,右键它,然后通过选择创建蓝图… 创建蓝图。

  5. 给蓝图命名。BP_ 是一个不错的前缀。当然了,你也可以遵循其他的命名规则,你要你懂我懂大家懂就行。

  6. 你可以编辑任何你为蓝图类创建的字段。

    如果蓝图编辑器没有自动打开,你可以在内容浏览器中双击蓝图类打开它。

总结

只要你创建的C++类的UCLASS宏中有Blueprintable标签,它就可以在UE4编辑器中蓝图化(被蓝图继承)。蓝图赋予你在UE4可视化GUI窗口自定义C++类属性。

5、创建可编辑属性UPROPERTY

UCLASS 类中可以声明任意数量的 UPROPERTY 属性。每个 UPROPERTY 属性可以是可视化可编辑的字段,也可以是类的可访问的数据成员。

有非常多的限定词可以添加到 UPROPERTY 中,每一个限定词都会影响属性在UE4编辑器中的表现,比如 EditAnywhere (标明属性可以被代码或蓝图编辑器修改),BlueprintReadWrite (标明变量在任何时候都可以被蓝图读写,C++代码也能这么干)。

准备

开始这部分前,你应该有个能添加C++代码的项目了。此外,你已经完成了上一个部分。

步骤

  1. 首先,我们给类的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;
    };
    
  2. 返回UE编辑器,然后点击 编辑 按钮来热更新代码。

  3. 热更新完成后,基于UserProfile创建蓝图类。

    可以直接使用上一个小节中的方法,也就是打开类查看器,搜索到UserProfile后,右键创建蓝图。

  4. 内容浏览器 选项卡里,点击 内容
    在这里插入图片描述

  5. 然后点击上方的 新增 按钮,选择 蓝图 ,再点击蓝图里面的 蓝图类,就会弹出 选取父类 的对话框。
    在这里插入图片描述

  6. 在选取父类的对话框中,点击 所有类,弹出搜索输入框和其他更多的类,我们在此输入 UserProfile ,选中 UserProfile ,然后点击 选择 按钮。
    在这里插入图片描述

  7. 点击 选择 按钮后,在内容目录下就会生成 UserProfile 的蓝图类,我们将其命名为 BP_UserProfile
    在这里插入图片描述

  8. 创建完成后,我们双击它就可以打开蓝图类的编辑器。

  9. 现在,可以在蓝图中改变这些新的UPROPERTY字段的默认值:
    在这里插入图片描述

    由于蓝图是空的,双击打开的时候,蓝图上可能只有数据而没有中间和左边的部分。想要看到完成的蓝图菜单,你需要点击 菜单上方的 打开完整蓝图编辑器 。虽然只有数据部分,但它也是完整的可以修改的。

  10. 通过编辑蓝图类的默认值,可以改变由蓝图类创建的每个实例的默认值。

总结

传递给 UPROPERTY()宏 的参数指定了一些有关变量的重要信息。在之前的例子中,我们使用了下面限定符中的三个:

  • EditAnywhere:表示每个变量属性不仅可以蓝图编辑器中编辑,也可以在每个拖到游戏关卡中的实例中编辑。与之相关的有以下两个:
    • EditDefaultOnly:表示蓝图中可以编辑,实例中不行。
    • EditInstanceOnly:表示拖放到游戏关卡中的蓝图实例可以编辑,但是蓝图类自身无法编辑。
  • BlueprintReadWrite:表示属性在蓝图图表可读可写。带有BlueprintReadWrite标签的 UPROPERTY()属性必须是共有成员。否则编译会报错。与之相关的有:
    • BlueprintReadOnly:表示这个属性只能从C++代码中修改,蓝图只能读。
  • Category:你应该始终记得为UPROPERTY()指定一个类别,这会让C++代码和蓝图变量看起来井然有序。类别决定了UPROPERTY()归纳在属性编辑器的哪个子菜单中。所有指定 Category=StatsUPROPERTY() 将会出现在蓝图编辑器的同一区域。如果没有指定类别,UPROPERTY()将归纳进UserProfile(或者你自己定义的其他的类)的默认类别中。

更多

了解整个流程很重要,这就是我们在这里进行所有操作的原因,你也通过内容浏览器找到找到C++类文件夹,然后在里面找到我们的项目,直至找到UserProfile。接着选中它,右键,在弹出的对话框中选择 创建基于UserProfile的蓝图类
在这里插入图片描述

更多的更多

完整的UPROPERTY属性说明符请查看:https://docs.unrealengine.com/zh-CN/Programming/UnrealArchitecture/Reference/Properties/Specifiers/index.html

6、从蓝图中访问C++变量

从蓝图访问C++变量非常容易。要做到这一点,成员变量必须使用UPROPERTY()宏修饰以暴露出来。你必须往UPROPERTY()宏中添加 BlueprintReadOnlyBlueprintReadWrite 属性说明符,这样就能在蓝图中访问可读或者可读写的变量了。

你也可以使用特殊的属性说明符 BlueprintDefaultsOnly ,来指定变量只能在程序运行前通过蓝图编辑器修改。BlueprintDefaultsOnly表示数据成员无法在蓝图运行时修改(不能在事件图表中修改变量)。

步骤

  1. 创建继承自UObject的类,并指定BlueprintableBlueprintType,像下面的代码,不过这里用的还是之前的,只是新增了一些内容。

    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,表明该类在蓝图中将作为类型使用。

  2. 保存并编译代码。

  3. 在UE4编辑器中,从C++类 UserProfile 继承一个蓝图类。具体操作查看之前的内容。

  4. 双击打开刚创建的蓝图类,并修改变量Name的默认值,比如,Peter。之后,点击编译按钮,再保存。
    在这里插入图片描述

  5. 在允许函数调用的蓝图中(比如关卡蓝图,可以通过点击编辑器的 蓝图 | 关卡蓝图以打开关卡蓝图),现在我们可以使用我们刚才添加的变量。我们可以在游戏开始的时候打印变量Name的值。

  6. 为此,我们需要在关卡蓝图中创建事件BeginPlay ,可以在蓝图中单击右键,输入BeginPlay,并选择 Event BeginPlay
    在这里插入图片描述
    现在,需要创建BP_UserProfile的实例。因为是从UObject继承过来的,所以我们不能通过拖拽实例化它,但是我们可以通过蓝图节点Construct Object from Class 来达到目的。

  7. 在我们刚才创建的节点右侧单击右键,然后在搜索栏中输入 construct,选择检索出来的Construct Object from Class 节点:
    在这里插入图片描述

  8. 接下来,单击Event Begin节点右下角的箭头,按住拖动至Construct左上角的箭头处释放鼠标。

    浏览蓝图也很简单。按住右键并拖动鼠标可以移动蓝图,而按住Alt键的同时按住鼠标右键拖动,或者滚动鼠标中键都可以缩放蓝图。你可以用鼠标左键单击一个节点,然后拖动至任何位置,也可以按住鼠标左键选择多个节点,一起移动多个节点。

  9. 在类的选择部分,单击下拉三角,输入我们刚创建的蓝图类(BP_Profile),并从列表中选中它。

  10. 你还需要给节点的Outer属性赋值,单击蓝色的圆圈,然后移动鼠标到节点的左侧后松开鼠标,在弹出的菜单里,输入单词self,然后选择 “Get a reference to self”,都整好后,应该是下面这个样子:
    在这里插入图片描述
    这会根据我们之前创建的蓝图类返回一个变量,然而,除非我们将其变成一个变量,否则没法使用它。拖拽 Return Value 属性到节点右侧后松开,选择提升至一个变量。这将自动创建一个名为NewVar_0的变量并创建一个Set节点,但是可以在之后将其重命名。

  11. 在Set节点的右侧,拖拽出一条白色的线,然后创建并连接Print Text节点。

  12. 我们现在把 Name 属性的值打印出来,在 Set 节点的右侧,拖拽出一条蓝色的线松开鼠标后,选择 Variables | Stats | Get Name节点。

  13. 最后,将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成员来这么干。

步骤

  1. 导航至你要添加UCLASS成员变量的C++类处。

  2. 在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;
    };
    
  3. 蓝图化C ++类,然后打开该蓝图:
    在这里插入图片描述

  4. 点击你的UClassOfPlayer菜单的下拉三角。

  5. 从列出的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类中,如下所示:

  1. 在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;
    };
    
  2. 保存并编译代码;

  3. 从打开的UE4编辑器里,创建一个继承自该类的蓝图。双击打开蓝图,在蓝图编辑器里的默认值处,找到上面创建的变量,点击下拉菜单,选择我们之前创建的BP_UserProfile。接着保存并退出编辑器。
    在这里插入图片描述

  4. 在C++代码中,找到你想要去实例化这个UCLASS实例的地方。(然鹅,下面的例子中并没有使用到变量 UPBlueprintClassName ,搞不懂…)

  5. 使用以下格式实例化对象:

    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对象,你得先创建它。

步骤

  1. 直接调用 ObjectInstance->ConditionalBeginDestroy()

  2. 调用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;
};

在蓝图中的情况
在这里插入图片描述

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UE4中,根据不同的别,有不同的方法来创建对象。 对于继承自UObject但不继承自Actor的,我们可以使用NewObject函数来创建对象。例如,我们可以创建一个UObjectClass的对象,代码如下: ```cpp UObjectClass* MyClass = NewObject<UObjectClass>(); ``` 这样创建的对象将由UE4引擎来管理。 对于纯C++(即非继承自UObject的,一般以F开头),我们可以使用new关键字来创建对象,并使用TSharedPtr和TSharedRef来管理对象。例如,我们可以创建一个MyClass的对象,代码如下: ```cpp TSharedPtr<MyClass> MyClassPtr = MakeShareable(new MyClass()); ``` 这样创建的对象将由TSharedPtr来进行引用计数,并在不再需要时自动释放内存。 对于继承自UObject的组件,我们也可以像创建UObject子一样使用NewObject函数来创建组件对象。但是,创建组件后,我们需要使用RegisterComponent或RegisterAllComponents函数将其注册才能使其生效。例如,我们可以创建一个UStaticMeshComponent的组件对象,并注册到当前对象上,代码如下: ```cpp UStaticMeshComponent* MyMeshComp = NewObject<UStaticMeshComponent>(this, TEXT("MyMeshComp")); MyMeshComp->SetupAttachment(RootComponent); MyMeshComp->SetRelativeLocation(FVector(0.f, 0.f, 0.f)); UStaticMesh* StaticMesh = LoadObject<UStaticMesh>(NULL, TEXT("StaticMesh'/Game/StaticMesh.StaticMesh'")); MyMeshComp->SetStaticMesh(StaticMesh); MyMeshComp->RegisterComponent(); ``` 这样创建的组件对象将由UE4引擎来管理,并在适当的时候进行更新和渲染。 总结起来,UE4创建对象的方法根据别的不同而不同。对于继承自UObject的,我们可以使用NewObject函数来创建对象;对于纯C++,我们可以使用new关键字来创建对象,并使用TSharedPtr和TSharedRef来管理对象;对于继承自UObject的组件,我们可以使用NewObject函数来创建组件对象,并使用RegisterComponent或RegisterAllComponents函数将其注册。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值