UE5 C++ 进阶学习 —— 01 - 基础知识

目录

一、简单使用

1.1 基础设置

1.2 创建 C++ 项目

1.2.1 蓝图项目转为 C++ 项目

1.2.2 直接创建 C++ 项目

1.3 打开 C++ 项目

1.4 启动虚幻引擎

1.5 编译代码

二、快捷键

三、命名规则

3.1 代码命名规则

3.2 其余命名规则

四、再谈 Actor

4.1 创建 Actor

4.2 在世界场景中生成 Actor 

4.3 消亡 Actor 通知

五、屏幕日志输出

六、UE 数据类型

6.1 基本数据类型

6.2 字符串类型

6.3 容器类型

6.4 类类型

6.5 结构体类型

6.6 枚举类型

七、反射

7.1 反射概念

7.2 宏

7.2.1 UCLASS() - 类标记

7.2.2 UFUNCTION() - 函数标记

7.2.3 UPROPERTY() - 变量标记


一、简单使用

1.1 基础设置

【编辑】-【编辑器偏好设置】-【通用】-【源代码】。

【编辑】-【编辑器偏好设置】-【编译】。

1.2 创建 C++ 项目
1.2.1 蓝图项目转为 C++ 项目

现在我们有一个纯蓝图项目,我们怎么将其转为 C++ 项目呢?

蓝图项目目录如下:

  • Config:存放项目的配置文件。(重要)
  • Content:游戏资源文件夹,包括蓝图、材质、动画等。(重要)
  • DerivedDataCache:缓存文件。
  • Intermediate:存放中间构建文件。
  • Saved:存放自动保存文件、日志等。
  • .uproject:项目描述文件(JSON格式)。(重要)

我们接下来做如下设置:

【工具】-【新建 C++ 类】。

【类的类型】-【公有】-【创建类】。

这样我们的蓝图项目就转为 C++ 项目啦。

C++ 项目目录如下:

  • .idea:Rider 工程配置文件。
  • .vs: Visual Studio 临时项目配置和缓存。
  • Config:存放项目的配置文件。(重要)
  • Content:游戏资源文件夹,包括蓝图、材质、动画等。(重要)
  • DerivedDataCache:缓存文件。
  • Intermediate:存放中间构建文件。
  • Saved:存放自动保存文件、日志等。
  • Source:存放 C++ 代码。(重要)
  • .vsconfig:Visual Studio 组件配置说明。
  • .sln:解决方案文件。
  • .uproject:项目描述文件(JSON格式)。(重要)
1.2.2 直接创建 C++ 项目

1.3 打开 C++ 项目

对于纯蓝图项目,我们通常双击 UECPP.uproject 进入虚幻引擎编辑器。但是对于 C++ 项目,我们通常双击 UECPP.sln 进行 C++ 编程与调试。

如果 UECPP.sln 丢失,可以右键 UECPP.uproject → Generate Visual Studio project files 进行重建。https://www.bilibili.com/opus/882311364936204310

1.4 启动虚幻引擎

在 Rider 中,进行如下操作:

  • Ctrl + F5:运行(不调试)项目,启动虚幻编辑器。

  • F5:调试模式启动虚幻编辑器。

1.5 编译代码

当我们禁用实时代码编写时,如果我们修改代码,需要关闭运行项目(不是关闭虚幻引擎编辑器)才能编译成功。下面介绍两种方法:

单击小锤子进行编译。

在虚幻引擎中进行编译。

当我们启用实时代码编写时(热重载的升级版),如果我们修改代码,不需要关闭运行项目也可以编译成功。(不推荐!)

但经测试只能在虚幻引擎中进行编译。

二、快捷键

Ctrl + T :全局搜索。

Alt + \ :当前文件搜索。

F12:跳转到定义位置。

Ctrl + 鼠标左键:跳转到定义位置。

Ctrl + K + C :注释。

三、命名规则

3.1 代码命名规则
  • 模版类以 T 作为前缀,比如 TArray,TMap,TSet。
  • UObject 派生类都以 U 前缀。
  • AActor 派生类都以 A 前缀。
  • SWidget派生类都以 S 前缀。
  • 全局对象使用 G 开头,如 GEngine。
  • 抽象接口以 I 前缀。
  • 枚举以 E 开头。
  • bool 变量以 b 前缀,如 bPendingDestruction。
  • 其他的大部分以 F 开头,如 FString, FName。
  • typedef 的以原型名前缀为准,如 typedef TArray FArrayOfMyTypes;
  • UHT 在工作的时候需要你提供正确的前缀,所以虽然说是约定,但你也得必须遵守。
  • UE遵循帕斯卡(大驼峰)命名法则。
3.2 其余命名规则

四、再谈 Actor

4.1 创建 Actor

在 Rider 中创建。

在虚幻引擎中创建。

4.2 在世界场景中生成 Actor 

静态生成:直接拖拽进世界场景中。优点是无需编码,更加直观简单;缺点是影响游戏启动速度,增加场景构建负担。

动态生成:通过编码动态生成。相对于前者复杂度上升,但是可控性更强,动态生成的 Actor 会持有有效的操作指针,我们可以根据实际情况进行生成,更加灵活。

下面举一个简单的例子,Actor 的动态生成流程如下:

  • 构造函数调用
  • 初始化成员变量
  • 如有蓝图,则初始化蓝图数据
  • 构建组件
  • BeginPlay(标志着 Actor 被创建到世界当中)
  • Tick
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameModeBase.generated.h"

/**
 * 
 */
UCLASS()
class UECPP_API AMyGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
	
protected:
	// UFUNCTION(Exec)表示该函数可以通过命令行或控制台(Console)执行
	UFUNCTION(Exec)
	void func();
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameModeBase.h"

#include "MyActor.h"

void AMyGameModeBase::func()
{
	// Actor 的生成与销毁 (不能简单的使用 new 和 delete)

	// 生成 Actor
	AMyActor* MyActor = GetWorld()->SpawnActor<AMyActor>(); // 	// GetWorld() 全局函数   SpawnActor<T>() 返回 T 类型的指针
	
	if (MyActor) // 判断指针是否有效
	{
		
	}
	
	// MyActor->AddActorWorldOffset(); 调用成员函数
	
	// 立刻删除
	// MyActor->Destroy(); // 销毁对象(会通知世界和与这个 Actor 相关的内容一并进行销毁)
	// 滞后删除
	MyActor->SetLifeSpan(5.f); // 实际内部也会调用 Destroy()
	
	MyActor = nullptr;
}
4.3 消亡 Actor 通知
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS()
class UECPP_API AMyActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AMyActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	// 当 Actor 被标记为销毁(调用 Destory 或 SetLifeSpan)(非内存删除)时触发。
	// 注意:Actor 仍存在于内存中,直到 EndPlay 被调用
	virtual void Destroyed() override;
	// 当 Actor 彻底从内存中删除时触发
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

};

五、屏幕日志输出

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

// 声明自定义日志类别(默认情况下,Unreal Engine 提供了一个全局日志类别 LogTemp)
// 参数1:自定义日志类别名称   参数2:日志默认级别,一般使用 Log   参数3:日志编译级别,一般用 All
DECLARE_LOG_CATEGORY_EXTERN(LOGME, Log, All);

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "MyGameModeBase.generated.h"

/**
 * 
 */
UCLASS()
class UECPP_API AMyGameModeBase : public AGameModeBase
{
	GENERATED_BODY()
	
protected:
	// UFUNCTION(Exec)表示该函数可以通过命令行或控制台(Console)执行
	UFUNCTION(Exec)
	void func();
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameModeBase.h"

#include "MyActor.h"

// 定义自定义日志类别(必须在 CPP 文件中进行)
DEFINE_LOG_CATEGORY(LOGME);

void AMyGameModeBase::func()
{
	// 打印日志
	// 屏幕日志输出
	// 参数1:-1 代表每次添加新消息,不会覆盖旧消息	  参数2:显示时长	 参数3:文本颜色	参数4:显示的信息	
	GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Green, TEXT("Hello"));

	// 控制台日志输出(日志会写入本地缓存) UE_LOG(LogCategory, Verbosity, Format, ...);
	// 参数1:日志类别	  参数2:日志级别   参数3:日志内容
	// UE_LOG(LogTemp, Log, TEXT("Hello")); // 常规日志级别
	// UE_LOG(LogTemp, Warning, TEXT("Hello")); // 警告日志级别
	// UE_LOG(LogTemp, Error, TEXT("Hello")); // 错误日志级别
	// UE_LOG(LogTemp, Verbose, TEXT("Hello")); // 详细日志级别,用于非常详细的调试信息。
	// UE_LOG(LogTemp, VeryVerbose, TEXT("Hello")); // 非常详细日志级别,用于极端详细的调试信息。
	// UE_LOG(LogTemp, Fatal, TEXT("Hello")); // 致命日志级别(引擎会崩溃)

	int32 Number = 10;
	UE_LOG(LogTemp, Log, TEXT("%d"), Number); // %s:字符串(const TCHAR*)   %d:整数   %f:浮点数   %d:布尔   %p:指针

	AMyActor* MyActor = GetWorld()->SpawnActor<AMyActor>();
	UE_LOG(LogTemp, Log, TEXT("%p"), MyActor);
	
	UE_LOG(LOGME, Warning, TEXT("OK"));
}

六、UE 数据类型

6.1 基本数据类型

为了方便跨平台,UE 对于 C++ 基本数据类型进行深度重定义,方便平台扩展特性,增加 UE 的移植便捷性。

布尔类型:bool

字符类型:TCHAR

整型:uint8(1字节)、uint16(2字节)、uint32(4字节)、uint64(8字节)、int8(1字节)、int16(2字节)、int32(4字节)、int64(8字节)

浮点型:float(4字节)、double(8字节)

6.2 字符串类型
  • 宏 TEXT:将字符串字面量转换为 const TCHAR* 类型。
  • FString:class FString。内部重载解引用操作符 *,返回 const TCHAR* 类型。
  • FName:class FName
  • FText:class FText

FName:资源命名字符串。

  • 存储不可变的字符串,必须用 TEXT 宏包裹字符串。
  • 相对轻量级的字符串类型,散列存储。
  • 相同字符串在内存中只存储一份,不区分大小写且只读。

FText:用于 UI 上的文本显示。

  • 存储不可变的字符串。
  • 处理用户的显式文本,提供对字符串的格式化、本地化等操作。
  • 提供了对文本的多语言支持,可以根据不同的语言提供不同的显示内容。

FString:处理字符串的主要类型。

  • 用于存储可变的字符串,必须用 TEXT 宏包裹字符串。
  • 开销大于其他类字符串类型。

基本操作如下:

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameModeBase.h"

#include <ThirdParty/ShaderConductor/ShaderConductor/External/DirectXShaderCompiler/include/dxc/DXIL/DxilConstants.h>

#include "MyActor.h"

// FText 构建方式二
#define LOCTEXT_NAMESPACE "MyGameModeBase"

void AMyGameModeBase::funcName()
{
	// --------------- FName --------------- 
	FName Name1 = TEXT("Hello");
	FName Name2(TEXT("World"));
	// 比较操作(最核心、最高频)
	if (Name1 != Name2)
	{
		UE_LOG(LogTemp, Log, TEXT("%s"), *Name1.ToString());
	}

	// --------------- FText --------------- 
	// 构建方式一(推荐):宏 NSLOCTEXT
	// 参数1:命名空间(我们一般把同一个页面的文本放在一个命名空间)		参数2:Key(同命名空间内唯一)   参数3:默认显示的文本
	FText t1 = NSLOCTEXT("MyGameModeBase", "Key1", "hello");

	// 构建方式二:宏 LOCTEXT
	// t2 和 t3 在同一个命名空间
	FText t2 = LOCTEXT("Key2", "二狗"); 
	FText t3 = LOCTEXT("Key3", "张三");

	// 格式化文本
	FNumberFormattingOptions Options;
	Options.UseGrouping = false; // 取消数字分组
	FText t5 = FText::Format(LOCTEXT("Key4","我叫{0}, 今年{1}岁。"), t2, FText::AsNumber(20000, &Options)); // FText::AsNumber() 将数字转换为 FText

	UE_LOG(LogTemp, Log, TEXT("%s"), *t5.ToString());

	// --------------- FString --------------- 
	FString str1 = TEXT("Hello");
	FString str2(TEXT("World"));
	UE_LOG(LogTemp, Log, TEXT("%s"), *str1);

	// 1 .Len() 获取字符个数
	UE_LOG(LogTemp, Log, TEXT("%d"), str1.Len());  // 5
	// 2 .Contains() 查找是否包含指定字符串(返回 bool)   默认忽略大小写
	UE_LOG(LogTemp, Log, TEXT("%d"), str1.Contains(TEXT("He"))); // 1  
	// 3 .find() 查找指定字符串的位置(如果没有找到,返回-1)   默认忽略大小写
	UE_LOG(LogTemp, Log, TEXT("%d"), str1.Find(TEXT("llo"))); // 2
	// 4 .Split() 分割字符串
	FString str3 = TEXT("你好_小明");
	FString str4, str5;
	if (str3.Split(TEXT("_"), &str4, &str5))
	{
		UE_LOG(LogTemp, Log, TEXT("%s %s"), *str4, *str5);
	}
	// 5 + 或者 .Append()  组合字符串
	UE_LOG(LogTemp, Log, TEXT("%s"), *(str1 + str2));
	UE_LOG(LogTemp, Log, TEXT("%s"), *(str1.Append(str2))); // str1 变化
	// 6 == 或者 .Equals() 比较两个字符串是否相同
	FString str6 = TEXT("test");
	FString str7 = TEXT("test");
	if (str6 == str7)
	{
		UE_LOG(LogTemp, Log, TEXT("same"));
	}
	if (str6.Equals(str7))
	{
		UE_LOG(LogTemp, Log, TEXT("same"));
	}
	// 7 .Compare() 比较两个字符串(返回 int32)
	FString str8 = TEXT("123");
	FString str9 = TEXT("125");
	UE_LOG(LogTemp, Log, TEXT("%d"), str8.Compare(str9));  // -1
	UE_LOG(LogTemp, Log, TEXT("%d"), str8.Compare(str8));  // 0
	UE_LOG(LogTemp, Log, TEXT("%d"), str9.Compare(str8));  // 1
	// 8 .EndsWith() .StartsWith() 检索头尾是否是指定的字符串
	FString str10 = TEXT("test.txt");
	if (str10.EndsWith(".txt"))
	{
		UE_LOG(LogTemp, Log, TEXT("结尾是.txt"));
	}
	// 9 .Empty() 清空字符串并释放内存  .Reset() 清空字符串但不释放内存
	// str10.Empty();
	// str10.Reset();
	// 10 .ToUpper() .ToLower() 大小写转换
	UE_LOG(LogTemp, Log, TEXT("%s"), *(str10.ToUpper()));
	UE_LOG(LogTemp, Log, TEXT("%s"), *(str10.ToLower()));
	// 11 .Replace() 替换
	UE_LOG(LogTemp, Log, TEXT("%s"), *(str10.Replace(TEXT(".txt"), TEXT(".html"))));
}

// FText 构建方式二
#undef LOCTEXT_NAMESPACE

三者之间转换如下:

我们怎么记忆呢?转换成某种类型,这种类型有专门的规律。

// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameModeBase.h"

#include <ThirdParty/ShaderConductor/ShaderConductor/External/DirectXShaderCompiler/include/dxc/DXIL/DxilConstants.h>

#include "MyActor.h"


void AMyGameModeBase::funcName()
{
	FName n1 = TEXT("我是FName");
	FText t1 = NSLOCTEXT("MyGameModeBase", "keyt1", "我是FText");
	FString s1 = TEXT("我是FString");

	// FName 转换成 FString
	FString s2 = n1.ToString();
	// FName 转换成 FText
	FText t2 = FText::FromName(n1);
	
	// FText 转换成 FString
	FString s3 = t1.ToString();
	// FText 转换成 FName
	FName n2 = FName(*t1.ToString());
	
	// FString 转换成 FName
	FName n3 = FName(*s1);
	// FString 转换成 FText
	FText t3 = FText::FromString(s1);
	
}
6.3 容器类型

TArray<T>:内存连续的动态数组

  • 是一个同质容器,即其所有元素均完全为相同类型;
  • 具有速度快、内存消耗小、安全性高等特点;
  • 与 std::vector 类似,可动态扩充内存。

TSet<T>:存储唯一值的集合

  • 是一个同质容器,即其所有元素均完全为相同类型;
  • 默认不允许元素重复,存储唯一元素;
  • 基于哈希表实现,提供了快速的查找、插入和删除操作;

TMap<k, T>:key-value 类型容器

  • 是一个同质容器,即其所有元素均完全为相同类型;
  • 键必须是唯一的,而值不是唯一的;(键也可以为自定义类型,但需要经过处理)
  • 基于哈希表实现,提供了高效的查找性能;
// Fill out your copyright notice in the Description page of Project Settings.


#include "MyGameModeBase.h"

#include <ThirdParty/ShaderConductor/ShaderConductor/External/DirectXShaderCompiler/include/dxc/DXIL/DxilConstants.h>

#include "MyActor.h"


void AMyGameModeBase::funcName()
{
	// ----------------------------- TArray ----------------------------- 
	// 1. 构建
	TArray<int32> array;
	// 2. 初始化
	array.Init(10, 5);
	// 3. 返回元素个数
	UE_LOG(LogTemp, Log, TEXT("%d"), array.Num()); // 5
	// 4. 遍历
	for (int32 i = 0; i < array.Num(); ++i)
	{
		UE_LOG(LogTemp, Log, TEXT("%d"), array[i]); // 10 10 10 10 10
	}
	for (const auto& v : array) // 本质是迭代器遍历
	{
		UE_LOG(LogTemp, Log, TEXT("%d"), v); // 10 10 10 10 10
	}
	// 5. 添加元素
	array.Add(20); // 10 10 10 10 10 20
	array.AddUnique(20); // 若存在重复元素,则添加失败
	array.Insert(999, 0); // 999 10 10 10 10 10 20
	// 6. 设置容器大小,类似于 vector 的 resize()
	array.SetNum(10); // 999 10 10 10 10 10 20 0 0 0
	array.SetNum(7); // 999 10 10 10 10 10 20
	// 7. 转换为普通数组(返回容器模板类型的指针)
	int32* ptr = array.GetData();
	// 8. 常规查询函数 略
	// 9. 常规移除函数 略

	// ----------------------------- TSet -----------------------------
	// 1. 构建
	TSet<int32> set;
	// 2. 添加元素
	set.Add(1);
	set.Add(2);
	// 3. 删除元素
	set.Remove(2);
	// 4. 返回元素个数
	UE_LOG(LogTemp, Log, TEXT("%d"), set.Num());
	// 5. 查询是否包含某个元素
	if (set.Contains(1))
	{
		UE_LOG(LogTemp, Log, TEXT("包含"));
	}
	// 6. 遍历
	for (const auto& v : set)
	{
		UE_LOG(LogTemp, Log, TEXT("%d"), v);
	}
	// 7. 清空
	// set.Empty(); // 清空映射并释放内存
	// set.Reset(); // 清空映射但不释放内存
	
	// ----------------------------- TMap -----------------------------
	// 1. 构建
	TMap<int32, FString> map;
	// 2. 添加元素
	map.Add(10, TEXT("小猫"));
	map.Add(20, TEXT("小狗"));
	map.Add(30, TEXT("小虎"));
	// 3. 删除元素
	map.Remove(20);
	// 4. 更改元素
	map[10] = TEXT("小蛇");
	// 5. 返回元素个数
	UE_LOG(LogTemp, Log, TEXT("%d"), map.Num());  // 2
	// 6. 查询是否包含某个元素
	if (map.Contains(10))
	{
		UE_LOG(LogTemp, Log, TEXT("包含"));
	}
	// 7. 遍历
	for (const auto& kv : map)
	{
		UE_LOG(LogTemp, Log, TEXT("%d: %s"), kv.Key, *(kv.Value));
	}
	// 8. 组合
	TMap<int32, FString> TempMap;
	TempMap.Add(999, TEXT("test"));
	map.Append(TempMap);
	// 9. 清空
	// map.Empty(); // 清空映射并释放内存
	// map.Reset(); // 清空映射但不释放内存

}
6.4 类类型

Object:UE5 中的根基类。包括但不限于以下几点功能:

  • GC(垃圾回收机制):当你创建一个继承自 UObject 的对象(需标记为 UPROPERTY)时,引擎会自动追踪它的引用关系。只要这个对象还被其他"存活"的对象引用着,它就不会被销毁;一旦所有引用都消失了,GC 就会在适当的时候自动回收它。见下方案例。
  • 序列化:序列化就是将内存中对象的变量(需标记为 UPROPERTY)转换为可存储的数据格式(通常是二进制),反序列化则是反向过程。例如保存 / 加载关卡等。
  • 反射:允许程序在运行时查询、访问和操作类、变量和函数的信息。
6.5 结构体类型
#include "CoreMinimal.h" // 需要包含此头文件(自动包含)

// 允许作为蓝图中的变量类型使用,需要添加 BlueprintType
USTRUCT(BlueprintType)
struct FMyStruct // 必须 F 开头
{
	GENERATED_BODY()

	UPROPERTY(BlueprintReadOnly, EditAnywhere)
	int32 Num;
};
6.6 枚举类型
#include "CoreMinimal.h" // 需要包含此头文件(自动包含)

// 允许作为蓝图中的变量类型使用,需要添加 BlueprintType
UENUM(BlueprintType)
enum class EEnum : uint8 // 必须 E 开头
{
    EONE,
    Menu,
    Close
};

七、反射

7.1 反射概念

Unreal Engine 的反射系统允许程序在运行时查询、访问和操作类、变量和函数的信息。然而,标准的 C++ 本身并不支持这种级别的反射。

其中,宏标记的作用就是为 HUT 提供指令。UHT 在编译前扫描你的代码,根据这些标记生成额外的反射代码(*.generated.h 文件),从而将你的 C++ 代码“暴露”给 UE 的各个系统,如蓝图编辑器、序列化、网络复制和垃圾回收。

7.2 宏

注意:UFUNCTION()、UPROPERTY()、UCLASS() 等宏中的这些标记,不会影响 C++ 代码的编译或运行逻辑,只会影响蓝图系统、反射系统和编辑器的行为。

7.2.1 UCLASS() - 类标记

作用:将一个 C++ 类声明为 UE 可识别的、可参与引擎对象管理的 UObject。

常用选项如下(会被继承):

  • Blueprintable:允许在蓝图中基于此类创建蓝图子类(默认行为)。例如 AActor 通常带有这个标记。
  • NotBlueprintable:禁止蓝图继承此类。适用于仅供 C++ 使用的类。
  • BlueprintType:允许此类作为蓝图中的变量类型或参数类型使用。
  • NotBlueprintType:禁止该类在蓝图中被引用(如变量类型)。
  • Abstract:声明该类为抽象类(无法直接实例化,只能被继承)

示例代码如下:

#pragma once
 
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h" // 必须放在最后
 
UCLASS(Blueprintable) 
class MYGAME_API AMyActor : public AActor
{
    GENERATED_BODY()  // // 必须包含在类体内
 
public:    
    AMyActor();
 
protected:
    virtual void BeginPlay() override;
 
public:    
    virtual void Tick(float DeltaTime) override;
};

补充:

U 类对象实例化:通过工厂方法 NewObject<class>()。

U 类对象销毁:xx = nullptr。

A 类对象实例化:GetWorld()->SpawnActor<class>()。

A 类对象销毁:xx.Destory()。

7.2.2 UFUNCTION() - 函数标记

作用:暴露函数给蓝图系统。(不建议将私有域中的函数进行暴露)

常用选项如下(会被继承):

  • BlueprintCallable:函数可在蓝图中调用(有执行引脚)。
  • BlueprintPure:函数可在蓝图中调用(函数必须要有返回值)(没有执行引脚)。
  • BlueprintImplementableEvent:在 C++ 中声明,但在蓝图中定义的事件/函数(可以在函数重载中找到。和蓝图中直接创建的事件不同,该事件/函数无法在蓝图中调用,除非加 BlueprintCallable 标记)。(类似纯虚函数)
  • BlueprintNativeEvent:在 C++ 中声明并定义(定义时会生成一个带有后缀的函数,但是调用时需要调用没有后缀的函数),也可在蓝图中重写。(可以在函数重载中找到。和蓝图中直接创建的事件不同,该事件/函数无法在蓝图中调用,除非加 BlueprintCallable 标记)(类似虚函数)
  • Category="xx|xx|xx...":设置蓝图节点的分类名。
  • meta=(...):附加元信息(如参数提示、隐藏某参数、限制输入范围等)
UFUNCTION(BlueprintImplementableEvent, meta=(DisplayName = "Tick")) // 修改在蓝图中的节点名称
ENGINE_API void ReceiveTick(float DeltaSeconds);
7.2.3 UPROPERTY() - 变量标记

作用:暴露变量给属性系统,使其可被编辑器详情面板显示、被蓝图访问、参与网络复制、序列化等。

  • BlueprintReadOnly:蓝图只读。 最常用、最安全。
  • BlueprintReadWrite:蓝图可读也可写。
  • EditAnywhere: 在类默认值和实例上可见且可编辑(我们很少在指针类型(A 类)变量上加此标记。此外,对于资产类型的指针我们可以加此标记,例如 UTexture* 等(硬引用))。
  • EditDefaultsOnly:仅在类默认值中可编辑,实例上不可编辑。 用于定义类的固有属性(如最大血量、速度)。
  • EditInstanceOnly:仅在实例上可编辑,类默认值中不可编辑。 用于每个实例都不同的属性(如 NPC 的名字)。
  • VisibleAnywhere:在类默认值和实例上可见但不可编辑 (唯一用法:标记组件)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值