UE 5.4中的自定义重力

科技   2024-11-03 10:35   河北  
点击上方蓝字CG世界关注我们
再点右上角···设为星标★


UE 5.4在CharacterMovementController中引入了对自定义重力的支持。了解如何利用它并做一些很酷的事情,比如重力拼图,或者创建一个像重力游戏一样的迷你星球。

简介

在本教程中,我们将利用UE 5.4的重力覆盖功能,使Player能够在一个小行星上奔跑。

这将为您自己的项目提供任何其他类型的重力操作的良好基础。

注:自定义重力在早期版本的Unreal中并不完全有效。相关代码可能存在,但存在一些问题使其无法使用。

创建项目

对于此项目,我们将扩展第三人称蓝图项目。本教程不需要初学者内容包,因此我跳过了它,但欢迎您将其包含在内。

添加重力摄像机处理(C++)
当您在游戏中移动鼠标时,Unreal将获取该鼠标输入并将其转发到Player Controller以用于在Player周围移动摄像机。但是鼠标坐标始终和世界本身保持相关,其Z轴向下。例如,当Player倒置时,这些鼠标输入需要考虑到这一点,否则我们的摄像机控制将颠倒。
为了解决这个问题,我们将添加一些代码来接收此输入,然后将其转换为“局部重力空间”,以便摄像机控制可以保持“相对”。我们通过创建一个名为GravityController的新C++类来实现这个功能,该类扩展了普通的APlayerController类,但增加了对相对重力输入处理的支持。

这是我们在本教程中唯一需要的C++类,其他所有内容都将在蓝图中。让我们通过单击工具 → 创建新的C++类...

对于父类,我们现在只需选择“无”,无论如何,我们都将在下一步中将代码复制并粘贴到其中。

调用新类GravityController,然后创建它。Unreal现在会将项目转换为C++项目,并为我们创建一个默认代码模块。

按“OK”继续。

按“No”关闭此对话框。

现在,我们将关闭Unreal并填充新类。

转到项目的文件夹并打开Source文件夹。其中应该有一个与您的项目同名的子文件夹,这是您的默认游戏模块。我们的新类应该在那里。

在诸如Visual Studio甚至只是记事本这样的文本编辑器中打开GravityController.h。删除其中的所有内容并将其替换为以下代码:
#pragma once
#include "CoreMinimal.h"#include "GameFramework/PlayerController.h"#include "GravityController.generated.h"
/** * A Player Controller class which adds input-handling functionality for * CharacterMovementController's custom gravity mechanics. */UCLASS()class AGravityController : public APlayerController{ GENERATED_BODY()
public: virtual void UpdateRotation(float DeltaTime) override;
// Converts a rotation from world space to gravity relative space. UFUNCTION(BlueprintPure) static FRotator GetGravityRelativeRotation(FRotator Rotation, FVector GravityDirection);
// Converts a rotation from gravity relative space to world space. UFUNCTION(BlueprintPure) static FRotator GetGravityWorldRotation(FRotator Rotation, FVector GravityDirection);
private:  FVector LastFrameGravity = FVector::ZeroVector;};

现在打开GravityController.cpp,删除其中的所有内容并将其替换为以下代码:

#include "GravityController.h"#include "GameFramework/Character.h"#include "GameFramework/CharacterMovementComponent.h"
void AGravityController::UpdateRotation(float DeltaTime){ FVector GravityDirection = FVector::DownVector; if (ACharacter* PlayerCharacter = Cast<ACharacter>(GetPawn())) { if (UCharacterMovementComponent* MoveComp = PlayerCharacter->GetCharacterMovement()) { GravityDirection = MoveComp->GetGravityDirection(); } }
// Get the current control rotation in world space FRotator ViewRotation = GetControlRotation();
// Add any rotation from the gravity changes, if any happened. // Delete this code block if you don't want the camera to automatically compensate for gravity rotation. if (!LastFrameGravity.Equals(FVector::ZeroVector)) { const FQuat DeltaGravityRotation = FQuat::FindBetweenNormals(LastFrameGravity, GravityDirection); const FQuat WarpedCameraRotation = DeltaGravityRotation * FQuat(ViewRotation);
ViewRotation = WarpedCameraRotation.Rotator(); } LastFrameGravity = GravityDirection;
// Convert the view rotation from world space to gravity relative space. // Now we can work with the rotation as if no custom gravity was affecting it. ViewRotation = GetGravityRelativeRotation(ViewRotation, GravityDirection);
// Calculate Delta to be applied on ViewRotation FRotator DeltaRot(RotationInput);
if (PlayerCameraManager) { ACharacter* PlayerCharacter = Cast<ACharacter>(GetPawn());
PlayerCameraManager->ProcessViewRotation(DeltaTime, ViewRotation, DeltaRot);
// Zero the roll of the camera as we always want it horizontal in relation to the gravity. ViewRotation.Roll = 0;
// Convert the rotation back to world space, and set it as the current control rotation. SetControlRotation(GetGravityWorldRotation(ViewRotation, GravityDirection)); }
APawn* const P = GetPawnOrSpectator(); if (P) { P->FaceRotation(ViewRotation, DeltaTime); }}
FRotator AGravityController::GetGravityRelativeRotation(FRotator Rotation, FVector GravityDirection){ if (!GravityDirection.Equals(FVector::DownVector)) { FQuat GravityRotation = FQuat::FindBetweenNormals(GravityDirection, FVector::DownVector); return (GravityRotation * Rotation.Quaternion()).Rotator(); }
return Rotation;}
FRotator AGravityController::GetGravityWorldRotation(FRotator Rotation, FVector GravityDirection){ if (!GravityDirection.Equals(FVector::DownVector)) { FQuat GravityRotation = FQuat::FindBetweenNormals(FVector::DownVector, GravityDirection); return (GravityRotation * Rotation.Quaternion()).Rotator(); }
return Rotation;}
编译项目

在此部分,您需要安装Visual Studio。如果您尚未安装,请查看本教程,了解如何执行此操作:设置Visual Studio | Epic开发者社区(https://dev.epicgames.com/documentation/en-us/unreal-engine/setting-up-visual-studio-development-environment-for-cplusplus-projects-in-unreal-engine)。

打开项目的Visual Studio解决方案文件。
然后按F5键,或按“▷本地的Windows调试器”按钮,编译项目并使用新类运行Unreal编辑器。

使用新的重力控制器

我们现在已经创建了GravityController类,但我们还需要通知Unreal使用该类,而不是默认的内置类。

进入虚幻编辑器后,打开Content/ThirdPerson/Blueprints/BP_ThirdPersonGameMode蓝图,并修改PlayerControllerClass属性以使用我们的新GravityController类。

和重力相关的鼠标输入

现在我们需要稍微修复一下鼠标输入。对于左/右Pawn输入,第三人称Character会丢弃控制器控制旋转的Pitch部分,以便输入值垂直于Player的胶囊体,即始终伸向Player。否则,如果摄像机面朝下,播放器会变慢,因为控件旋转也会朝下。而且我们也只需要Yaw部分用于前置/后置输入。

但是旋转控制已经旋转了!所以我们不能再丢弃Pitch了。

我们的解决方法是,首先将旋转控制从世界变换转换为相对的“局部重力”变换。然后,我们可以像往常一样丢弃Pitch,然后将控制旋转转换回世界变换,然后再将其馈送到Player Pawn。

我们在上一步中添加的代码介绍了几个便捷的蓝图函数,即GetGravityRelativeRotation和GetGravityWorldRotation,它们可以帮助我们做到这一点。

现在就使用它们吧!打开Content/ThirdPerson/Blueprints/BP_ThirdPersonCharacter(如果您愿意,也可以复制它以创建自己的,请记得修改BP_ThirdPersonGameMode),并将输入处理修改为以下内容:

单击图像以查看它的放大版本,或右键单击选择“在新选项卡中打开图片”。

要让Gravity Rotation节点显示三个浮点数,而不是上面屏幕截图中的Rotator,您可以右键单击Rotator Return Value引脚并选择“Split Struct Pin”。

修正动画图表

第三人称Character假定X和Y速度是其行走速度。但是,如果角色旋转,则不成立。我们需要它考虑到所有轴的行走速度。

打开Content/Characters/Mannequins/Animations/ABP_Manny,在这里我们可以修改地面速度计算以使用整个矢量的长度,而不仅仅是X和Y。我们可以修改注释。

修改前:
修改后:

操纵重力

现在进入有趣的部分!您现在可以通过其SetGravityDirection蓝图节点任意修改CharacterMovementComponent的重力!

重力切换器

这就是您拥有一个体积所需的全部内容,当Player与它重叠时,它会改变Player的重力方向。它非常适合将重力倒置,或让Player爬墙。

行星引力

如果您希望Player的重力每帧都改变,例如,使Player的重力始终与另一个对象(如行星),则可以将类似这样的内容放入PlayerCharacter的Tick函数中。“Target Gravity Actor”是一个变量,它包含您想要让Player行走的目标Actor。

全文完

太逼真了!


《荒野机器人》概念图和动画制作幕后


他用PS把Blender模型都涂成了脑洞超大的概念图



CG世界
3D视觉先锋自媒体。内容主要涵盖影视特效,CG动画,前沿CG技术,作品欣赏,游戏引擎,CG软件,行业流程,知识!
 最新文章