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 (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 (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模型都涂成了脑洞超大的概念图