虚幻引擎C++调试技巧:数据断点

科技   2024-10-16 12:30   上海  

数据断点应该是每个UE C++程序员进行调试的必备工具。这是一个游戏开发场景案例,数据断点在此案例中作用很明显。
数据断点是Visual Studio和Rider等编程IDE的一个功能。它是你在某个内存地址上设置的断点,当该地址的值发生变更时,该断点会被触发。然后,你可以使用IDE的调试器来检查调用堆栈,并找出修改了该值的代码。请参阅此处(https://learn.microsoft.com/en-us/visualstudio/debugger/using-breakpoints?view=vs-2022) Visual Studio 中的数据断点教程或此处(https://www.jetbrains.com/help/rider/Unreal_Engine__Debugger.html#ue_data_breakpoints) Rider 的监视点(数据断点)文档。
本教程演示了数据断点在虚幻引擎游戏开发场景中的实用性。每个UE C++程序员都应该在调试工具中使用数据断点。
示例:覆盖Actor的Rotation属性
假设我正在开发一款多人游戏,正在尝试从服务器上设置Player1的Pawn的Rotation属性。我的首次尝试是让服务器在我想要旋转的Pawn上调用SetActorRotation函数。该行为在服务器和Player2的客户端上效果很好,然而由于某种原因,我发现Player1的客户端上,Pawn并不总是和其在服务器上的旋转方式相同——要么Transform属性没有被复制,要么在Player1的客户端上执行的某些代码在复制操作完成后覆盖掉了原有的值。
在下面的动画屏幕捕捉中,服务器每秒对Player1的Pawn旋转90°,但在Player1客户端上的Pawn并没有按照预期的那样旋转。近半在服务器集合上的旋转操作似乎并未同步于Pawn的持有者客户端:

注:如果图片未自动播放,请点击图片播放。

接下来我尝试让服务器调用一个客户端的RPC,以便Player1的客户端将旋转操作直接应用于其本地控制的Pawn:
// Header fileUFUNCTION(BlueprintCallable, Client, Reliable)void ClientSetRotation(const FRotator& RotationFromServer);virtual void ClientSetRotation_Implementation(const FRotator& RotationFromServer);
// CPP filevoid AMyCharacter::ClientSetRotation_Implementation(const FRotator& RotationFromServer){ // Apply the rotation SetActorRotation(RotationFromServer);
// Check that it indeed has that rotation check(GetActorRotation().Equals(RotationFromServer));}
我现在确认的是,Player1的客户端将在本地进行正确的Actor旋转操作。然而,当我进入游戏模式时,发现该Pawn并不总是旋转。在这一点上,我确认我无论设置任何Rotation的值是什么,它都会被另一个系统覆盖。
使用数据断点进行跟踪
注:本教程演示了在Visual Studio中的步骤,同样的方法也可以在Rider中实现。
由于SceneComponent的Transform属性是在引擎代码库的许多地方设置的,因此研究所有代码或将断点放在所有会修改Transform属性的地方在时间上并非很高效。那些断点也会在很多不相关的上下文中触发。
为取代这种低效方案,我会在Player1的客户端执行时SetActorRotation函数的调用之处设置一个断点,让程序运行至该断点,并在其再次修改Rotation值时开始观察。Actor的Rotation属性作为ComponentToWorld变量(FTransform类型)的一部分存储在其RootComponent上,这样就可以在“Autos”选项卡中观察Actor->RootComponent->ComponentToWorld的值。

注:你可以使用“Autos”选项卡中的搜索栏展开类,快速地查找并突出显示变量。例如,键入RootComponent可以立即导航到Character的RootComponent。这种方法对唯一命名的变量而言尤其有帮助。

之后,你可以展开ComponentToWorld的Transform属性的旋转四元数。右键单击该四元数的每个初始值X、Y、Z、W,然后选择“当值修改时中断”选项。这样你就将数据断点放到了该数据的内存地址上。

现在我恢复执行程序。由于我在SetActorRotation函数上设置了断点,所以该行代码尚未执行。我第一次触发数据断点是在SetActorRotation函数调用中。这仅仅是为了演示一个事实——你可以在进行过预期的修改后选择将初始断点放在该行。

恢复执行代码,数据断点再次触发,我找到覆盖掉了刚刚设置的Rotation值的内容:
在此案例中,我发现CharacterMovementComponent对Player移动的本地预测可以将Pawn的Rotation属性恢复为之前的值,有效地撤销了我们早些时候在帧中的SetActorRotation函数调用。有多种方法可以解决该问题。在这个移动Character的案例中,已经实现了一种可以通过重写UCharacterMovementComponent::ShouldCorrectRotation()函数并调用UCharacterMovementComponent::SendClientAdjustment()函数来正确复制Rotation属性值的方法,后者的作用是从某个在服务器上复制的值更新本地Player预计的移动缓冲区。该函数只需要被重写并正确调用即可。

数据断点可以帮助你快速找到不需要的或预期之外的值修改的来源,即便你并不熟悉那段代码。在某些情况下,相比使用断点一步步地执行代码这种一般的方法,这种方法会更快地帮助你知晓问题所在。

有关更多调试的技巧,请查阅此集合:虚幻引擎中的高级调试(https://dev.epicgames.com/community/learning/tutorials/dXl5/advanced-debugging-in-unreal-engine)

近期焦点
优化 Android 移动设备的打包大小
深入了解这些全新的虚幻引擎课程和教程
七个虚幻引擎游戏迁移案例
UE5标识符详解 | 你一定会收藏!
Dimension Studio见证越来越多的导演转向虚拟制片


扫描下方二维码,关注后点击菜单栏按钮“更多内容”并选择“联系我们”获得更多虚幻引擎的授权合作方式和技术支持

长按屏幕选择“识别二维码”关注虚幻引擎

“虚幻引擎”微信公众账号是Epic Games旗下Unreal Engine的中文官方微信频道,在这里我们与大家一起分享关于虚幻引擎的开发经验与最新活动。


虚幻引擎
Epic Games 旗下 Unreal Engine 虚幻引擎官方订阅号
 最新文章