UEC++游戏架构
游戏模式(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++类,这里展示一个示例。
创建成功会在living coding界面显示
这时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
,则会重复更新同一条消息内容,而不会新增消息。