Unreal Engine C++ 编程基础
Unreal Engine C++ 编程基础
本教程将引导你从零开始,掌握使用 C++ 进行虚幻引擎开发的核心知识。我们将避开冗长的理论,直接通过实用的示例、清晰的解释,让你快速上手 UE5 C++ 编程。
环境准备
在编写第一行代码之前,请确保开发环境配置正确。
安装必备软件
- Epic Games Launcher:从虚幻引擎官网下载并安装启动器。
- Unreal Engine 5:在启动器中安装推荐版本的引擎(建议5.3或更高)。
- Visual Studio 2022:下载安装社区版,必须勾选“使用 C++ 的游戏开发”工作负载,以及“Unreal Engine 安装程序”组件。
- Windows SDK / NET Core 3.1:通常随 VS2022 工作负载自动安装,若编译出错可手动补全。
验证编译环境
启动 UE5,创建空白 C++ 项目,若能自动打开 VS 并成功编译,则环境就绪。如遇到“找不到 XXX.h”错误,检查引擎安装路径是否包含空格或中文,并确认为项目安装了正确的模块依赖。
创建你的第一个 C++ 项目
- 打开 UE5 启动器,点击 游戏 → 空白 模板。
- 项目设置中:
- 蓝图 改为 C++
- 选择 无入门内容
- 项目名称用英文,避免路径包含中文或空格
- 点击创建,引擎会自动生成
.sln并用 Visual Studio 打开。
虚幻 C++ 核心概念
UE 的 C++ 并非标准 C++,而是构建在标准之上的反射和宏系统。理解这些概念是高效编程的基础。
反射与 UHT(Unreal Header Tool)
UE 使用自定义的预处理器 UHT 来解析头文件中的特殊宏,生成用于反射、序列化、垃圾回收的胶水代码。因此,所有参与 UE 反射系统的类、属性、函数都必须包含对应的宏。
必须知道的 UHT 宏
- UCLASS():标记类可被 UE 反射。常用的说明符:
BlueprintType(可在蓝图中使用)、Blueprintable(可被蓝图继承)。 - UPROPERTY():将成员变量暴露给反射。常用说明符:
EditAnywhere(可在细节面板编辑)、BlueprintReadOnly(蓝图只读)、BlueprintReadWrite(蓝图读写)、VisibleAnywhere(不可编辑但可见)。 - UFUNCTION():暴露出函数到反射系统。常用说明符:
BlueprintCallable(蓝图可调用)、BlueprintImplementableEvent(C++声明,蓝图实现)、BlueprintNativeEvent(C++可提供默认实现,蓝图可重载)。
游戏核心类概览
| 类名 | 功能 | 常用场景 |
|---|---|---|
| UObject | 所有反射对象的基类,提供序列化、网络复制、垃圾回收 | 数据资产、游戏子系统 |
| AActor | 可放入关卡的基础游戏对象 | 放置物、触发器、玩家生成物 |
| APawn | 可被控制器操纵的 Actor | 玩家角色、AI 控制的实体 |
| ACharacter | 内置胶囊体、移动组件、骨骼网格的专用 Pawn | 人形玩家、敌人角色 |
| APlayerController | 玩家输入与视角管理 | 处理玩家输入、HUD |
| AGameModeBase / AGameMode | 定义游戏规则,负责生成玩家控制器、Pawn | 团队死亡竞赛、合作模式 |
| UActorComponent | 可附加到 Actor 的功能模块 | 音频播放、粒子效果、移动逻辑 |
编写你的第一个 C++ 类
我们将创建一个简单的交互式物体:按下键盘 E 键时改变颜色并播放声音。
步骤一:创建 C++ 类
在编辑器中,点击 工具 → 新建C++类,选择父类为 Actor,命名为 InteractiveBox。创建后 Visual Studio 会自动打开生成的两个文件:
InteractiveBox.h(头文件)InteractiveBox.cpp(实现文件)
步骤二:编辑头文件 (InteractiveBox.h)
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "InteractiveBox.generated.h"
UCLASS()
class YOURPROJECT_API AInteractiveBox : public AActor
{
GENERATED_BODY()
public:
AInteractiveBox();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
// 静态网格体组件,代表物体外观
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
class UStaticMeshComponent* MeshComp;
// 音频组件,用于播放音效
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
class UAudioComponent* AudioComp;
// 可在细节面板自定义颜色
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Interaction")
FLinearColor HighlightColor = FLinearColor::Red;
// 可交互调用
UFUNCTION(BlueprintCallable, Category = "Interaction")
void Interact();
private:
FLinearColor OriginalColor;
bool bIsHighlighted = false;
};
步骤三:实现核心逻辑 (InteractiveBox.cpp)
#include "InteractiveBox.h"
#include "Components/StaticMeshComponent.h"
#include "Components/AudioComponent.h"
#include "Sound/SoundBase.h"
AInteractiveBox::AInteractiveBox()
{
PrimaryActorTick.bCanEverTick = true;
// 创建根组件
RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
// 创建静态网格组件并附加到根
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
MeshComp->SetupAttachment(RootComponent);
// 创建音频组件
AudioComp = CreateDefaultSubobject<UAudioComponent>(TEXT("Audio"));
AudioComp->SetupAttachment(RootComponent);
AudioComp->bAutoActivate = false; // 默认不播放
}
void AInteractiveBox::BeginPlay()
{
Super::BeginPlay();
// 记录初始颜色(如果材质有颜色)
if (MeshComp && MeshComp->GetMaterial(0))
{
OriginalColor = HighlightColor; // 简便方式,可从材质获取
}
}
void AInteractiveBox::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AInteractiveBox::Interact()
{
if (!MeshComp) return;
bIsHighlighted = !bIsHighlighted;
// 切换材质颜色(示例仅通过矢量参数,实际可能需要动态材质实例)
UMaterialInstanceDynamic* DynMat = MeshComp->CreateAndSetMaterialInstanceDynamic(0);
if (DynMat)
{
DynMat->SetVectorParameterValue(TEXT("BaseColor"), bIsHighlighted ? HighlightColor : OriginalColor);
}
// 播放音效
if (AudioComp && AudioComp->Sound)
{
AudioComp->Play();
}
}
步骤四:深入理解代码
- 构造函数中的组件创建:
CreateDefaultSubobject是创建组件的唯一推荐方式。组件必须附加到根组件或其它组件上。 - UPROPERTY 宏的作用:使得变量能被编辑器识别并保存。添加了
EditAnywhere的属性会出现在细节面板中。 - 交互函数:
Interact被标记为BlueprintCallable,意味着可以在蓝图中直接调用此函数。 - 动态材质实例:要在运行时修改材质参数,需使用
UMaterialInstanceDynamic,否则修改会应用到所有使用该材质的物体。
步骤五:蓝图调用 C++ 函数
在编辑器中创建一个基于 InteractiveBox 的蓝图。打开事件图表,添加键盘事件 E,并按下时调用 Interact 节点。编译蓝图并将它拖入关卡,运行游戏,靠近物体按 E 即可看到效果。
常用开发技巧
蓝图与 C++ 协作模式
| 需求 | 方案 |
|---|---|
| 核心玩法逻辑、复杂计算 | C++ 实现,暴露蓝图接口 |
| 美术表现、简单的输入绑定 | 蓝图实现 |
| 需要多人重载的事件 | BlueprintImplementableEvent(仅蓝图实现)或 BlueprintNativeEvent(C++带默认实现) |
示例:在 C++ 中声明事件,蓝图实现具体效果
// 头文件中
UFUNCTION(BlueprintImplementableEvent, Category = "Interaction")
void OnPickup();
在蓝图中该函数会作为事件节点出现,可在其中播放动画、粒子等。
访问游戏模式与玩家控制器
// 获取游戏模式基类
AGameModeBase* GM = GetWorld()->GetAuthGameMode();
// 获取玩家控制器(可在玩家Pawn内使用)
APlayerController* PC = Cast<APlayerController>(GetController());
内存管理与垃圾回收
UE 使用智能指针系统 TSharedPtr TWeakObjectPtr 以及自动的垃圾回收。凡是用 NewObject<>() 创建的 UObject 都由 GC 管理,只要根上有引用就不会被销毁。记住:任何继承自 UObject 的类不要手动 delete。
调试与日志
UE_LOG(LogTemp, Warning, TEXT("Player health: %f"), Health);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Debug Message"));
使用 LogVerbosity 控制日志级别:Log、Warning、Error。可在 Output Log 中查看或通过 Visual Studio 输出窗口调试。
模块与头文件包含
UE 采用模块化设计。新建类时,确保 Build.cs 文件中包含了所需模块:
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
若使用了 UAudioComponent,需包含 "AudioMixer" 或相关模块。编译出错时检查模块依赖。
总结与下一步
通过本教程,你已了解了 UE C++ 开发的基础框架,包括反射宏、核心游戏类、组件创建、蓝图交互以及基础调试。现在你可以:
- 尝试创建自定义
APawn并重载输入绑定函数SetupPlayerInputComponent。 - 使用
TimerManager实现延迟逻辑。 - 学习
UGameInstanceSubsystem实现跨关卡数据管理。
虚幻引擎的 C++ 文档和源码是最好的老师,保持实践,你将很快构建出强大的游戏功能。