目录 - Table of contents

UEC++游戏架构

UE内游戏结构

游戏模式(GameMode)

游戏模式也就是游戏的基础规则,负责管理游戏规则和流程的核心类。它定义了玩家的行为、游戏的胜利或失败条件、关卡规则以及默认的玩家控制器、HUD 等关键组件。

默认pawn类(Default Pawn Class),可以是有运动属性的character,也可以是不带有运动属性的pawn。这个可以理解成人体的躯干,它可以被controller控制,而controller可以是玩家,也可以是我们的AI controller。

HUD类(用户界面类),绘制到屏幕上的UI,用来进行一些交互界面的展示。

玩家控制器类(Player Controller Class),控制器是一个非物理的Actor,可以是一个pawn或者是一个pawn的派生类,可以控制角色的移动。

游戏状态类(Game State Class),主要是追踪记录游戏层面的属性,比如说已经连接玩家的列表,团队的一些得分,开放世界中完成的任务等等。

玩家状态类(Player State Class),追踪玩家的状态属性,比如当前玩家的姓名,得分,在线状态等等。

旁观者类(Spectator Class),描述第三方的视角。

创建C++类

这里用一个和之前创建C++类略有不同的方法,但殊途同归。

首先使用ctrl + 空格召唤出内容菜单,在C++类文件夹中右击可以选择新建C++类,这里展示一个示例。

创建C++类 选择要创建的C++类 创建C++类

创建成功会在living coding界面显示

创建C++类成功

这时VS会侦测到发生修改,这里点击全部重新加载来加载更新的项目

加载更新

然后在MyGameMode.h里将刚刚创建的头文件都加载在GameMode里,要构造一个默认的构造函数,并且在cpp里实现:

MyGameMode.h

#pragma once

#include "CoreMinimal.h"
#include "MyGameState.h"
#include "MyHUD.h"
#include "MyPawn.h"
#include "MyPlayerController.h"
#include "MyPlayerState.h"
#include "GameFramework/GameMode.h"
#include "MyGameMode.generated.h"

UCLASS()
class PROJECT4LEARNING_API AMyGameMode : public AGameMode
{
	GENERATED_BODY()
	AMyGameMode();
};

MyGameMode.cpp

#include "MyGameMode.h"
AMyGameMode::AMyGameMode()
{
	DefaultPawnClass = AMyPawn::StaticClass();
	PlayerControllerClass = AMyPlayerController::StaticClass();
	GameStateClass = AMyGameState::StaticClass();
	PlayerStateClass = AMyPlayerState::StaticClass();
	HUDClass = AMyHUD::StaticClass();
}

然后重新编译生成一下,这里要先将Editor关闭,否则会失败,然后再打开Ediotr就可以选到我们刚刚写好的内容:

加载成功

重写BeginPlay、Tick、EndPlay函数

MyGameMode.h

#pragma once

#include "CoreMinimal.h"
#include "MyGameState.h"
#include "MyHUD.h"
#include "MyPawn.h"
#include "MyPlayerController.h"
#include "MyPlayerState.h"
#include "GameFramework/GameMode.h"
#include "MyGameMode.generated.h"

UCLASS()
class PROJECT4LEARNING_API AMyGameMode : public AGameMode
{
	GENERATED_BODY()
	AMyGameMode();
	
public:
	virtual void BeginPlay() override;
	virtual void Tick(float DeltaTime) override;
	virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
};

BeginPlay()在游戏运行的一开始执行。

Tick()函数在每一帧都会被调用,DeltaTime表示当前帧与上一帧的时间间隔(以秒为单位)。

EndPlay()在游戏结束或关卡切换时调用,用于清理资源。EndPlayReason 参数是一个枚举,表示调用 EndPlay() 的原因。

常见枚举值:

  • EEndPlayReason::Destroyed:对象被销毁时调用。
  • EEndPlayReason::LevelTransition:关卡切换时调用。
  • EEndPlayReason::EndPlayInEditor:在编辑器中停止游戏时调用。
  • EEndPlayReason::Quit:玩家退出游戏时调用。

virtual:表示该函数是一个虚函数,可以被子类重写。

override:表示该函数是对基类的重写,编译器会检查基类是否有匹配的虚函数,检查函数签名是否匹配。

MyGameMode.cpp

#include "MyGameMode.h"
AMyGameMode::AMyGameMode()
{
	DefaultPawnClass = AMyPawn::StaticClass();
	PlayerControllerClass = AMyPlayerController::StaticClass();
	GameStateClass = AMyGameState::StaticClass();
	PlayerStateClass = AMyPlayerState::StaticClass();
	HUDClass = AMyHUD::StaticClass();
}

void AMyGameMode::BeginPlay()
{
	Super::BeginPlay();
}

void AMyGameMode::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

void AMyGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Super::EndPlay(EndPlayReason);
}

在重写基类函数时,通常需要调用Super::Function(),可以在扩展或修改行为时保留基类的默认行为。这在处理复杂的游戏逻辑时尤其重要,因为基类可能包含关键的初始化或更新逻辑,避免破坏引擎默认行为。

UE_LOG和AddOnScreenDebugMessage

void AMyGameMode::BeginPlay()
{
	Super::BeginPlay();

	// LogTemp临时日志记录类别名称,Warning日志记录的级别,TEXT是日志打印的文本内容
	UE_LOG(LogTemp, Error, TEXT("My name is ok"));
	UE_LOG(LogTemp, Warning, TEXT("My name is ok"));
	UE_LOG(LogTemp, Display, TEXT("My name is ok"));
}

日志记录级别常用的有三种,级别由高到低:Error > Warning > Display,每一个打印的颜色也不一样,依次分别是红色、黄色、白色。TEXT()宏将字符串转换为 UE4/UE5 支持的宽字符格式(FString或FText)。

运行后打开输出日志可以看到:

输出日志

另外一种打印是打印到屏幕上的,相当于蓝图中的Print String:

void AMyGameMode::BeginPlay()
{
	Super::BeginPlay();

	// 打印到屏幕上
	// -1是默认值,5.0是打印到屏幕上的时间,这里5秒后会消失,FColor是打印显示的颜色,TEXT是日志打印的文本内容
	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("My name is ok"));
}

这里的第一个Key是消息的唯一标识符,-1表示自动生成一个唯一的Key,每次调用都会显示一条新消息。如果使用相同的Key,则会覆盖先前具有相同Key的消息。例如,如果使用0,则会重复更新同一条消息内容,而不会新增消息。