多人游戏复制与预测
复制与预测
每位玩家看到的损伤都是相同的。每辆车都由一端模拟并决定其损伤,其他端接收结果。驾驶端不会等待该结果:它会立即绘制自己的命中,服务器稍后会确认或拒绝这些命中。本页说明由谁决定、命中如何在端之间传递,以及复制了哪些内容。
一个客户端上的撞击会立即在该客户端上造成车身凹陷。服务器随后确认这一凹陷,并且其他每位玩家的屏幕上都会出现相同的凹陷。
车辆适配器上的 Authority 决定由哪一端模拟车辆并判定其命中。两种模型和允许撞击的速度余量,参见核心概念;服务器对每份报告所应用的规则见下文。
在 Owning Client 模型下,驾驶端客户端会立即将每次命中应用到自己的晶格上。它应用的是它将要发送的报告,并按服务器解码的方式解码,因此两端最终得到相同的数值。超过客户端报告预算(Max Reports Per Second)的命中根本不会绘制,而不是先绘制再撤销。
客户端会以最多 Max Reports Per Batch 条为一批,将报告发送给服务器。服务器在应用每份报告之前都会进行检查:修复代数必须一致,撞击点必须位于晶格内部,速度和滑移必须在 Max Plausible Delta V 以及刮擦限制之内。宿主也可以拒绝该报告。被拒绝的报告会以拒绝通知的形式回传给客户端,客户端随即撤销这处凹陷。在 Pending Report Timeout 内既未被服务器确认也未被拒绝的预测会被丢弃。
端如果不再是撞击源(例如车辆现在归另一位玩家所有),会丢弃其尚未发送的预测。此后,新的撞击源的命中将以块的形式到达。
服务器持有真实的晶格,并将其以块的形式发送,正如核心概念所述。以下内容也会被复制:
- 代数:修复的次数。不同代数的块和报告会被忽略。
- 清洗次数:一次清洗会让每一端清除其污垢。污垢本身从不通过网络传输。
- 已分离和摆动的部件:哪些铰接部件已脱落,以及模拟该车辆的一端让哪些部件开始摆动。每一端在收到这一决定时才放开部件,因此所有玩家会在同一时刻看到同一个部件松开。摆动本身仍是每一端自己的物理。
- **玻璃裂纹、可破坏单元的等级以及近期的破坏事件,以及倒下的树木的状态。**关于之后才连接的端会收到什么,参见中途加入的玩家。
- 撞击声音:撞击源播放命中并进行转发。其他每一端播放转发的副本。
轮胎、滚动声音和滚动 FX,以及发动机声音不会被复制。每一端各自运行自己的这些内容。
-
在 Content Browser 中打开 Settings,勾选 Plugin Content。依次打开 Plugins › Crushform Content › Blueprints › Sedan,将
BP_VehicleAdvSedan复制到你的项目中,然后打开该副本。本页以轿车为例,但这些步骤适用于任何带有适配器的车辆。 -
在 Components 面板中选中 CF_Adapter,即 Chaos 车辆适配器(
UCFChaosVehicleAdapterComponent)。它从基础适配器类继承 Authority。 -
在 Details 面板中,将 Authority 设为 Owning Client。
-
Ram Allowance Mps 保持默认值 15;如果你的硬撞击被拒绝,可以提高它。它是服务器在近期峰值之上允许的速度余量。
Crushform Authority Ram Allowance Mps 15.0 m/s Chaos 适配器的 Crushform 分类,其中 Authority 设为 Owning Client。 -
检查 Deform Profile 已设置。留空则使用项目的默认配置文件。
-
打开 Project Settings › Plugins › Crushform › Network。默认值适用于首次测试。下表列出了每项设置的作用。
-
将你所使用的 Game Mode 的 Default Pawn Class 设为你的副本。插件的
BP_VehicleAdvGameMode会生成插件自带的轿车作为 Pawn,因此如果副本不是 Default Pawn Class,它就永远不会作为玩家的车辆生成,其 Owning Client 设置也不会生效。 -
保存蓝图。在 Play 设置中,将 Number of Players 设为 2,Net Mode 设为 Play As Listen Server,然后按 Play。编辑器会打开一个服务器窗口(NetMode: Server 0)和一个客户端窗口(NetMode: Client 1)。在客户端窗口中将车开向墙壁,并在服务器窗口中观察。
蓝图中的一项常见任务是在服务器上修复车辆。Repair 和 Wash 只在拥有权威的端上运行,它们清除的内容会复制到每一端上。图表中通过一个自定义事件调用 Repair,你可以从服务器上调用该事件,例如从你自己的服务器 RPC 中调用。
C++ 中的相同调用:
// The vehicle actor that owns the adapter.AActor* Vehicle = GetOwner();if (UCFDeformComponent* Deform = Vehicle->FindComponentByClass<UCFDeformComponent>()){ if (Vehicle->HasAuthority()) { // Clears every dent and scratch, on every machine. Deform->Repair(); }
// Hits this client sent that the server has not yet confirmed or refused. const int32 Waiting = Deform->GetNumPendingReports(); // How many repairs the body has had. const uint8 Generation = Deform->GetGeneration();}| 属性 | 作用 | 默认值 |
|---|---|---|
UCFVehicleAdapterComponent::Authority | 由哪一端模拟车辆并判定其命中:Server 或 Owning Client。 | Server |
UCFVehicleAdapterComponent::RamAllowanceMps | 配合 Owning Client:上报的命中速度可以比近期峰值速度快多少,单位为米/秒。 | 15 |
UCFProfile::MaxPlausibleDeltaV | 报告中允许携带的最猛烈的撞击,单位为米/秒。滑移同样受此限制。 | 40 |
UCFSettings::MaxReportsPerBatch | 驾驶端客户端每帧在一批中发送的报告数。服务器会拒绝超出的部分。 | 8 |
UCFSettings::MaxReportsPerSecond | 一个组件每秒接受的报告数。客户端的突发量相同;服务器的容量是它的 1.5 倍。 | 60 |
UCFSettings::PendingReportTimeout | 客户端在服务器既未确认也未拒绝时,保留某次预测命中的秒数。 | 10 |
UCFSettings::ScrapeReportInterval | 同一组件两次刮擦报告之间的秒数。 | 0.1 |
UCFSettings::MaxScrapeDt | 一份刮擦报告所能代表的最长滑移,单位为秒。 | 0.25 |
检查是否正常工作
Section titled “检查是否正常工作”运行设置步骤中的双人测试。在编辑器控制台中输入一次 Crushform.Stats。它会为每个正在运行的世界中每辆可形变车辆输出一行。服务器副本那一行显示 server lattice,客户端副本那一行显示 client view。每行的格式如下:
<vehicle>: <server lattice or client view>, generation <n>, <n> pending, <n> chunks | hits <n>, sent <n>, accepted <n>, rejected <n>, published <n>, received <n>, bad <n>, expired <n>, parts off sent <n>, accepted <n>, rejected <n>, collision builds <n>, longest <ms> ms, collision waits <n>, parts held <n>, part hulls <n>, longest <ms> ms, most points <n>在客户端撞击之后,其 pending 计数会上升,并在服务器确认凹陷后回落到 0;服务器的 accepted 计数则会上升。被拒绝的命中会在客户端显示为 rejected 计数。
客户端的凹陷消失了
Section titled “客户端的凹陷消失了”原因。 服务器拒绝了该报告,客户端于是撤销了这处凹陷。当报告的速度或滑移高于 Max Plausible Delta V、撞击点位于晶格之外,或其修复代数已过期时,服务器会拒绝该报告。在 Owning Client 模式下,比近期峰值速度加上 Ram Allowance Mps 更猛烈的命中同样会被拒绝。
修复。 在 Crushform.Stats 中查看 rejected 计数。如果希望客户端判定自己的命中,请将 Authority 设为 Owning Client。如果真实的撞击比 Max Plausible Delta V 更猛烈,请在配置文件上提高它。
客户端的命中完全不产生凹陷
Section titled “客户端的命中完全不产生凹陷”原因。 车辆的 Authority 为 Server。此时客户端不是撞击源,因此它不做任何预测,而是等待服务器的块。
修复。 将 Authority 设为 Owning Client,如设置步骤中所述。
凹陷在几秒后消失
Section titled “凹陷在几秒后消失”原因。 服务器没有在 Pending Report Timeout(默认 10 秒)内确认客户端的报告,客户端于是丢弃了这次预测。
修复。 在 Crushform.Stats 中查找 rejected 和 expired。如果超时时间对你的网络来说太短,请在 Network 下提高 Pending Report Timeout。
