GameState 是 UE 网络框架中一个至关重要且强大的类,理解它是构建稳健多人游戏的关键。
1. 什么是 GameState?
GameState(游戏状态)是一个在服务器和所有客户端上都存在的 Actor。它的核心职责是存储和同步整个游戏层面的状态信息。
可以把它想象成游戏的“公告板”或“数据中心”,所有玩家都需要知道的全局信息都存放在这里。
- 服务器 拥有权威的
GameState,负责更新其上的数据。 - 客户端 拥有
GameState的副本,服务器会通过网络自动将相关属性同步给它们,让所有玩家对游戏大局有一致的认知。
2. GameState 的核心特性
- 网络同步:
GameState及其内部的PlayerState会自动从服务器复制到所有客户端。 - 全局存在: 在游戏会话的整个生命周期中都存在。
- 权威性: 只有服务器可以修改
GameState的核心数据。客户端上的GameState是只读的副本。 - 存储全局信息: 它应该包含那些不属于某个特定玩家,而是属于整局游戏的信息。
3. GameState 与其它关键类的区别
理解 GameState 的最好方式就是把它和其他核心类进行对比。
| 类名 | 权威者 | 存在位置 | 主要职责 |
|---|---|---|---|
GameState | 服务器 | 服务器和所有客户端 | 存储和同步全局游戏状态(如剩余时间、当前比分、游戏阶段)。 |
PlayerState | 服务器 | 服务器和所有客户端 | 存储和同步玩家的公开状态(如玩家姓名、得分、杀敌/死亡数)。一个 PlayerState 对应一个玩家。 |
GameMode | 服务器 | 仅服务器 | 定义游戏规则、处理游戏流程(如如何获胜、生成玩家)。包含敏感逻辑和数据。 |
PlayerController | 各自客户端 | 服务器和所属客户端 | 代表玩家的人机接口。处理输入、管理UI。服务器有所有 PlayerController,但只能控制自己的。 |
Pawn / Character | 各自客户端 | 服务器和所有客户端 | 玩家或AI在游戏世界中的物理实体。其位置、动画等会被同步。 |
简单比喻:
GameMode是比赛的裁判长(只在后台工作,制定规则)。GameState是体育场里的记分牌和大屏幕(向所有观众显示比分、剩余时间)。PlayerState是记分牌上显示的某个球员的个人数据(如进球数、犯规次数)。PlayerController是球员的大脑(决定何时跑动、何时射门)。Pawn是球员的身体(在场上执行动作)。
4. GameState 中应该放什么?
以下是一些非常适合放在 GameState 中的数据的例子:
- 游戏计时器: 整局游戏的剩余时间、已经进行的时间。
- 团队分数: 在多团队游戏中,各个团队的当前得分。
- 游戏阶段: 例如
WaitingToStart,InProgress,PostGame等。 - 获胜者和结果: 游戏结束时,哪个团队或玩家获胜。
- 全局世界状态: 比如一个“大逃杀”游戏中的安全区位置和缩小时间。
- 玩家列表的引用:
GameState持有一个所有PlayerState的数组,方便客户端查询其他玩家的信息。
5. 如何在 C++ 中实现一个 GameState
让我们通过一个“夺旗”游戏的例子来演示。
MyGameState.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameStateBase.h"
#include "Net/UnrealNetwork.h" // 重要:用于网络复制
#include "MyGameState.generated.h"
UCLASS()
class MYGAME_API AMyGameState : public AGameStateBase
{
GENERATED_BODY()
public:
AMyGameState();
// 团队分数的复制属性
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Game State")
int32 TeamAScore;
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Game State")
int32 TeamBScore;
// 游戏剩余时间的复制属性
UPROPERTY(ReplicatedUsing = OnRep_MatchTime, BlueprintReadOnly, Category = "Game State")
float MatchRemainingTime;
// 游戏阶段的枚举
UENUM(BlueprintType)
enum class EGamePhase : uint8
{
WaitingForPlayers,
InProgress,
RoundOver,
GameOver
};
// 游戏阶段的复制属性
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Game State")
EGamePhase CurrentGamePhase;
// 服务器端增加分数的方法
void AddScoreToTeamA(int32 Amount);
void AddScoreToTeamB(int32 Amount);
// 服务器端设置时间的方法
void SetMatchTime(float NewTime);
protected:
// 网络复制所需的函数
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
// 当 MatchRemainingTime 在客户端上更新时调用的函数
UFUNCTION()
void OnRep_MatchTime();
};
MyGameState.cpp
#include "MyGameState.h"
AMyGameState::AMyGameState()
{
// 设置初始值
TeamAScore = 0;
TeamBScore = 0;
MatchRemainingTime = 600.0f; // 10分钟
CurrentGamePhase = EGamePhase::WaitingForPlayers;
// 设置此 Actor 每帧复制一次,对于频繁变化的数据(如时间)很重要。
NetUpdateFrequency = 100.0f;
}
void AMyGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
// 告诉引擎哪些属性需要复制
DOREPLIFETIME(AMyGameState, TeamAScore);
DOREPLIFETIME(AMyGameState, TeamBScore);
DOREPLIFETIME(AMyGameState, MatchRemainingTime);
DOREPLIFETIME(AMyGameState, CurrentGamePhase);
}
void AMyGameState::AddScoreToTeamA(int32 Amount)
{
// 确保这个逻辑只在服务器上运行
if (HasAuthority())
{
TeamAScore += Amount;
// 由于 TeamAScore 是 Replicated,变化会自动同步到所有客户端
// 你可以在这里添加一些额外的逻辑,比如检查是否获胜
// CheckWinCondition();
}
}
void AMyGameState::AddScoreToTeamB(int32 Amount)
{
if (HasAuthority())
{
TeamBScore += Amount;
}
}
void AMyGameState::SetMatchTime(float NewTime)
{
if (HasAuthority())
{
MatchRemainingTime = NewTime;
}
}
void AMyGameState::OnRep_MatchTime()
{
// 这个函数在客户端上执行,当 MatchRemainingTime 从服务器复制更新后
// 你可以在这里更新客户端的UI,播放时间警告音效等
// 例如: UpdateHUDTimer();
}
6. 如何在蓝图中使用 GameState
在蓝图中获取 GameState 非常方便:
- 使用
Get Game State节点。 - 将其转换为你的自定义 GameState 类(例如
MyGameState)。
之后,你就可以安全地访问所有标记为 BlueprintReadOnly 的变量,如 TeamAScore, MatchRemainingTime 等,并在UI(如UMG)中绑定它们。
示例蓝图流程:
- 服务器(GameMode 中): 在
BeginPlay时,获取GameState并调用SetMatchTime。 - 服务器(当玩家夺旗时): 获取
GameState并调用AddScoreToTeamA。 - 所有客户端(在UMG Widget中): 在
Construct事件中,获取GameState,并将其TeamAScore变量绑定到一个文本控件上。当分数从服务器复制过来时,UI会自动更新。
总结
GameState 是 UE 多人游戏的粘合剂,它确保了所有客户端对游戏全局状态的一致性。设计时应遵循以下原则:
- 由服务器驱动: 所有对
GameState的修改都必须源自服务器。 - 只读客户端: 客户端只能读取其
GameState副本的数据,用于显示和本地逻辑。 - 职责分离: 不要把属于
PlayerState(个人数据)或GameMode(游戏规则)的东西放在GameState里。
熟练掌握 GameState 将极大地帮助你构建清晰、稳定、可维护的多人游戏体验。
1006

被折叠的 条评论
为什么被折叠?



