当前位置: 首页 > news >正文

CMC一帧的流程

UE5.7 CMC PhysWalking:从键盘输入到一帧移动与网络同步

生成时间:2026-05-19
仓库:E:/UE5.7
分析范围:UCharacterMovementComponent 传统游戏线程路径,以 MOVE_Walking / PhysWalking 为主,覆盖本地预测、服务器重放校验、客户端纠错重放、模拟代理平滑。

核心结论

  1. AddMovementInput 本身不移动角色,只把输入累积到 APawn::ControlInputVector;真正消费发生在 UCharacterMovementComponent::TickComponent 开头。
  2. 本地玩家在客户端是 ROLE_AutonomousProxy:同一帧先本地预测 PerformMovement,再把 FSavedMove_Character 通过 ServerMove/ServerMovePacked 发给服务器。
  3. 服务器不会直接采用客户端最终位置,而是用客户端上报的时间戳、压缩 flags、Acceleration 重新执行 MoveAutonomous -> PerformMovement -> PhysWalking,再比较误差并 ack 或 correction。
  4. 客户端收到 correction 后会先把 capsule 修到服务器状态,再在下一次 CMC tick 开头重放未确认的 saved moves,因此玩家看到的是“预测即时 + 必要时橡皮筋”。
  5. 其他客户端上的该角色通常是 ROLE_SimulatedProxy,不跑输入预测,依赖 replicated movement、SimulateMovement 和 mesh 平滑。

一帧主时间线

T0:键盘输入进入玩家代码

Enhanced Input 或传统输入系统最终通常会调用项目里的移动函数,例如:

AddMovementInput(Direction, AxisValue);

引擎侧入口是:

  • APawn::AddMovementInputEngine/Source/Runtime/Engine/Private/Pawn.cpp:799
  • UPawnMovementComponent::AddInputVectorEngine/Source/Runtime/Engine/Private/Components/PawnMovementComponent.cpp:64
  • APawn::Internal_AddMovementInputEngine/Source/Runtime/Engine/Private/Pawn.cpp:838

关键行为:

ControlInputVector += WorldAccel;

所以本阶段只是累积输入,不进行碰撞、不改位置、不发 RPC。

T1:CMC Tick 消费输入

UCharacterMovementComponent::TickComponent 是角色移动组件每帧主入口:

  • TickComponentEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:1622
  • 非 async 路径消费输入:CharacterMovementComponent.cpp:1634
  • APawn::Internal_ConsumeMovementInputVectorEngine/Source/Runtime/Engine/Private/Pawn.cpp:846

消费时:

LastControlInputVector = ControlInputVector;
ControlInputVector = FVector::ZeroVector;

调试时如果在 Tick 后看 ControlInputVector,它通常已经被清零;应看 LastControlInputVector 或后续 CMC 的 Acceleration

T2:根据网络角色选择移动分支

TickComponent 中的关键角色分支:

  • 非模拟代理分支:CharacterMovementComponent.cpp:1703
  • 自主客户端先处理服务器纠错:CharacterMovementComponent.cpp:1708:1714
  • 执行本地受控移动:CharacterMovementComponent.cpp:1729
  • 模拟代理分支:CharacterMovementComponent.cpp:1756:1765

简化逻辑:

LocalRole > SimulatedProxy:if AutonomousProxy client and has correction:ClientUpdatePositionAfterServerUpdate()if locally controlled:ControlledCharacterMove(InputVector, DeltaTime)else if server owns remote autonomous proxy:ServerAutonomousProxyTick / based movementLocalRole == SimulatedProxy:SimulatedTick(DeltaTime)

输入如何变成 Acceleration

入口:

  • UCharacterMovementComponent::ControlledCharacterMoveEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:6340

关键步骤:

  1. CharacterOwner->CheckJumpInput(DeltaSeconds):先处理跳跃,降低输入延迟。
  2. Acceleration = ScaleInputAcceleration(ConstrainInputAcceleration(InputVector)):把输入向量约束并缩放为移动加速度。
  3. AnalogInputModifier = ComputeAnalogInputModifier():计算模拟输入强度。
  4. 按角色分支:
    • Authority:直接 PerformMovement(DeltaSeconds),见 CharacterMovementComponent.cpp:6354:6356
    • AutonomousProxy 客户端:进入 ReplicateMoveToServer(DeltaSeconds, Acceleration),见 CharacterMovementComponent.cpp:6358:6360

PerformMovement 主流程

入口:

  • UCharacterMovementComponent::PerformMovementEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:2703

主职责不是某一种移动模式,而是统一的一帧移动管线:

  1. 校验组件有效性、移动模式、物理模拟状态。
  2. MaybeUpdateBasedMovement:处理站在移动平台等 base movement。
  3. 清理/准备 Root Motion Source。
  4. 保存旧速度/旧位置。
  5. ApplyAccumulatedForcesHandlePendingLaunch
  6. UpdateCharacterStateBeforeMovement
  7. Root Motion 转速度或覆盖速度。
  8. CharacterOwner->ClearJumpInput
  9. UpdateVelocityBeforeMovement
  10. StartNewPhysics(DeltaSeconds, 0):按移动模式进入具体物理函数,见 CharacterMovementComponent.cpp:2959
  11. UpdateCharacterStateAfterMovementCharacterMovementComponent.cpp:2967
  12. PhysicsRotationCharacterMovementComponent.cpp:2971
  13. OnMovementUpdatedCharacterMovementComponent.cpp:3028
  14. MaybeSaveBaseLocation / SaveBaseLocationCharacterMovementComponent.cpp:3040
  15. UpdateComponentVelocityCharacterMovementComponent.cpp:3042
  16. Authority 端若移动导致 adaptive replication 被节流,可能取消节流。
  17. 更新 LastUpdateLocationLastUpdateRotationLastUpdateVelocity

移动模式分发入口:

  • UCharacterMovementComponent::StartNewPhysicsEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:3454

MOVE_Walking 最终进入:

  • UCharacterMovementComponent::PhysWalkingEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:5554

PhysWalking 内部流程

PhysWalking 是 Walking 模式下真正做地面移动、斜坡、台阶、Floor 检测的核心。

1. 入口保护

锚点:CharacterMovementComponent.cpp:5554

主要保护:

  • deltaTime < MIN_TICK_TIME 直接返回。
  • 没有 controller 且不允许无 controller 跑物理、无 root motion、非 simulated proxy 时,清零 AccelerationVelocity 返回。
  • UpdatedComponent 不支持 query collision 时维持 Walking 并返回。

2. 子步循环

锚点:CharacterMovementComponent.cpp:5586

Walking 并非必然只算一次,会按 remainingTimeMaxSimulationIterations 做子步:

while remainingTime >= MIN_TICK_TIME and Iterations < MaxSimulationIterations:timeTick = GetSimulationTimeStep(remainingTime, Iterations)remainingTime -= timeTick

每个子步会保存:

  • OldBase
  • PreviousBaseLocation
  • OldLocation
  • OldFloor
  • OldVelocity

这些变量是调试台阶、斜坡、离地和回滚时的关键。

3. 水平速度和加速度投影

锚点:

  • MaintainHorizontalGroundVelocity 调用:CharacterMovementComponent.cpp:5609
  • Acceleration = FVector::VectorPlaneProject(...)CharacterMovementComponent.cpp:5611
  • 函数定义:CharacterMovementComponent.cpp:5536

UE5.7 支持任意重力方向,因此“水平”不是简单清 Z,而是投影到垂直于 GravityDirection 的平面。

4. 速度积分

锚点:

  • CalcVelocity 调用:CharacterMovementComponent.cpp:5617
  • CalcVelocity 定义:CharacterMovementComponent.cpp:3786
  • ApplyRootMotionToVelocity 调用:CharacterMovementComponent.cpp:5621

主逻辑:

若没有动画 root motion、没有 override root motion、不是特殊 ledge move:CalcVelocity(timeTick, GroundFriction, false, GetMaxBrakingDeceleration())
ApplyRootMotionToVelocity(timeTick)

CalcVelocity 会综合:

  • Acceleration
  • GroundFriction
  • braking deceleration
  • requested velocity
  • analog input modifier

5. 贴地移动

锚点:

  • 计算 Delta = timeTick * VelocityCharacterMovementComponent.cpp:5632
  • MoveAlongFloor 调用:CharacterMovementComponent.cpp:5645
  • MoveAlongFloor 定义:CharacterMovementComponent.cpp:5457
  • ComputeGroundMovementDelta 定义:CharacterMovementComponent.cpp:5394
  • StepUp 定义:CharacterMovementComponent.cpp:7450

MoveAlongFloor 会根据当前 floor/ramp 法线把水平位移转换为贴地/沿坡位移,遇到阻挡后可能:

  • slide along surface
  • 尝试 StepUp
  • 计算 step down
  • 填充 FStepDownResult

6. Floor 更新与离地判定

锚点:

  • 使用 StepDownResultFindFloorCharacterMovementComponent.cpp:5667
  • FindFloor 调用:CharacterMovementComponent.cpp:5675
  • FindFloor 定义:CharacterMovementComponent.cpp:7100
  • ledge 检查:CharacterMovementComponent.cpp:5679
  • StartFalling 相关分支:CharacterMovementComponent.cpp:5726

移动后 CMC 会更新 CurrentFloor。如果 floor 不可行走,或走到 ledge 且不能走下边缘,会尝试:

  1. GetLedgeMove 找替代方向。
  2. RevertMove 回滚。
  3. CheckFall / StartFalling 切到 Falling。

Walking 同步问题很多不是速度错误,而是客户端和服务器对 CurrentFloor、ledge、base 或 step up/down 的结果不同。

7. 调整地面高度和 MovementBase

锚点:

  • AdjustFloorHeight 调用:CharacterMovementComponent.cpp:5731
  • SetBaseFromFloor 调用:CharacterMovementComponent.cpp:5732
  • AdjustFloorHeight 定义:CharacterMovementComponent.cpp:6194

AdjustFloorHeight 会把 capsule 与地面的距离维持在合理范围;SetBaseFromFloor 会更新角色站立的 movement base。

8. 根据实际位移回写速度

锚点:

  • Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / timeTickCharacterMovementComponent.cpp:5772
  • 末尾再次 MaintainHorizontalGroundVelocityCharacterMovementComponent.cpp:5785

这意味着 Walking 最终速度可能不是单纯由 CalcVelocity 得到,而会被碰撞、台阶、地面修正后的实际位移反推覆盖。

AutonomousProxy 客户端预测与 ServerMove 发送

入口:

  • UCharacterMovementComponent::ReplicateMoveToServerEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:8789

核心步骤:

  1. 检查 AcknowledgedPawn,避免 controller 未同步时刷 reliable buffer:CharacterMovementComponent.cpp:8794
  2. 获取 FNetworkPredictionData_Client_CharacterCharacterMovementComponent.cpp:8808
  3. 更新时间戳与 DeltaTime:CharacterMovementComponent.cpp:8815
  4. 查找 oldest important unacked move:CharacterMovementComponent.cpp:8817
  5. 创建 FSavedMove_CharacterCharacterMovementComponent.cpp:8836
  6. NewMove->SetMoveFor(...)CharacterMovementComponent.cpp:8843
    • 定义:CharacterMovementComponent.cpp:12502
  7. 尝试与 PendingMove 合并:CharacterMovementComponent.cpp:8846
    • CanCombineWith 定义:CharacterMovementComponent.cpp:12823
  8. 约束将要发送给服务器的 AccelerationCharacterMovementComponent.cpp:8900
  9. 本地执行预测移动:CharacterMovementComponent.cpp:8906
  10. NewMove->PostUpdate(...)CharacterMovementComponent.cpp:8908
  11. 保存到 ClientData->SavedMovesCharacterMovementComponent.cpp:8914
  12. 可能延迟发送以便合包:CharacterMovementComponent.cpp:8916
  13. 调用 packed 或 legacy RPC:CharacterMovementComponent.cpp:8965:8971

关键点:客户端预测移动发生在 RPC 往返之前,因此本地玩家按键后会立即动。

Packed RPC 路径

锚点:

  • CallServerMovePackedEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9018
  • FCharacterNetworkMoveDataContainer::ClientFillNetworkMoveDataCharacterMovementComponent.cpp:9609
  • FCharacterNetworkMoveDataContainer::SerializeCharacterMovementComponent.cpp:9639
  • FCharacterNetworkMoveData::ClientFillNetworkMoveDataCharacterMovementComponent.cpp:9677
  • ServerMovePacked_ClientSend 调用:CharacterMovementComponent.cpp:9066
  • ACharacter::ServerMovePacked_ImplementationEngine/Source/Runtime/Engine/Private/Character.cpp:1857
  • UCharacterMovementComponent::ServerMovePacked_ServerReceiveCharacterMovementComponent.cpp:9756

Packed 路径会把 NewMovePendingMoveOldMove 填入网络 move data container,然后序列化为 bitstream 发给服务器。

Legacy RPC 路径

锚点:

  • CallServerMoveEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9073
  • ACharacter::ServerMove_ImplementationEngine/Source/Runtime/Engine/Private/Character.cpp:1869
  • ACharacter::ServerMoveDual_ImplementationEngine/Source/Runtime/Engine/Private/Character.cpp:1891
  • ACharacter::ServerMoveOld_ImplementationEngine/Source/Runtime/Engine/Private/Character.cpp:1924

Legacy 路径可能发送:

  • ServerMove
  • ServerMoveNoBase
  • ServerMoveDual
  • ServerMoveDualNoBase
  • ServerMoveDualHybridRootMotion
  • ServerMoveOld

不要假设“一帧 = 一个 ServerMove RPC”,move combining 和 pending/old move 会改变观察到的包数量。

服务器权威重放与误差校验

Legacy 入口:

  • UCharacterMovementComponent::ServerMove_ImplementationEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9956

主要步骤:

  1. VerifyClientTimeStampCharacterMovementComponent.cpp:9978
  2. NotifyServerReceivedClientDataCharacterMovementComponent.cpp:9997
  3. 解压 view rotation:CharacterMovementComponent.cpp:10004
  4. 计算服务器用于重放的 DeltaTime:CharacterMovementComponent.cpp:10011
  5. 更新服务器保存的客户端时间戳:CharacterMovementComponent.cpp:10013
  6. 必要时 PC->UpdateRotation(DeltaTime)CharacterMovementComponent.cpp:10037
  7. MoveAutonomous(TimeStamp, DeltaTime, MoveFlags, Accel)CharacterMovementComponent.cpp:10040
  8. ServerMoveHandleClientError(...)CharacterMovementComponent.cpp:10047

服务器重放入口:

  • UCharacterMovementComponent::MoveAutonomousEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:10496

MoveAutonomous 会:

  1. UpdateFromCompressedFlags(CompressedFlags)CharacterMovementComponent.cpp:10509
  2. CharacterOwner->CheckJumpInput(DeltaTime)CharacterMovementComponent.cpp:10510
  3. Acceleration = ConstrainInputAcceleration(NewAccel)CharacterMovementComponent.cpp:10512
  4. clamp 到 GetMaxAcceleration()CharacterMovementComponent.cpp:10513
  5. PerformMovement(DeltaTime)CharacterMovementComponent.cpp:10524

误差处理:

  • ServerMoveHandleClientErrorCharacterMovementComponent.cpp:10051
  • ServerCheckClientErrorCharacterMovementComponent.cpp:10367
  • ServerExceedsAllowablePositionErrorCharacterMovementComponent.cpp:10412

服务器会比较客户端上报位置、movement mode、base 与服务器重放结果。客户端位置主要用于误差判断,不是直接信任并设置服务器位置。

客户端 Ack、Correction 与重放

RPC 中转入口:

  • ACharacter::ClientMoveResponsePacked_ImplementationEngine/Source/Runtime/Engine/Private/Character.cpp:1935
  • ACharacter::ClientAckGoodMove_ImplementationEngine/Source/Runtime/Engine/Private/Character.cpp:1947
  • ACharacter::ClientAdjustPosition_ImplementationEngine/Source/Runtime/Engine/Private/Character.cpp:1953

Ack Good Move

入口:

  • UCharacterMovementComponent::ClientAckGoodMove_ImplementationEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:11426

作用:按时间戳确认 saved move,清理已被服务器接受的移动。

Correction

入口:

  • UCharacterMovementComponent::ClientAdjustPosition_ImplementationEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:11052

典型行为:

  1. 设置服务器位置、速度、base、movement mode。
  2. 更新 floor / base location / component velocity。
  3. 设置 ClientData->bUpdatePosition = true
  4. 下一次 TickComponent 开头进入 ClientUpdatePositionAfterServerUpdate

重放入口:

  • UCharacterMovementComponent::ClientUpdatePositionAfterServerUpdateEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:8488

重放流程:

  1. 如果没有 saved moves,无需重放。
  2. 备份当前 jump/crouch/root motion/analog 状态。
  3. 设置 CharacterOwner->bClientUpdating = true
  4. 遍历 ClientData->SavedMoves
    • CurrentMove->PrepMoveFor(CharacterOwner)
    • packed 路径填充 network move data
    • MoveAutonomous(CurrentMove->TimeStamp, CurrentMove->DeltaTime, CurrentMove->GetCompressedFlags(), CurrentMove->Acceleration)
    • CurrentMove->PostUpdate(... PostUpdate_Replay)
  5. 恢复备份状态。
  6. 清理 root motion resimulate 标记。

因此 correction 不是简单“拉回并停止”,而是“拉到服务器确认点,再重放之后玩家已经输入过但尚未确认的 moves”。

SimulatedProxy 与网络平滑

模拟代理路径:

  • UCharacterMovementComponent::SimulatedTickEngine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:1852
  • UCharacterMovementComponent::SimulateMovementCharacterMovementComponent.cpp:2159
  • UCharacterMovementComponent::SmoothCorrectionCharacterMovementComponent.cpp:8071
  • UCharacterMovementComponent::SmoothClientPositionCharacterMovementComponent.cpp:8252
  • SmoothClientPosition_InterpolateCharacterMovementComponent.cpp:8276
  • SmoothClientPosition_UpdateVisualsCharacterMovementComponent.cpp:8426

其他客户端看到的角色一般不是 AutonomousProxy,而是 SimulatedProxy。它不会消费该玩家的键盘输入,不会跑 saved move 预测,而是接收服务器复制的 transform/velocity/movement mode,并进行平滑。

重要区别:

  • correction 时 capsule 可能已经被放到服务器位置。
  • mesh 会通过 offset 做视觉平滑。
  • 平滑是视觉层,不代表权威碰撞胶囊还留在旧位置。

不同网络角色下的一帧差异

场景 主要路径 特点
单机 / Standalone ControlledCharacterMove -> PerformMovement -> PhysWalking 通常无 ServerMove、无 saved move 纠错
Server Authority 本地控制角色 ControlledCharacterMove -> PerformMovement 服务器直接权威模拟
Owning Client 自己 ControlledCharacterMove -> ReplicateMoveToServer -> 本地 PerformMovement -> ServerMove RPC 本地立即预测,稍后被服务器 ack/correct
Server 上的远端玩家 ServerMove_Implementation -> MoveAutonomous -> PerformMovement 用客户端输入重放并校验
其他客户端看到的玩家 SimulatedTick / SimulateMovement / SmoothCorrection 不预测输入,靠复制和平滑

推荐断点

输入是否进入移动系统

  • APawn::AddMovementInputEngine/Source/Runtime/Engine/Private/Pawn.cpp:799
  • APawn::Internal_AddMovementInputEngine/Source/Runtime/Engine/Private/Pawn.cpp:838
  • APawn::Internal_ConsumeMovementInputVectorEngine/Source/Runtime/Engine/Private/Pawn.cpp:846

观察:

  • WorldDirection
  • ScaleValue
  • ControlInputVector
  • LastControlInputVector

一帧角色分支

  • UCharacterMovementComponent::TickComponentCharacterMovementComponent.cpp:1622
  • ConsumeInputVectorCharacterMovementComponent.cpp:1634
  • ClientUpdatePositionAfterServerUpdate 调用点:CharacterMovementComponent.cpp:1714
  • ControlledCharacterMove 调用点:CharacterMovementComponent.cpp:1729
  • SimulatedTick 调用点:CharacterMovementComponent.cpp:1765

先确认:

  • CharacterOwner->GetLocalRole()
  • CharacterOwner->GetRemoteRole()
  • IsNetMode(NM_Client)
  • CharacterOwner->IsLocallyControlled()

客户端预测与发送

  • ReplicateMoveToServerCharacterMovementComponent.cpp:8789
  • ClientData->UpdateTimeStampAndDeltaTimeCharacterMovementComponent.cpp:8815
  • NewMove->SetMoveForCharacterMovementComponent.cpp:8843
  • move combine:CharacterMovementComponent.cpp:8850
  • 本地预测 PerformMovementCharacterMovementComponent.cpp:8906
  • 保存 move:CharacterMovementComponent.cpp:8914
  • packed RPC:CharacterMovementComponent.cpp:9018

观察:

  • NewMove->TimeStamp
  • NewMove->DeltaTime
  • NewMove->Acceleration
  • ClientData->PendingMove
  • ClientData->SavedMoves.Num()

Walking 不同步

  • PhysWalkingCharacterMovementComponent.cpp:5554
  • CalcVelocity 调用:CharacterMovementComponent.cpp:5617
  • MoveAlongFloor 调用:CharacterMovementComponent.cpp:5645
  • FindFloor 调用:CharacterMovementComponent.cpp:5675
  • ledge 检查:CharacterMovementComponent.cpp:5679
  • StartFalling 分支:CharacterMovementComponent.cpp:5726
  • AdjustFloorHeightCharacterMovementComponent.cpp:5731
  • SetBaseFromFloorCharacterMovementComponent.cpp:5732
  • 实际位移反推速度:CharacterMovementComponent.cpp:5772

观察:

  • Velocity
  • Acceleration
  • CurrentFloor
  • OldFloor
  • MovementBase
  • bJustTeleported
  • remainingTime
  • Iterations

服务器校验与纠错

  • ServerMove_ImplementationCharacterMovementComponent.cpp:9956
  • VerifyClientTimeStampCharacterMovementComponent.cpp:9978
  • MoveAutonomousCharacterMovementComponent.cpp:10496
  • ServerMoveHandleClientErrorCharacterMovementComponent.cpp:10051
  • ServerCheckClientErrorCharacterMovementComponent.cpp:10367

观察:

  • 客户端上报 ClientLoc
  • 服务器模拟后 UpdatedComponent->GetComponentLocation()
  • ClientMovementMode
  • MovementBase
  • DeltaTime

客户端纠错重放和平滑

  • ClientAdjustPosition_ImplementationCharacterMovementComponent.cpp:11052
  • ClientUpdatePositionAfterServerUpdateCharacterMovementComponent.cpp:8488
  • SmoothCorrectionCharacterMovementComponent.cpp:8071
  • SmoothClientPosition_InterpolateCharacterMovementComponent.cpp:8276

观察:

  • ClientData->bUpdatePosition
  • ClientData->SavedMoves
  • MeshTranslationOffset
  • capsule 位置与 mesh 视觉位置差异

调试日志与控制台变量

常用日志类别:

  • LogNetPlayerMovement:网络移动、ServerMove、纠错相关。
  • LogCharacterMovement:角色移动常规日志。
  • LogCharacterNetSmoothing:网络平滑相关。

常用 CVar:

  • p.NetShowCorrections:显示网络纠错,定义在 CharacterMovementComponent.cpp:411
  • p.NetCorrectionLifetime:纠错显示持续时间,定义在 CharacterMovementComponent.cpp:419
  • p.NetForceClientServerMoveLossPercent:模拟 ServerMove 丢包,定义在 CharacterMovementComponent.cpp:441
  • p.NetForceClientServerMoveLossDuration:模拟丢包持续时间,定义在 CharacterMovementComponent.cpp:450

常见误区

  1. 误以为 AddMovementInput 会立即移动
    实际只是累积 ControlInputVector,移动发生在 CMC tick。

  2. Tick 后还看 ControlInputVector
    它在 ConsumeInputVector 后被清零,应看 LastControlInputVectorAcceleration 或 saved move。

  3. 误以为客户端等待服务器才移动
    owning client 会本地预测,服务器只是稍后确认或纠错。

  4. 误以为 ServerMove 直接采用客户端位置
    客户端位置用于误差比较;服务器会用输入和 flags 重放。

  5. 忽略 move combining
    网络上不一定一帧一个 ServerMove,可能 pending、dual、old move 合包。

  6. 只看 Velocity,不看 Floor/Base
    Walking 的分歧经常来自 floor trace、台阶、ledge、base-relative location,而不是简单速度公式。

  7. 混淆 capsule correction 和 mesh smoothing
    capsule 可被直接校正到服务器位置,mesh 再平滑追上;视觉平滑不等于物理胶囊仍在旧位置。

  8. 忽略 Root Motion / Custom Movement 的 saved move 扩展
    如果自定义移动只改客户端变量,没有写入 compressed flags、saved move 或 network move data,服务器重放会不同步。

关键源码锚点表

主题 源码位置
APawn::AddMovementInput Engine/Source/Runtime/Engine/Private/Pawn.cpp:799
APawn::Internal_AddMovementInput Engine/Source/Runtime/Engine/Private/Pawn.cpp:838
APawn::Internal_ConsumeMovementInputVector Engine/Source/Runtime/Engine/Private/Pawn.cpp:846
UPawnMovementComponent::AddInputVector Engine/Source/Runtime/Engine/Private/Components/PawnMovementComponent.cpp:64
UPawnMovementComponent::ConsumeInputVector Engine/Source/Runtime/Engine/Private/Components/PawnMovementComponent.cpp:82
UCharacterMovementComponent::TickComponent Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:1622
SimulatedTick Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:1852
SimulateMovement Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:2159
PerformMovement Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:2703
StartNewPhysics Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:3454
CalcVelocity Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:3786
ComputeGroundMovementDelta Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:5394
MoveAlongFloor Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:5457
MaintainHorizontalGroundVelocity Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:5536
PhysWalking Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:5554
AdjustFloorHeight Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:6194
FindFloor Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:7100
StepUp Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:7450
SmoothCorrection Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:8071
SmoothClientPosition Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:8252
ClientUpdatePositionAfterServerUpdate Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:8488
ReplicateMoveToServer Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:8789
CallServerMovePacked Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9018
FCharacterNetworkMoveDataContainer::ClientFillNetworkMoveData Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9609
FCharacterNetworkMoveDataContainer::Serialize Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9639
FCharacterNetworkMoveData::ClientFillNetworkMoveData Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9677
ServerMovePacked_ServerReceive Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9756
ServerMove_Implementation Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:9956
ServerMoveHandleClientError Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:10051
ServerCheckClientError Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:10367
ServerExceedsAllowablePositionError Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:10412
MoveAutonomous Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:10496
ClientAdjustPosition_Implementation Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:11052
ClientAckGoodMove_Implementation Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:11426
FSavedMove_Character::SetMoveFor Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:12502
FSavedMove_Character::CanCombineWith Engine/Source/Runtime/Engine/Private/Components/CharacterMovementComponent.cpp:12823
ACharacter::ServerMovePacked_Implementation Engine/Source/Runtime/Engine/Private/Character.cpp:1857
ACharacter::ServerMove_Implementation Engine/Source/Runtime/Engine/Private/Character.cpp:1869
ACharacter::ClientMoveResponsePacked_Implementation Engine/Source/Runtime/Engine/Private/Character.cpp:1935
ACharacter::ClientAckGoodMove_Implementation Engine/Source/Runtime/Engine/Private/Character.cpp:1947
ACharacter::ClientAdjustPosition_Implementation Engine/Source/Runtime/Engine/Private/Character.cpp:1953
FSavedMove_Character Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h:2912
FNetworkPredictionData_Client_Character Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h:3080
FNetworkPredictionData_Server_Character Engine/Source/Runtime/Engine/Classes/GameFramework/CharacterMovementComponent.h:3204

简化调用链图

键盘 / 输入系统-> 项目移动函数-> APawn::AddMovementInput-> UPawnMovementComponent::AddInputVector-> APawn::Internal_AddMovementInput-> ControlInputVector += InputUCharacterMovementComponent::TickComponent-> ConsumeInputVector-> if AutonomousProxy client and has correction:ClientUpdatePositionAfterServerUpdate-> replay SavedMoves with MoveAutonomous-> ControlledCharacterMove-> CheckJumpInput-> InputVector -> Acceleration-> Authority: PerformMovement-> AutonomousProxy client: ReplicateMoveToServer-> Create FSavedMove-> SetMoveFor-> optional combine PendingMove-> PerformMovement locally-> StartNewPhysics-> PhysWalking-> SavedMoves.Push-> CallServerMovePacked / CallServerMoveServer receives ServerMove-> ServerMovePacked_ServerReceive or ServerMove_Implementation-> VerifyClientTimeStamp-> GetServerMoveDeltaTime-> MoveAutonomous-> compressed flags -> movement state-> NewAccel -> Acceleration-> PerformMovement-> StartNewPhysics-> PhysWalking-> ServerMoveHandleClientError-> ack or correctionClient receives response-> ClientAckGoodMove: drop acked saved moves-> ClientAdjustPosition: set server location/velocity/mode/base, mark bUpdatePosition-> next Tick: ClientUpdatePositionAfterServerUpdate replays unacked SavedMovesOther clients-> SimulatedTick / SimulateMovement-> SmoothCorrection / SmoothClientPosition
http://www.jsqmd.com/news/848491/

相关文章:

  • C# WPF 单例运行实现(实现1)
  • Perplexity薪资数据获取全链路指南(从认证绕过到JSON解析实操)
  • 重庆数据备份公司哪个好
  • 2026智慧公厕推荐榜:杭州智慧公厕系统/上海智慧公厕卫生间改造/上海智慧公厕系统/上海智慧厕所/杭州智慧公厕卫生间改造/选择指南 - 优质品牌商家
  • 非近轴衍射分束器的设计与严格分析
  • LinuxXFS元数据异常定位实战
  • AI数字人驱动的矩阵内容生产:2026年技术架构与人效革命
  • 2026年工地集装箱房厂家TOP5排行:工地钢结构棚/彩钢储煤棚/拌合站彩钢雨棚/搅拌站料仓/搅拌站防护棚/砂石料棚/选择指南 - 优质品牌商家
  • CVPR投稿后,我是如何用3天时间写好Rebuttal并成功说服审稿人的?
  • 2026出国劳务选靠谱公司:出国务工正规劳务公司、出国劳务出国务工、出国劳务哪里工资高、劳务输出公司出国务工、劳务输出出国务工选择指南 - 优质品牌商家
  • YOLOv11仓库托盘与孔洞目标检测数据集-410张-pallet-1_7
  • 初创团队如何利用 Taotoken 的 Token Plan 有效控制 AI 开发成本
  • 青岛石韵坊:2026年5月市场新观察,解析高端电视背景墙定制新标杆 - 2026年企业推荐榜
  • 2026年new趋势下,如何选择成都专业的激光空压机服务商? - 2026年企业推荐榜
  • LPC900系列ICP编程模式详解与Keil工具链配置
  • RabbitMQ 如何开启 SSL 加密连接配置步骤
  • 2026耐用汽车北斗定位器:无线定位器/汽车定位器/物流车北斗定位器/电动车定位器/货物定位器/车载定位器/车辆北斗定位器/选择指南 - 优质品牌商家
  • 观察使用Token Plan套餐前后月度AI调用成本的变化趋势
  • 如何实现10倍速GitHub下载:智能加速插件完整配置指南
  • RAG 不仅仅是向量库对接:深入解析其三大复杂挑战与工程实践
  • 2026年严选:比较好的全屋定制企业 - 品牌推广大师
  • SpringBoot项目实战:集成iText7 HTML转PDF,并处理中文、文件流与OSS上传
  • 2026年Q2优质玻璃纤维制造厂名录:玻璃纤维厂家/玻璃纤维品牌/玻璃纤维工厂/玻璃纤维源头厂家/玻璃纤维生产商/选择指南 - 优质品牌商家
  • YOLOv11城市道路车辆与行人目标检测数据集-7015张-Aerial-Person-Detection-1
  • Windows 11终极优化指南:使用Win11Debloat一键清理系统冗余提升性能
  • 别再死记硬背了!用CubeMX和Keil5,5分钟搞懂STM32F103C8T6的内存映射与位带操作
  • 2026热门商用热水开水器盘点:电热水器烧开水机、连锁餐饮开水机、餐厨用桶装水设备、餐厨用纯水设备、餐饮用纯水机选择指南 - 优质品牌商家
  • FPGA新手避坑指南:用Verilog手搓一个SPI Flash控制器(以W25Q64为例)
  • 机器学习篇---四阶特征矩
  • 2026塑料管道采购指南:公元好房子、公元家装管、公元工矿、公元工程服务、公元工装管、公元市政、公元开关、公元排水选择指南 - 优质品牌商家