UE5 & C++ 开发游戏 Roll A Ball
0 前言
使用驼峰命名法。
设置英文界面 culture=en
。
点击虚幻编辑器右下角的编译按钮 可以实时编译被修改的源文件。
可以代替下文中 按 F5 编译打开 UE5 编辑器 的步骤。
源代码目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.../Unreal Projects/RollABall/Source
│ RollABall.Target.cs
│ RollABallEditor.Target.cs
│
└─RollABall
│ RollABall.Build.cs
│ RollABall.cpp
│ RollABall.h
│
├─Game
│ RollABallGameModeBase.cpp
│ RollABallGameModeBase.h
│ RollABallPlayer.cpp
│ RollABallPlayer.h
│
├─Items
│ RollABallItemBase.cpp
│ RollABallItemBase.h
│
└─UHD
RollABallWidget.cpp
RollABallWidget.h
- RollABall.Build.cs
- RollABallGameModeBase.cpp
- RollABallGameModeBase.h
- RollABallPlayer.cpp
- RollABallPlayer.h
- RollABallItemBase.cpp
- RollABallItemBase.h
- RollABallWidget.cpp
- RollABallWidget.h
1 创建项目
- 游戏 - 空白
- 项目默认设置:蓝图
- 项目名称:RollABall
- 点击创建
在创建项目时应该选择蓝图而非 C++ 。因为 C++ 项目会默认生成一个 game mode 和初始 sln 工程,不利于项目文件的管理。
Editor Preference 设置(Edit - Editor Preferences…):
- General - Appearance
- User Interface - Asset Editor Open Location: Main Window 资产编辑窗口以选项卡的形式在主窗口打开
- General - Source Code
- Accessor - Source Code Editor: Visual Studio Code
1 创建 Player Class
虽然 Character Class 提供了很多实用的功能,可以更方便地创建玩家。但是为了学习更底层的知识,此处选择创建 Pawn Class 。
- Tools - New C++ Class…
- Add C++ Class 窗口 - 选择 Pawn - Next
- Name: RollABallPlayer
- Path:
/Unreal Projects/RollABall/Source/RollABall/Game
(将与游戏有关的类放在 Game 文件夹下) - 点击 Create Class
下面的提示框选择 OK
1
Project now includes sources, please close the editor and build from your IDE.
下面的提示框选择 No
1 2 3
Successfully added class 'RollABallPlayer', however you must recompile the 'RollABall' module before it will appear in the Content Browser. Would you like to edit the code now?
- 关闭 UE5 编辑器
- 在项目路径
/Unreal Projects/RollABall
下打开 code-workspace 文件 - 将
RollABallPlayer.cpp
中的#include "Game/RollABallPlayer.h"
改为#include "RollABallPlayer.h"
以解决路径错误 - 在运行和调试(Ctrl + Shift + D)中选择
Launch RollABallEditor (Development)
- 以后通过 F5 编译并打开 UE5 编辑器
1.1 编辑 RollABallPlayer.h
声明组件
1
2
3
4
5
6
7
// 声明 Components
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UStaticMeshComponent *Mesh; // 静态网格体
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
USpringArmComponent *SpringArm; // 弹簧臂
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UCameraComponent *Camera; // 摄像机
相当于创建 Components 选项卡中的组件。
所需的头文件
1
2
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
所有的
#include
语句都应该在#include "XXX.generated.h"
之前。
定义变量
1
2
3
4
5
6
7
// 定义变量
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MoveForce = 1000.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float JumpImpulse = 800.0f;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
int32 MaxJumpCount = 1;
相当于在 My Blueprint 选项卡中添加新的变量。
在类内使用的变量
1
2
// 当前跳跃的次数
int32 JumpCount = 0;
1.2 编辑 RollABallPlayer.cpp
设置每帧是否调用 Tick 函数
1
PrimaryActorTick.bCanEverTick = false;
设置 Components 的默认值
1
2
3
4
5
6
7
8
9
// 设置 Components 的默认值;字符串是 Component 的名字
Mesh = CreateDefaultSubobject<UStaticMeshComponent>("Mesh");
SpringArm = CreateDefaultSubobject<USpringArmComponent>("SpringArm");
Camera = CreateDefaultSubobject<UCameraComponent>("Camera");
// 设置 Components 的依赖关系
RootComponent = Mesh;
SpringArm->SetupAttachment(Mesh);
Camera->SetupAttachment(SpringArm);
这里的依赖关系相当于在 Components 选项卡中设置的父子关系。
2 从 Player Class C++ 类创建蓝图
2.1 创建蓝图
- 在 Content 文件夹下创建
_RollABall/Blueprints
文件夹 - 右键
C++ Classes/RollABall/Game
中的RollABallPlayer
C++ 类 - Create Blueprint class based on RollABallPlayer - Name: BP_RollABallPlayer
- Path 选择
_RollABall/Blueprints
- 点击 Create Blueprint Class
2.2 设置蓝图
- 选中 Mesh 组件 - Details 选项卡 - Static Mesh - Static Mesh - 下拉菜单 - 点击齿轮图标 - 勾选 Show Engine Content - 选择 100x100x100 的 Sphere
- 选中 SpringArm 组件 - Details 选项卡 -
- Camera - Target Arm Length: 1000
- Transform - Rotation: 0, -45, 0
- 编译保存
3 绑定输入
3.1 修改编译选项
- 打开
Source/RollABall
中的RollABall.Build.cs
- 在
PublicDependencyModuleNames
中添加EnhancedInput
1
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
3.2 声明输入映射上下文和输入动作
在
RollABallPlayer.h
中添加头文件1 2
#include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h"
声明变量
1 2 3 4 5 6 7 8 9
// 声明角色的输入映射上下文和输入动作 UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) UInputMappingContext *DefaultIMC; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) UInputAction *JumpAction; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) UInputAction *MoveForwardAction; UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true")) UInputAction *MoveRightAction;
- 按 F5 编译打开 UE5 编辑器
- Tools - Refresh Visual Studio Project
声明输入动作触发的回调函数
1 2 3 4
// 声明输入触发回调函数 void MoveForward(const FInputActionValue &Value); void MoveRight(const FInputActionValue &Value); void Jump();
3.3 绑定动作
修改 RollABallPlayer.cpp
中的 ARollABallPlayer::BeginPlay
函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
void ARollABallPlayer::BeginPlay()
{
Super::BeginPlay();
// 在启动时添加 Input Mapping Context 文件
if (APlayerController *PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem *Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultIMC, 0);
}
}
}
修改 RollABallPlayer.cpp
中的 ARollABallPlayer::SetupPlayerInputComponent
函数:
1
2
3
4
5
6
7
8
9
10
11
12
void ARollABallPlayer::SetupPlayerInputComponent(UInputComponent *PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// 绑定输入动作
if (UEnhancedInputComponent *EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
{
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ARollABallPlayer::Jump);
EnhancedInputComponent->BindAction(MoveForwardAction, ETriggerEvent::Triggered, this, &ARollABallPlayer::MoveForward);
EnhancedInputComponent->BindAction(MoveRightAction, ETriggerEvent::Triggered, this, &ARollABallPlayer::MoveRight);
}
}
定义输入动作的回调函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void ARollABallPlayer::Jump()
{
// 给网格体施加一个向上的冲量
Mesh->AddImpulse(FVector(0, 0, JumpImpulse));
}
void ARollABallPlayer::MoveForward(const FInputActionValue &Value)
{
float AxisValue = Value.Get<float>();
const FVector Forward = Camera->GetForwardVector() * MoveForce * AxisValue;
// 给网格体施加一个向前的力
Mesh->AddForce(Forward);
}
void ARollABallPlayer::MoveRight(const FInputActionValue &Value)
{
float AxisValue = Value.Get<float>();
const FVector Right = Camera->GetRightVector() * MoveForce * AxisValue;
// 给网格体施加一个向右的力
Mesh->AddForce(Right);
}
3.4 创建输入映射上下文和输入动作
参考设置按键映射在 _RollABall/Input
文件夹下创建 IMC 文件,在 _RollABall/Input/Actions
文件夹下创建 IA 文件。
IA_Jump
Value Type: Digital (bool)IA_MoveForward
Value Type: Axis 1D (float)IA_MoveRight
Value Type: Axis 1D (float)IMC_Default
MappingsIA_Jump
- Space Bar
- Gamepad Face Button Bottom
IA_MoveForward
- W
- S - Modifiers: Negative
- Gamepad Left Thumbstick Y-Axis
IA_MoveRight
- D
- A - Modifiers: Negative
- Gamepad Left Thumbstick X-Axis
3.5 在蓝图类中设置 IMC 和 IA 文件
- 打开
BP_RollABallPlayer
蓝图 - 分别搜索
DefaultIMC
、JumpAction
、MoveForwardAction
、MoveRightAction
(在RollABallPlayer.h
中声明的变量),将其设置为对应的 IMC 和 IA 文件 - Physics - Mesh -
- Simulate Physics: true
- Mass (kg): 10
- Camera Settings - Spring Arm - (不让摄像机跟随角色旋转)
- Inherit Pitch: false
- Inherit Yaw: false
- Inherit Roll: false
- 编译保存
- 将
BP_RollABallPlayer
蓝图拖入场景中 - Pawn - Auto Possess Player: Player 0
4 设置默认值
在构造函数
ARollABallPlayer::ARollABallPlayer
中设置默认值1 2
// 设置默认值 Mesh->SetSimulatePhysics(true);
在关卡开始时
ARollABallPlayer::BeginPlay
设置给网格体施加的力1 2 3
// 根据质量设置施加给网格体的力 MoveForce *= Mesh->GetMass(); JumpImpulse *= Mesh->GetMass();
5 限制跳跃次数
在 ARollABallPlayer::Jump
中进行跳跃计数
1
2
3
4
5
6
7
8
9
void ARollABallPlayer::Jump()
{
if (JumpCount >= MaxJumpCount)
return;
// 给网格体施加一个向上的冲量
Mesh->AddImpulse(FVector(0, 0, JumpImpulse));
++JumpCount;
}
在构造函数 ARollABallPlayer::ARollABallPlayer
中设置 Mesh 发生碰撞时的回调函数
1
Mesh->OnComponentHit.AddDynamic(this, &ARollABallPlayer::OnHit);
获取 OnComponentHit
的类型定义:右键 OnComponentHit
- 转到类型定义
1
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_FiveParams( FComponentHitSignature, UPrimitiveComponent, OnComponentHit, UPrimitiveComponent*, HitComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, FVector, NormalImpulse, const FHitResult&, Hit );
按照上述类型签名声明回调函数
1
2
3
// 声明碰撞回调函数
UFUNCTION()
void OnHit(UPrimitiveComponent *HitComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, FVector NormalImpulse, const FHitResult &Hit);
定义回调函数
1
2
3
4
5
6
7
8
9
10
11
12
void ARollABallPlayer::OnHit(UPrimitiveComponent *HitComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, FVector NormalImpulse, const FHitResult &Hit)
{
float HitDirection = Hit.Normal.Z; // 获取碰撞法线的 Z 分量
if (HitDirection > 0.0f)
{
// 碰撞法线的 Z 分量大于 0 ,说明碰撞平面在物体的下方
// 碰撞法线的 Z 分量小于 0 ,说明碰撞平面在物体的上方
// 碰撞法线的 Z 分量最大为 1 ,说明碰撞平面在物体的正下方
JumpCount = 0;
}
}
- 打开
BP_RollABallPlayer
蓝图 - Collision - Mesh - Simulation Generates Hit Events: true
- 编译保存
6 输出 DEBUG 信息的方法
1
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, FString::Printf(TEXT("HitDirection: %f"), HitDirection));
7 创建 Item Class
- Tools - New C++ Class…
- Add C++ Class 窗口 - 选择 Actor - Next
- Name: RollABallItemBase
- Path:
/Unreal Projects/RollABall/Source/RollABall/Items
(将与物品有关的类放在 Items 文件夹下) - 点击 Create Class
- 忽略编译错误
- 关闭 UE5 编辑器
- 将
RollABallItemBase.cpp
中的#include "Items/RollABallItemBase.h"
改为#include "RollABallItemBase.h"
以解决路径错误 - 按 F5 编译打开 UE5 编辑器
- Tools - Refresh Visual Studio Project
在 RollABallItemBase.h
中声明静态网格体:
1
2
3
// 声明 Components
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
UStaticMeshComponent *Mesh;
7.1 定义并绑定 OnBeginOverlap
在构造函数 ARollABallItemBase::ARollABallItemBase
中绑定回调函数
1
Mesh->OnComponentBeginOverlap.AddDynamic(this, &ARollABallItemBase::OnBeginOverlap);
获取 OnComponentBeginOverlap
的类型定义:右键 OnComponentBeginOverlap
- 转到类型定义
1
DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE_SixParams( FComponentBeginOverlapSignature, UPrimitiveComponent, OnComponentBeginOverlap, UPrimitiveComponent*, OverlappedComponent, AActor*, OtherActor, UPrimitiveComponent*, OtherComp, int32, OtherBodyIndex, bool, bFromSweep, const FHitResult &, SweepResult);
在 RollABallItemBase.h
中声明回调函数
1
2
3
// 声明回调函数
UFUNCTION()
void OnBeginOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult);
在 RollABallItemBase.cpp
中定义回调函数
1
2
3
4
5
6
7
8
9
10
#include "RollABall/Game/RollABallPlayer.h"
void ARollABallItemBase::OnBeginOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
{
if (Cast<ARollABallPlayer>(OtherActor) != nullptr)
{
// Overlap 的 Actor 是 RollABallPlayer 类
Collected(); // 调用蓝图函数
}
}
7.2 定义蓝图函数 Collected
在 RollABallItemBase.h
中声明蓝图函数
1
2
3
// 声明蓝图函数
UFUNCTION(BlueprintNativeEvent)
void Collected();
Collected 函数使用蓝图实现
在 RollABallItemBase.cpp
中定义蓝图函数的 C++ 部分(要添加 _Implementation
后缀)
1
2
3
4
void ARollABallItemBase::Collected_Implementation()
{
// TODO: Do Game Mode Stuff
}
编译后 Implementation 版本的函数不会出现报错。
8 从 Item Class C++ 类创建蓝图
8.1 创建蓝图
- 右键
C++ Classes/RollABall/Items
中的RollABallItemBase
C++ 类 - Create Blueprint class based on RollABallItemBase - Name: BP_ItemBase
- Path 选择
_RollABall/Blueprints
- 点击 Create Blueprint Class
8.2 设置蓝图
- 选中 Mesh 组件 - Details 选项卡 - Collision - Collision Presets: OverlapAll
- 选中 Mesh 组件 - Details 选项卡 - Static Mesh - Static Mesh - 下拉菜单 - 点击齿轮图标 - 勾选 Show Engine Content - 选择 SM_Cube_01
- Components 选项卡 -
+ Add
- Rotating Movement - Details 选项卡 - Rotating Component - Rotation Rate: 100, 0, 100 - My Blueprint 选项卡 - FUNCTIONS - 右侧 Override 下拉菜单 - 选择 Collected
- 编译保存
- 将
BP_ItemBase
蓝图拖入场景中
8.3 通过蓝图实现 Collected 函数
右键 Event Collected
- Add Call to Parent Function
该节点用于调用 C++ 中的 ARollABallItemBase::Collected_Implementation
函数。
9 创建 Game Mode Class
Game Mode 记录当前关卡的状态。在 Roll A Ball 中,Game Mode 记录了关卡中的 Item 总数,以及玩家收集的 Item 数量。
同时,Game Mode 还可以允许设置玩家 Pawn ,详见下文。
- Tools - New C++ Class…
- Add C++ Class 窗口 - 选择 Game Mode Base - Next
- Name: RollABallGameModeBase
- Path:
/Unreal Projects/RollABall/Source/RollABall/Game
(将与游戏有关的类放在 Game 文件夹下) - 点击 Create Class
- 忽略编译错误
- 关闭 UE5 编辑器
- 将
RollABallGameModeBase.cpp
中的#include "Game/RollABallGameModeBase.h"
改为#include "RollABallGameModeBase.h"
以解决路径错误 - 按 F5 编译打开 UE5 编辑器
- Tools - Refresh Visual Studio Project
9.1 编辑 RollABallGameModeBase.h
声明变量和函数:
1
2
3
4
5
6
7
8
9
10
11
12
protected:
int32 ItemsCollected = 0;
int32 ItemsInLevel = 0;
// 在游戏开始时初始化关卡内的 Items 总数:ItemsInLevel
virtual void BeginPlay() override;
// 用于更新 UHD
void UpdateItemText();
public:
// 在 Item 被收集时调用,用于更新 ItemsCollected
void ItemCollected();
9.2 编辑 RollABallGameModeBase.cpp
定义函数:
1
2
3
4
5
6
7
8
9
10
11
void ARollABallGameModeBase::BeginPlay()
{
TArray<AActor *> Items; // 储存 Items 的数组
UGameplayStatics::GetAllActorsOfClass(GetWorld(), ARollABallItemBase::StaticClass(), Items); // 将 Level 中的所有 ARollABallItemBase 类型的 Actor 储存到 Items 数组中
ItemsInLevel = Items.Num(); // 获取 Items 数组的长度
}
void ARollABallGameModeBase::ItemCollected()
{
++ItemsCollected;
}
10 从 Game Mode Class C++ 类创建蓝图
10.1 创建蓝图
- 按 F5 编译打开 UE5 编辑器
- 在
_RollABall/Blueprints
下新建Game
文件夹 - 右键
C++ Classes/RollABall/Game
中的RollABallGameModeBase
C++ 类 - Create Blueprint class based on RollABallGameModeBase - Name: BP_GameModeBase
- Path 选择
_RollABall/Blueprints/Game
- 点击 Create Blueprint Class
- 编译保存
10.2 设置蓝图
- 在
BP_GameModeBase
中 - Components 选项卡 - 选中根节点 (Self) - Details 选项卡 - Classes - Default Pawn Class: BP_RollABallPlayer
- 编译保存
10.3 应用蓝图
- Edit - Project Settings… - Maps & Modes - Default Modes - Default GameMode: BP_GameModeBase
- 关闭 Project Settings 面板
- 删除场景中的
BP_RollABallPlayer
- Place Actors 选项卡 - Basic - 将 Player Start 拖入场景中
- 选中 Player Start - Details 选项卡 - Transform - Location - 设置位置
在 Edit - Project Settings… - Maps & Modes - Default Modes - Selected GameMode - Default Pawn Class 中的设置会与
BP_GameModeBase
蓝图内的 Default Pawn Class 同步修改
11 创建 User Widget Class
- Tools - New C++ Class…
- Add C++ Class 窗口 - 选择 All Classes - 搜索并选中 UserWidget - Next
- Name: RollABallWidget
- Path:
/Unreal Projects/RollABall/Source/RollABall/UHD
- 点击 Create Class
- 忽略编译错误
- 关闭 UE5 编辑器
- 将
RollABallWidget.cpp
中的#include "UHD/RollABallWidget.h"
改为#include "RollABallWidget.h"
以解决路径错误 - 按 F5 编译打开 UE5 编辑器
- Tools - Refresh Visual Studio Project
在 RollABallWidget.h
中声明函数:
1
2
3
UFUNCTION(BlueprintImplementableEvent)
// 用于更新 UHD ;功能通过蓝图实现
void SetItemText(int32 ItemsCollected, int32 ItemsInLevel);
12 从 User Widget Class C++ 类创建蓝图
使用上文的方法(同这个方法和这个方法)创建 Widget 蓝图无法获得可视化编辑界面
因此此处需要使用另一种方法创建继承 C++ 类的 Widget 蓝图
- 在
_RollABall/Blueprints
中右键 - User Interface - Widget Blueprint - 在 Pick Parent Class for New Widget Blueprint 窗口中的 ALL CLASSES 下拉菜单中搜索并选中 RollABallWidget (上一步创建的 C++ 类)
- 点击 Select
- 命名为
WBP_RollABallWidget
- 双击进入编辑界面
- 点击右上角的 Graph 按钮
- My Blueprint 选项卡 - FUNCTIONS - 右侧 Override 下拉菜单 - 选择 Set Item Text
12.1 设计 UHD
- 点击右上角 Designer 按钮
- 将 Palette 选项卡中的 Common - Text 拖入界面中
- Details 选项卡
- Is Variable: true
- 变量名改为
TextCollected
- Content - Text: Items Collected: 0 / 0
- Appearance - Color and Opacity: 黑色
- Appearance - Font - Size: 72
- Appearance - Justification: Center
- Appearance - Margin - Top: 100
12.2 编写蓝图函数 Set Item Text 修改变量 TextCollected
- 点击右上角 Graph 按钮
- 将 My Blueprint 选项卡中的 TextCollected 拖入 Graph 中 - 拖动输出端口 - 添加 SetText (Text) 节点
- 右键空白区域 - 添加 Utilities/String 中的 Append 节点 - 点击 Add pin 添加到 4 个端口
- 如图所示连接节点
- 编译保存
13 完善 Game Mode
方法一:通过蓝图实现(未验证)
在 BP_GameModeBase
的 Event Graph 中:
Event BeginPlay 连接 Create Widget 节点;然后将 Create Widget 节点的返回值连接到 Add to Viewport 节点。
方法二:通过 C++ 实现
修改代码
在 RollABallGameModeBase.h
中声明 Widget 变量:
1
2
3
4
5
6
7
#include "RollABall/UHD/RollABallWidget.h"
// 用于创建 UHD
UPROPERTY(EditAnywhere, Category = "Widgets")
TSubclassOf<class UUserWidget> GameWidgetClass; // 暴露给虚幻编辑器选择 UHD 的接口
UPROPERTY()
URollABallWidget *GameWidget; // UHD 的实例
在 RollABallGameModeBase.cpp
中的 ARollABallGameModeBase::BeginPlay
中添加 Widget:
1
2
3
4
5
6
7
8
9
if (GameWidgetClass)
{
GameWidget = Cast<URollABallWidget>(CreateWidget(GetWorld(), GameWidgetClass)); // 创建 UHD
if (GameWidget)
{
GameWidget->AddToViewport(); // 将 UHD 添加到视口
UpdateItemText(); // 更新 UHD
}
}
定义 ARollABallGameModeBase::UpdateItemText
函数:
1
2
3
4
5
6
7
8
void ARollABallGameModeBase::UpdateItemText()
{
if (GameWidget)
{
// RollABallWidget.h 中的 SetItemText() 函数通过蓝图实现
GameWidget->SetItemText(ItemsCollected, ItemsInLevel); // 更新 UHD 的文本内容
}
}
完善 ARollABallGameModeBase::ItemCollected
函数:
1
2
3
4
5
void ARollABallGameModeBase::ItemCollected()
{
++ItemsCollected; // 计数
UpdateItemText(); // 更新 UHD
}
设置 Game Mode
- 按 F5 编译打开 UE5 编辑器
- 进入
BP_GameModeBase
蓝图 - Widgets - Game Widget Class: WBP_RollABallWidget - 编译保存
14 完善 Item Class
完成 ARollABallItemBase::Collected_Implementation
的定义:
1
2
3
4
5
6
7
8
void ARollABallItemBase::Collected_Implementation()
{
ARollABallGameModeBase *GameMode = Cast<ARollABallGameModeBase>(GetWorld()->GetAuthGameMode()); // 获得当前关卡的 Game Mode
if (GameMode)
{
GameMode->ItemCollected(); // 调用 Game Mode 的 ItemCollected() 函数
}
}