控制移动+完善运动+镜头摇臂+自制移动组件+镜头调整

基础内容

Posted by AIaimuti on April 24, 2020

控制移动

功能需求: 能够通过WASD进行简单移动

移动准备

1)先Project Setting添加轴映射–>添加Components/InputComponent.h头文件–>SetupPlayerInputComponent中绑定函数
2).h文件中声明

void MoveForward(float Value);

void MoveRight(float Value);

3).cpp文件中定义
在构造函数中,让0号玩家拥有控制权 AutoPossessPlayer = EAutoReceiveInput::Player0;

移动函数

void ACreature::MoveForward(float Value)
{
	CurrentVelocity.X = FMath::Clamp(Value, -1.f, 1.f)* MaxSpeed;
}

void ACreature::MoveRight(float Value)
{
	CurrentVelocity.Y = FMath::Clamp(Value, -1.f, 1.f)* MaxSpeed;
}

将速度限制到-1到1 * MaxSpeed CurrentVelocity.X = FMath::Clamp(Value, -1.f, 1.f)* MaxSpeed;

FMath::Clamp(Value,min,max) 限制Value介于max和min之间

在Tick函数中

void ACreature::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	//新位置等于当前位置+速度*每帧时间
	FVector NewLocation = GetActorLocation() + CurrentVelocity * DeltaTime;
	SetActorLocation(NewLocation);

}

完善运动

功能需求: 简单的写法没有考虑脸的朝向,这种写法考虑了脸的朝向,角色转身后也是脸的正向
AddInputVector:将给定向量添加到世界空间的累积输入中。输入向量的大小通常在0到1之间。它们在帧中累积,然后在运动更新期间用作加速度。

virtual void AddInputVector
(
    FVector WorldVector,
    bool bForce
)

WorldVector:在世界空间中应用输入的方向
bForce:如果为true,则始终添加输入

GetActorForwardVector:获取Actor在世界空间的正向(X)方向,取值-1,0,1
GetActorRightVector:获取Actor在世界空间的右向(Y)方向,取值-1,0,1

void ACreature::MoveForward(float Value)
{
	if (MovementComp)
	{
		MovementComp->AddInputVector(GetActorForwardVector() * Value);
	}

}

void ACreature::MoveRight(float Value)
{
	if (MovementComp)
	{
		MovementComp->AddInputVector(GetActorRightVector() * Value);
	}
}

摄像机弹簧臂

功能需求: 摄像机动态追踪物体,并且有一定延迟效果。

设置球形碰撞体配置

1).h文件声明

UPROPERTY(VisibleAnywhere)
	class USphereComponent * SphereCollision;

2).cpp配置
添加头文件:
#include “Components/SphereComponent.h”
#include “Components/StaticMeshComponent.h”
#include “Uobject/ConstructorHelpers.h”

//实例化球形组件
SphereCollision = CreateDefaultSubobject<USphereComponent>(TEXT("SphereCollision"));
//设置半径
SphereCollision->SetSphereRadius(80);
//设置初始碰撞类型
SphereCollision->SetCollisionProfileName(TEXT("Block All"));
//设置球形组件是否隐藏,不设置的话默认隐藏
SphereCollision->SetHiddenInGame(false);
//设置根组件
SetRootComponent(SphereCollision);

ConstructorHelpers直接指定并添加静态网格体资源

直接指定网格资源采用如下方式,需要添加Uobject/ConstructorHelpers.h头文件。
TEXT内部引用的路径:打开actor蓝图–>Static Mesh点放大镜打开内容浏览器–>在内容浏览器中右键点击所选物体–>点击Copy Reference

//使用ConstructorHelpers找指定位置的静态网格体资源给MeshComponentAsset
static ConstructorHelpers::FObjectFinder<UStaticMesh>MeshComponentAsset
        (TEXT("StaticMesh'/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere'"));

if (MeshComponentAsset.Succeeded())
	{
        //如果寻找成功,放到静态网格体中,并设置位置
		MeshComponent->SetStaticMesh(MeshComponentAsset.Object);
		MeshComponent->SetRelativeLocation(FVector(0.f, 0, -50));
	}

有时编译好了会没有变化,这时点类设置,换一下蓝图的父类,然后再还回去

摄像机弹簧臂配置

设置了弹簧臂长度、位置以及作为谁的附件,并开启摄像机延迟

SetupAttachment函数

void SetupAttachment
(
    USceneComponent * InParent,
    FName InSocketName
)

InParent:指定依附对象
InSocketName:指定插槽名称

弹簧臂一段连接物体原点,另一端自带插槽。这里将摄像机绑定弹簧臂的插槽,不然摄像机会绑定到弹簧臂的原点 Camera->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);

配置

1).h文件声明

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
	class UCameraComponent * Camera;

UPROPERTY(VisibleAnywhere)
	class USpringArmComponent * SpringArmComp;

2).cpp文件配置
添加头文件:
#include “Camera/CameraComponent.h”
#include “GameFramework/SpringArmComponent.h”

//实例化弹簧臂组件
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
//弹簧臂组件组件附着在根组件
SpringArmComp->SetupAttachment(RootComponent);
//弹簧臂组件长度设为400
SpringArmComp->TargetArmLength = 400;
//设置弹簧臂组件位置
SpringArmComp->SetRelativeRotation(FRotator(-45.f, 0, 0));
//开启摄像机延迟
SpringArmComp->bEnableCameraLag = true;
//设置摄像机延迟速度
SpringArmComp->CameraLagSpeed = 4;

Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
//摄像机绑定弹簧臂,位置是插槽的位置,如果没有指定,摄像机会绑定在弹簧臂自身原点的位置
//弹簧臂的另一端自带插槽
Camera->SetupAttachment(SpringArmComp, USpringArmComponent::SocketName);

自制动作组件

功能需求: 球体碰撞到墙壁时,沿边移动。

自制动作组件准备

1)创建一个继承自UPawnMovementComponent的C++类
class UGDC_API UMyPawnMovementComponent : public UPawnMovementComponent

2)覆写 TickComponent以形成我们的自定义运动组件
从父类依次寻找找到函数定义
UPawnMovementComponent–>UNavMovementComponent–>UMovementComponent
UMovementComponent中有函数TickComponent
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;

3)Creature中声明自制运动组件
Creature.h文件中声明
首先定义UMyPawnMovementComponent类的MovementComp变量;
覆写UPawnMovementComponent的GetMovementComponent()函数,返回我们指定的运动组件来控制Pawn的运动

UPROPERTY(VisibleAnywhere)
    class UMyPawnMovementComponent * MovementComp;

virtual UPawnMovementComponent * GetMovementComponent() const override;

Creature.cpp文件中
实例化继承后的UMyPawnMovementComponent类MovementComp,并指定根组件作为我们移动和更新的标准
GetMovementComponent()中使用我们自定义的移动组件
并添加头文件:
#include “..\Public\MyPawnMovementComponent.h”

MovementComp = CreateDefaultSubobject<UMyPawnMovementComponent>(TEXT("MovementComp"));
//设置移动和更新的标准,以根组件我们移动和更新的标准
MovementComp->UpdatedComponent = RootComponent;
    
UPawnMovementComponent * ACreature::GetMovementComponent() const
{   //使用我们自定义的移动组件
	return MovementComp;
}

自制动作组件内容

条件判断–>获取运动向量–>sweep扫描并实现溜边运动

条件判断

PawnOwner:运动是必须要有Owner的,没有会返回空向量
UpdatedComponent:UpdatedComponent指定以某个组件的运动和更新视为整体的运动和更新,在Creature.cpp 87行设置了以根组件为准
ShouldSkipUpdate(DeltaTime):允许自动跳过更新,有时候没有对Pawn进行操作,就不需要对其更新

//运动是必须要有Owner的,没有会返回空向量
//UpdatedComponent指定以某个组件的运动和更新视为整体的运动和更新,在Creature.cpp 87行设置了以根组件为准
//允许自动跳过更新,有时候没有对Pawn进行操作,就不需要对其更新

if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
{
    return;
}

获取运动向量

ConsumeInputVector():消费者,消费运动向量
GetClampedToMaxSize(1): 约束最大长度为1

//ConsumeInputVector消费运动向量
//GetClampedToMaxSize约束最大长度为1
//功能是获取运动向量的方向,然后按这个方向以150的速度运动
FVector DeltaMovement
    = ConsumeInputVector().GetClampedToMaxSize(1) * DeltaTime * 150;

sweep扫描并实现溜边运动

IsNearlyZero:在指定小数点阈值内输出true,用于容错
SafeMoveUpdatedComponent:进行运动的试算,并开启sweep扫描,有阻挡会输出撞击信息
HitResult.IsValidBlockingHit:检测是有有效的撞击
SlideAlongSurface:延边滑动

virtual FVector ComputeSlideVector
(
    const FVector & Delta,
    const float Time,
    const FVector & Normal,
    const FHitResult & Hit
) const

Delta:尝试移动的方向
Time:通常为1-碰撞时间,即1-Hit.Time
Normal:法向,正常的相对运动方向
Hit:碰撞结果

//如果有运动向量输入,IsNearlyZero在指定小数点阈值内输出true,非0就是有运动向量输入
if (!DeltaMovement.IsNearlyZero())
{
    FHitResult HitResult;
    //采用SafeMoveUpdatedComponent进行运动的试算,其中sweep参数设为true
    SafeMoveUpdatedComponent(DeltaMovement,
        UpdatedComponent->GetComponentRotation(), true, HitResult);
    //如果有有效的阻挡碰撞,就溜边运动,HitResult.Normal法向
    if (HitResult.IsValidBlockingHit())
    {
        SlideAlongSurface(DeltaMovement,
            1 - HitResult.Time, HitResult.Normal, HitResult);
    }
}

调整镜头

功能需求: 可以对视角进行左右和上下调整

调整镜头准备

1)先Project Setting添加轴映射,CameraPitch–>鼠标Y(Y一般是-1),CameraYaw–>鼠标X(X一般是1)
2).h文件中设置

//视角调整函数
void AngleAdjustment();
//获取鼠标Y输入	
void CameraPitch(float Value);
//获取鼠标X输入
void CameraYaw(float Value);
//记录鼠标变化量
FVector2D CameraInput;

3).cpp文件中配置

void ACreature::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	AngleAdjustment();
}
void ACreature::AngleAdjustment()
{
	//获取当前Actor的旋转状态
	FRotator NewRotation = GetActorRotation();
	//将鼠标yaw输入加到NewRotation.Yaw中
	NewRotation.Yaw += CameraInput.X;
	//更新Actor的旋转状态
	SetActorRotation(NewRotation);

	//获取当前弹簧臂的旋转状态
	FRotator NewSpringArmRotation = SpringArmComp->GetComponentRotation();
	//将鼠标pitch输入加到NewRotation.Pitch中
	NewSpringArmRotation.Pitch += CameraInput.Y;
	//将NewRotation.Pitch限制到(-80.f, -15.f)之间
	NewSpringArmRotation.Pitch = FMath::Clamp(NewSpringArmRotation.Pitch, -80.f, -15.f);
	//更新弹簧臂的旋转状态
	SpringArmComp->SetWorldRotation(NewSpringArmRotation);
}


void ACreature::CameraPitch(float Value)
{
	//获取鼠标Y输入
	CameraInput.Y = Value;
}

void ACreature::CameraYaw(float Value)
{
	//获取鼠标X输入
	CameraInput.X = Value;
}