UE4 CharacterMovementComponent

news/2024/9/20 15:39:08/文章来源:https://www.cnblogs.com/XTG111/p/18378971

之前在学习网络通信时,经常需要对客户端上的行为进行预测,来降低延迟带来的比较差的体验。而UE4为角色移动提供了CharacterMovementComponent这个组件,其在移动时可以发现已经实现了客户端预测。
现在可以通过对CharacterMovementComponent进行扩展,实现我们自定义的运动模式,并且还能够借助于UE写好的网络同步实现多人游戏上的表现

FSavedMove_Character

这是UE在网络通信时传递的有关客户端角色的移动信息的类,其通过类中有一个成员变量TimeStamp来进行客户端和服务器上的统一。服务器上会存在一个最新一次确认成功的移动版本,当客户端角色产生移动时,会不断将移动信息存储为这个类对象,然后发送给服务器,服务器上会根据TimeStamp来进行权威角色的更新。

客户端移动过程

首先根据当前的硬件输入获取到移动方向,然后引擎将输入转换为加速度然后在客户端上将进行移动(根运动)以及更新物理模拟(通过运动模式等等)。之后会进行时间同步,生成FSavedMove_Character类对象,然后将数据压缩到FCharacterNetworkMoveData结构体中进行传输,服务器通过接受该数据后,进行权威角色的移动。

服务器上更新过程

通过客户端将移动信息发送到服务器之后,会执行根据传输信息进行服务器上权威角色的移动更新位置和模拟物理,此时会得到服务器上的一个准确位置loc,之后在服务器上进行loc的对比(客户端和服务器上),如果不匹配将执行客户端Loc的调整

客户端调整

由于延迟的存在当服务器上验证了时刻为t0的权威位置,如果进行比较发现客户端上位置出现了错误,那么就会进行调整。
此时客户端上由于会进行预测已经运动到了时刻为t1的位置。
考虑一种最简单的情况,就是说直接将此时客户端的t1位置修改到服务器确认的t0位置,但是会出现视觉上的拉扯。
而UE是这样做的,假如在t0时刻验证了出现错误,那么会首先将这个正确的位置loc0返回给客户端,而由于从t0到t1这一段是还没有验证的所以可以假定为是正确的,所以会在客户端从loc0沿着t0到t1计算出t1时刻的位置,从而纠正的差距没有那么大

模拟物理


在使用过程中可以通过调整MoveMode来实现move时Actor的不同物理模拟特性
而在物理模拟的函数中可以读取deltaTime,当前提供的加速度,运动时的状态还有一些标志位,将这些数据通过对应的MoveMode设计的逻辑进而计算得到Actor的Loc,Rot,速度以及目前的状态。
当在一个模拟的物理状态时,如果需要切换可以通过StartNewPhysics进行切换

/** changes physics based on MovementMode */
virtual void StartNewPhysics(float deltaTime, int32 Iterations);

PerformMove

该函数是主要用来移动Actor的功能函数,这个函数是每帧执行的,在执行这个函数的时候会调用上述的模拟物理的计算。当出现客户端和服务器不一致的情况,那么就会执行客户端调整,但是从客户端进行位置更新时,会在一帧中将PredictReal的位置计算出来。但是同样也会调用PerformMove函数同时执行模拟物理函数,所以如果在模拟物理中执行了一些其他的计算可能会导致计算多次,并且后续计算PredictReal的位置也只会在客户端上执行,从而会导致客户端和服务器的不同步。但UE提供一些参数这种参数会在服务器和客户端上保持同步。

保持同步的参数

该保持同步的参数其实是指类似于SavedMove这种类对象,因为先后两次的PerformMove(第一次客户端预测,第二次根据服务器进行调整),执行Move都是从同一个SavedMove获得的,也就是说当第二次从服务器返回的真实位置预测位置时,会从第一个SavedMove重新计算调用。
由于在进行纠正的时候,会在客户端会重新执行PerformMove然后继续执行模拟物理(这一段执行的SavedMove是和预测时执行的完全一致的),这些是只会在客户端上执行的,如果执行的函数中,使用了一些其他参数,比如说一个count计数器,当在第一次进行客户端预测的时候每一帧进行count++,到t1时刻后count变为5,那么当进行调整时,该count不会重置,而是继续增加。
但如果我们将count存放在SavedMove里面,会在第二次调用performMove时进行重置,这样可以替代实现数据同步。

UE提供的一些会发生重置的参数

  1. SavedMove
  2. Velocity和Acceleration
  3. CompressedFlags
  4. CharacterOwner
    但是我们自定义的CharacterOwner属性是不可以重置的
    所以说在模拟物理的函数中我们只能使用这些可以重置的变量。

Character运动发送到服务器上的流程(以Dash功能为例)
客户端处理输入

  1. 首先通过硬件输入,会修改CharacterMovement组件中的一个布尔值变量,该变量就是表示当前状态
  2. 然后会调用SavedMove类里面的SetMoveFor修改类里面对应的Flag
  3. 接着会调用CanCombineWith函数,来判断是否可以和之前的动作Move结合到一起发送
  4. 然后调用GetCompressedFlags函数,将整个类对象压缩到适合网络传输的数据包中
    服务器接受到数据之后
  5. 会调用CharacterMovement类里面的UpdateFromCompressedFlags函数对服务器上的对应的布尔值变量进行修改

构建自定义的CharacterMovementComponent

前期准备

为了实现自定义的CharacterMovementComponent,需要在自定义类中重写一些类和函数

FSavedMove_XCharacter类

继承于FSavedMove_Character类,该类就是主要保存需要每一帧发送给服务器状态信息的快照,当我们自定义时,一般都会增加需要额外保存同步的状态,所以需要自定义这个类,然后定义后续的传输是传递的这个类

class FSavedMove_XCharacter : public FSavedMove_Character
{typedef FSavedMove_Character Super;//The Flag Use To send and rec Server When Safe_bWantsToSprint Changged the param will change//and the replicate from sever will change the Safe_bWantsToSprintuint8 Saved_bWantsToSprint : 1;//Check The OldMove can or not Combine with NewMove//if true will combine two Movevirtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const override;//Reset The All States In SavedMovevirtual void Clear() override;//SavedMove Flags//The Flags always use to control or show the Statesvirtual uint8 GetCompressedFlags() const;//From The State To Set The SavedFlagvirtual void SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel, class FNetworkPredictionData_Client_Character& ClientData);//From The SaveFlag To Set The Statevirtual void PrepMoveFor(ACharacter* C);
};

类中的函数都是重写的FSavedMove_Character类里面的函数

FNetworkPredictionData_Client_XCharacter类

继承于FNetworkPredictionData_Client_Character,该类就是告知客户端后续网络传输使用上面创建的SavedMove而不是标准的SavedMove

class FNetworkPredictionData_Client_XCharacter : public FNetworkPredictionData_Client_Character
{
public:FNetworkPredictionData_Client_XCharacter(const UCharacterMovementComponent& ClientMovement);typedef FNetworkPredictionData_Client_Character Super;virtual FSavedMovePtr AllocateNewMove() override;
};

通知客户端进行网络传输使用上方的FNetworkPredictionData_Client

这是一个函数

virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;

通过返回的SavedMove里面的Flag修改状态

//Update State Form the Flags In SavedMove
virtual void UpdateFromCompressedFlags(uint8 Flags) override;

响应状态更新需要处理的逻辑

该函数会在每次移动的时候执行

//Update after pre move and will do any logic don't care about the Movemode
virtual void OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity) override;

上述基本上就是需要固定写的一部分逻辑

以Shift加速为例

不更改CharacterMovementComponent

如果不更改CharacterMovementComponent,在多人游戏中想要实现某个角色按下Shift加速的功能,就需要调用RPC来进行传递,当按下Shift后首先可以在本地客户端进行速度的更改,然后调用ServerRPC将速度更改传递到服务器上,然后通过CharacterMovementComponent的自动网络同步功能,将整个Actor的运动同步,实现所有客户端和服务器的体现速度的变换
如果只在本地设置速度变化,会发现客户端上角色明显的抽搐(rubber banding)

更改CharacterMovementComponent

在之前介绍的固定框架下需要增加冲刺的状态,以及在FSavedMove里面增加冲刺的Flag

//cmc.h
class FSavedMove_XCharacter : public FSavedMove_Character
{uint8 Saved_bWantsToSprint : 1;
}
bool Safe_bWantsToSprint;

其次可以向基本CMC定义速度一样定义两个变量来继续控制

//cmc.h
UPROPERTY(EditDefaultsOnly)
float Sprint_MaxWalkSpeed;
UPROPERTY(EditDefaultsOnly)
float Walk_MaxWalkSpeed;

接着有两个蓝图可调用的函数,可以用来响应按键,从而进行对Safe_bWantsToSprint的修改

//CMC.h
UFUNCTION(BlueprintCallable)
void SprintPressed();
UFUNCTION(BlueprintCallable)
void SprintReleased();
//CMC.cpp
void UXCharacterMovementComponent::SprintPressed()
{Safe_bWantsToSprint = true;
}void UXCharacterMovementComponent::SprintReleased()
{Safe_bWantsToSprint = false;
}

最后是运动状态的更改就是写在之前编写的OnMovementUpdated函数中

//CMC.cpp
void UXCharacterMovementComponent::OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity)
{//Do Some Change when the State ChangeSuper::OnMovementUpdated(DeltaSeconds, OldLocation, OldVelocity);if (MovementMode == MOVE_Walking){if (Safe_bWantsToSprint){MaxWalkSpeed = Sprint_MaxWalkSpeed;}else{MaxWalkSpeed = Walk_MaxWalkSpeed;}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/787509.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

抖音coze接入网站实现智能在线客服

在coze.cn上创建好机器人并且可以发布为API,方便接入自己的系统 一定要先创建新令牌,再点发布,并且必须勾选,发布到API 在客服系统gofly.v1kf.com中配置 【菜单】【团队设置】【大模型设置】接口地址部分,请填写扣子的机器人ID 接口密钥部分,请填写扣子的API令牌然后…

centos9(linux): 安装clamav

一,官方网站 https://www.clamav.net/ 如图:二,安装 1,开启epel仓库 [root@blog ~]# yum install -y epel-release 2,安装clamav [root@blog ~]# yum install clamav 说明:病毒库数据较大,200多M,安装时需要等待较长时间 安装完成后查看clamav的版本: [root@blog ~]# clam…

pygame手搓五子棋

代码:#coding=utf-8import os,sys,re,time import pygame import random from win32api import GetSystemMetricspygame.init() pygame.display.set_caption("五子棋")percent = 0.6 screen_width = GetSystemMetrics(0) screen_height = GetSystemMetrics(1) windo…

网站提示404错误:页面未找到怎么办

当网站提示 404 Error 或 “页面未找到” 时,这意味着客户端尝试访问的资源在服务器上不存在或无法找到。这种情况很常见,可以通过以下几个步骤来诊断和解决问题: 常见原因URL 输入错误:这是最常见的原因之一。由于人为疏忽或输入错误,导致请求的 URL 与服务器上实际存在的…

OpenCV开发笔记(七十九):基于Stitcher类实现全景图片拼接

前言一个摄像头视野不大的时候,我们希望进行两个视野合并,这样让正视的视野增大,从而可以看到更广阔的标准视野。拼接的方法分为两条路,第一条路是stitcher类,第二条思路是特征点匹配。  本篇使用stitcher匹配,进行两张图来视野合并拼接。 Demo 两张图拼接过程步骤一:…

WPF 路由事件

一、什么是路由事件? 根据MSDN定义:功能定义:路由事件是一种可以针对元素树中的多个侦听器(而不是仅针对引发该事件的对象)调用处理程序的事件。 实现定义:路由事件是由 类的实例支持的 CLR 事件, RoutedEvent 由事件 Windows Presentation Foundation (WPF) 系统处理。…

【转载】Win11优化大小核调度(无需重启)

出处:https://bbs.saraba1st.com/2b/thread-2140520-1-1.html 打开隐藏电源管理选项: 管理员模式运行cmd,分别输入: powercfg -attributes SUB_PROCESSOR 7f2f5cfa-f10c-4823-b5e1-e93ae85f46b5 -ATTRIB_HIDE powercfg -attributes SUB_PROCESSOR 93b8b6dc-0698-4d1c-9ee4-…

【Pytorch教程】迅速入门Pytorch深度学习框架

@目录前言1.tensor基础操作1.1 tensor的dtype类型1.2 创建tensor(建议写出参数名字)1.2.1 空tensor(无用数据填充)API示例1.2.2 全一tensor1.2.3 全零tensor1.2.4 随机值[0,1)的tensor1.2.5 随机值为整数且规定上下限的tensorAPI示例1.2.6 随机值均值0方差1的tensor1.2.7 从…

AMD显卡VGA转HDMI花屏或者雪花屏解决办法

本以为是显卡或者显示屏或者转接线有问题,查阅资料后得出VGA转HDMI花屏或者雪花屏解决办法: 进入AMD控制面板。点击设置 - > 显示器 - > 覆盖 - > 禁用HDCP 即可完美解决问题。

AIGC时代,如何为“数据飞轮”提速

本文从技术角度,具体拆解DataLeap-找数助手、开发助手的实现方式,详AIGC如何为企业数智化转型赋能。更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 企业通过数智化转型实现降本增效,已经成为行业共识。而随着AIGC时代到来,企业的…

windows 安装nginx

1.进入下载页面下载window版本 安装 2.解压, 直接运行 exe就行 3.使用nssm 将exe文件 作为服务运行 4.配置conf/nginx.conf 的配置文件

使用yum命令报错

报错如下Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock error was 14: curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error"One of the configured repositories fai…