UE5中的移动预测机制

UE5的移动预测系统与GAS(Gameplay Ability System)有相似之处,但实现机制有所不同。本文将从底层解析UE5中客户端移动是如何预测并同步到服务器的。

基础架构:Character Movement Component

UE5的移动预测核心是UCharacterMovementComponent,它实现了客户端预测、服务器权威和回滚纠正的整套机制。

// UCharacterMovementComponent内部关键结构  

struct FNetworkPredictionData_Client_Character  
{  
    // 预测键相关  
    uint32 ClientUpdateNumber;    // 客户端移动更新计数  
    TArray<FSavedMovePtr> SavedMoves;  // 已发送但未确认的移动  
    FSavedMovePtr PendingMove;    // 待发送的移动  
    FSavedMovePtr LastAckedMove;  // 服务器最后确认的移动  
    // ...  
};  

客户端移动同步到服务器的流程

1. 输入收集与移动创建

每帧客户端处理输入并创建FSavedMove_Character对象:

void UCharacterMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)  
{  
    // 1. 收集当前输入状态  
    // 2. 创建并填充FSavedMove对象  
    FSavedMove_Character* NewMove = AllocateNewMove();  
    NewMove->SetMoveFor(CharacterOwner, DeltaTime, Acceleration, NewRotation);  
    // 3. 执行预测移动  
    PerformMovement(DeltaTime);  
    // 4. 保存移动到预测数据中  
    ClientData->PendingMove = NewMove;  
}  
2. 网络RPCs发送移动数据

UE使用特殊的RPC机制发送移动信息:

void UCharacterMovementComponent::CallServerMove()  
{  
    FSavedMovePtr LastClientMove = ClientData->SavedMoves.Last();  
    
    // 每个服务器移动请求包含:  
    // - 时间戳  
    // - 预测键(ClientUpdateNumber)  
    // - 加速度、旋转等移动参数  
    // - 位置(用于验证)  
    
    if (CanSendLastMove())  
    {  
        ServerMove(  
            LastClientMove->TimeStamp,  
            LastClientMove->Acceleration,  
            LastClientMove->GetCompressedFlags(),  
            LastClientMove->ClientUpdateNumber  // 这是预测键  
        );  
    }  
}  
3. 服务器验证与权威处理

服务器接收到移动请求后验证和应用:

void UCharacterMovementComponent::ServerMove_Implementation(float TimeStamp, FVector_NetQuantize10 InAccel, uint8 CompressedMoveFlags, uint32 ClientUpdateNumber)  
{  
    // 验证时间戳防止作弊  
    if (!IsValidTimeStamp(TimeStamp))  
    {  
        return;  
    }  
    
    // 应用移动  
    CharacterOwner->MoveAutonomous(TimeStamp, InAccel, CompressedMoveFlags);  
    
    // 记录最后确认的移动请求编号  
    ServerData->LastClientUpdateNumber = ClientUpdateNumber;  
    
    // 如必要,发送纠正信息  
    if (NeedsClientCorrection())  
    {  
        ClientAdjustPosition(CurrentServerTimeStamp, GetActorLocation(), GetActorRotation());  
    }  
}  
4. 客户端纠正机制

当客户端预测与服务器权威计算不匹配时,执行纠正:

void UCharacterMovementComponent::ClientAdjustPosition_Implementation(float TimeStamp, FVector NewLocation, FVector NewVelocity, UPrimitiveComponent* NewBase)  
{  
    // 1. 查找匹配的预测移动  
    // 2. 计算错误量  
    // 3. 应用纠正并重新应用后续所有待确认移动  
    
    // 错误超过阈值时,直接纠正  
    if (ClientError > AllowedError)  
    {  
        // 丢弃所有待确认移动并直接采用服务器状态  
        UpdateComponentVelocity();  
        // 平滑过渡到正确位置  
        SmoothCorrection(NewLocation);  
    }  
    else  
    {  
        // 重新应用所有待确认移动  
        ForcePositionUpdate(TimeStamp);  
        ReplayMoves(ClientData);  
    }  
}  

与GAS预测系统的区别与联系

虽然角色移动和GAS都使用预测键(prediction key)概念,但有几个关键差异:

  1. 细粒度不同

    • 移动预测通常每帧或固定间隔发送

    • GAS预测通常基于能力激活事件触发

  2. 数据结构不同

    • 移动使用FSavedMove_Character保存状态

    • GAS使用FPredictionKeyFGameplayAbilitySpec

  3. 网络优化

    • 移动预测专门设计了压缩算法减少带宽

    • 移动预测有批量处理机制(ServerMoveBatch)减少RPC调用

底层源码关键点

在引擎源码层面,以下是几个核心实现点:

预测键生成

// 在客户端生成唯一递增的预测键  
ClientData->ClientUpdateNumber++;  

移动数据压缩

// 使用量化和标志位压缩移动数据  
uint8 CompressedFlags = 0;  
if (bPressedJump) CompressedFlags |= FLAG_Jump;  
if (bWantsToCrouch) CompressedFlags |= FLAG_Crouch;  
// FVector_NetQuantize10用于位置压缩  

网络带宽优化

// 仅当必要时发送完整数据  
if (PendingMove->GetCompressedFlags() != LastAckedMove->GetCompressedFlags())  
{  
    // 发送更完整的状态  
    ServerMoveFull();  
}  
else  
{  
    // 发送增量更新  
    ServerMoveMinimal();  
}  

物理状态同步
当使用物理模拟时,底层还会同步FRigidBodyState确保物理状态一致。

深入定制移动预测

如果需要扩展默认预测系统,可以:

  • 派生自己的FSavedMove类:
class FMySavedMove : public FSavedMove_Character  
{  
    // 添加自定义状态  
    bool bMyCustomFlag;  

    virtual void SetMoveFor(...) override  
    {  
        Super::SetMoveFor(...);  
        // 保存自定义状态  
        bMyCustomFlag = MyCharacter->IsCustomFlagActive();  
    }  

    virtual void PrepareForReplay() override  
    {  
        Super::PrepareForReplay();  
        // 设置回放状态  
        MyCharacter->SetCustomFlag(bMyCustomFlag);  
    }  
};  
  • 扩展UCharacterMovementComponent
UMyMovementComponent::AllocateNewMove()  
{  
    return new FMySavedMove();  
}  

高级网络优化考虑

  1. 客户端权威与服务器权威平衡
    可以通过修改ShouldUsePackedMovementRPCs()ClientAuthorativePosition调整。

  2. 移动补偿(Lag Compensation)
    UWorld::GetTimeSeconds()基础上,UE提供FRewindData支持服务器回滚验证。

  3. 智能合并与优先级
    可定制ProcessQueuedMoves()控制移动请求的优先级和合并策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值