WECHAT

扫码添加微信

YoungFan 微信二维码
← 返回 UE5 笔记
Unreal Engine Multiplayer Tips and Tricks (持续施工中) 封面
技术研究研究中

Unreal Engine Multiplayer Tips and Tricks (持续施工中)

翻译自 WizardCell的《Unreal Engine Multiplayer Tips and Tricks》

UE5C++Multiplayer

前言

本文翻译自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 信息,用于安全审核和防止滥用。