前言
本文翻译自WizardCell的《Unreal Engine Multiplayer Tips and Tricks》。 原文汇总了作者在基于Unreal Engine 默认网络框架的开发实践中总结出的若干坑点和技巧,用作UE网络开发最佳实践参考。
了解游戏框架对象
尽管理解UE游戏框架(Gameplay Framework)和网络通信内容,不一定能让你的代码结构更加清晰,但可以避免重复造轮子、节省大量宝贵的时间。
从多人游戏角度来,游戏框架中的对象——更准确地说是Actors,都具体各不相同的网络行为。一些Actor与所有客户端有关,而另一些Actor则只与它所属的客户端有关,
还有其他Actor可能不与任何客户端有关。单人游戏通常不用关心这些差异,但对于多人游戏来说至关重要。不仅只有
相关性(Relevancy)上的差异,还有其他网络复制属性的差异。不过,如果Actor不开启复制,则相关性及其他大
部分网络复制的设置将是无效的。
下表罗列出常见的可复制Actor部分网络复制属性的默认值:
| Actor | Only Relevant To Owner? | Always Relevant | Replicate Movement? | Net Update Frequency | Net Priority |
|---|---|---|---|---|---|
| GameState | ✔ | 10.0 | 1.0 | ||
| PlayerState | ✔ | 1.0 | 1.0 | ||
| PlayerController | ✔ | 100.0 | 3.0 | ||
| Pawn | ✔ | 100.0 | 3.0 | ||
| Actor | 100.0 | 1.0 |
上表列出的网络复制属性的含义:
bOnlyRelevantToOwner:如果为true,则Actor与拥有它的客户端有关。如果在运行过程中改变此属性,所有非拥有者通道(即模拟代理,Simulated Proxies)需要显式地关闭。bAlawysRelevant:与所有客户端相关(覆盖bOnlyRelevantToOwner)需要显式地关闭。bReplicateMovement:如果为true,将复制移动/位置相关属性。NetUpdateFrequency:Actor进行网络复制的频率,用于计算NextUpdateTime。NetPriority:在低带宽和饱和状态下,Actor的复制优先级。更高的数值代表更优先进行复制。
更深入的内容可参考官方文档 Detailed Actor Replication Flow
注意GetPlayerXXX(0)静态函数
在线多人游戏玩法中,很多人会用下面这些静态函数:

它们对应的原生函数为:
UGameplayStatic::GetPlayerController(const UObject* WorldContextObject, int32 PlayerIndex).UGameplayStatic::GetPlayerCharacter(const UObject* WorldContextObject, int32 PlayerIndex).UGameplayStatic::GetPlayerPawn(const UObject* WorldContextObject, int32 PlayerIndex).UGameplayStatic::GetPlayerState(const UObject* WorldContextObject, int32 PlayerIndex).UGameplayStatic::GetPlayerCameraManager(const UObject* WorldContextObject, int32 PlayerIndex).
使用这些函数存在潜在的风险,除非你足够了解这些函数,或者只在本地多人游戏(分屏)中使用。
例如:调用蓝图函数GetPlayerController(0)或者原生函数UGameplayStatics::GetPlayerController(GetWorld(), 0),
不同模式下返回结果不同:
- Listen-server:主机(Host)的
PlayerController. - Dedicated-server:第一个客户端的
PlayerController. - Client: 本地客户端的
PlayerController.
大部分初学者并不清楚这些函数在不同模式下的行为差异。此外,几乎不需要通过索引获取Actor,因为客户端与服务端之间(包括客户端与客户端之间)Actor的索引
并不一一对应。在关卡传输时,PlayerController迭代器的遍历顺序也并不一致,所以我们最好不要用索引取PlayerController。当开发者传入索引
0时,99%的情况是想获取本地主PlayerController(Primary local PlayerController),但在Listen-server模式下,如果一个客户端的连接速度足够快,在Host主机上其索引会为0。这显然会脱离
我们期望取得Host主机的PlayerController的预期。因此,应该使用UGameInstance::GetFirstLocalPlayerController()函数获取我们期望的本地主
PlayerController。不过,这个函数没有暴露给蓝图,我们应该使用下图展示的蓝图节点。
我建议使用GetPlayerXXX()之类的函数要慎重,尽量使用安全、确定的方式可以获取Actor。
根据不同情况(取决你期望从哪些类型上获取Actor),可用的替代函数/属性有所不同,但以下是最实用的:

它们对应的原生函数为:
AActor::GetOwner()UActorComponent::GetOwner()APawn::GetController()AController::GetPawn()APlayerState::GetPlayerController()UUserWidget::GetOwningPlayer()AHUD::GetOwningPlayerController()APlayerCameraManager::GetOwningPlayerController()APlayerState::GetPawn()AController::GetPlayerState()APlayerController.PlayerCameraManager

Discussion
留言讨论
发表留言时,评论服务会记录必要的 IP 与 User-Agent 信息,用于安全审核和防止滥用。