Unreal Engine C++ 编程基础

FreeGuideOnline 最新 2026-07-12

Unreal Engine C++ 编程基础

本教程将引导你从零开始,掌握使用 C++ 进行虚幻引擎开发的核心知识。我们将避开冗长的理论,直接通过实用的示例、清晰的解释,让你快速上手 UE5 C++ 编程。

环境准备

在编写第一行代码之前,请确保开发环境配置正确。

安装必备软件

  1. Epic Games Launcher:从虚幻引擎官网下载并安装启动器。
  2. Unreal Engine 5:在启动器中安装推荐版本的引擎(建议5.3或更高)。
  3. Visual Studio 2022:下载安装社区版,必须勾选“使用 C++ 的游戏开发”工作负载,以及“Unreal Engine 安装程序”组件。
  4. Windows SDK / NET Core 3.1:通常随 VS2022 工作负载自动安装,若编译出错可手动补全。

验证编译环境

启动 UE5,创建空白 C++ 项目,若能自动打开 VS 并成功编译,则环境就绪。如遇到“找不到 XXX.h”错误,检查引擎安装路径是否包含空格或中文,并确认为项目安装了正确的模块依赖。

创建你的第一个 C++ 项目

  1. 打开 UE5 启动器,点击 游戏空白 模板。
  2. 项目设置中:
    • 蓝图 改为 C++
    • 选择 无入门内容
    • 项目名称用英文,避免路径包含中文或空格
  3. 点击创建,引擎会自动生成 .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 控制日志级别:LogWarningError。可在 Output Log 中查看或通过 Visual Studio 输出窗口调试。

模块与头文件包含

UE 采用模块化设计。新建类时,确保 Build.cs 文件中包含了所需模块:

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });

若使用了 UAudioComponent,需包含 "AudioMixer" 或相关模块。编译出错时检查模块依赖。

总结与下一步

通过本教程,你已了解了 UE C++ 开发的基础框架,包括反射宏、核心游戏类、组件创建、蓝图交互以及基础调试。现在你可以:

  • 尝试创建自定义 APawn 并重载输入绑定函数 SetupPlayerInputComponent
  • 使用 TimerManager 实现延迟逻辑。
  • 学习 UGameInstanceSubsystem 实现跨关卡数据管理。

虚幻引擎的 C++ 文档和源码是最好的老师,保持实践,你将很快构建出强大的游戏功能。