AdvancedLocomotionSystemV第二篇 C++ 实现镜头跟随人物缓慢移动

Posted Tanzq*

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AdvancedLocomotionSystemV第二篇 C++ 实现镜头跟随人物缓慢移动相关的知识,希望对你有一定的参考价值。

效果展示


上篇那样做,镜头和人物走着走着就会跑偏,所以这章完成镜头时刻紧跟着角色。
首先要将更新镜头位置的这个函数删除,之后会换成C++的代码。

创建接口

接口的使用我在之前的文章中讲过了, 所以这里就不讲啦。【【UE4 C++】如何使用C++接口

命名为:ALS_Camera

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "ALS_Camera.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UALS_Camera : public UInterface
{
	GENERATED_BODY()
};

/**
 * 
 */
class ALSVLEARNDEMO_API IALS_Camera
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:

	/**
	 * @brief 返回Actor的Transform变量
	 * 包含所在位置、旋转、缩放等信息。
	 */
	UFUNCTION(BlueprintNativeEvent)
	FTransform Get3PPivotTarget();

	virtual FTransform Get3PPivotTarget_Implementation();

	/**
	 * @brief 获得摄像机的一些视场角属性,视场角越大视野就越大。
	 * @param TP_FOV 第三人称视场角
	 * @param FP_FOV 第一人称市场角
	 * @return 是否是右边位置。
	 */
	UFUNCTION(BlueprintNativeEvent)
	bool GetCameraParameters(float& TP_FOV, float& FP_FOV);

	virtual bool GetCameraParameters_Implementation(float& TP_FOV, float& FP_FOV);
};

源文件这边就默认定义一下就好了。
注释中有说到这个源文件的主要作用就是为任何非纯虚函数添加默认功能。

#include "ALS_Camera.h"

// Add default functionality here for any IALS_Camera functions that are not pure virtual.
FTransform IALS_Camera::Get3PPivotTarget_Implementation()
{
	return {};
}

bool IALS_Camera::GetCameraParameters_Implementation(float& TP_FOV, float& FP_FOV)
{
	return false;
}

在角色类中进行接口的实现

在角色的基类中继承接口,并重新实现接口函数。

// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Interfaces/ALS_Camera.h"
#include "ALSVLearnDemoCharacter.generated.h"

UCLASS(config=Game)
class AALSVLearnDemoCharacter : public ACharacter, public IALS_Camera // 继承接口
{
	GENERATED_BODY()

public:
	
	……
	……
	……

	/
	/// 摄像机相关变量、函数

	UPROPERTY(Category="Camera System", EditInstanceOnly, BlueprintReadWrite)
	float ThirdPersonFOV;

	UPROPERTY(Category="Camera System", EditInstanceOnly, BlueprintReadWrite)
	float FirstPersonFOV;

	UPROPERTY(Category="Camera System", EditInstanceOnly, BlueprintReadWrite)
	bool bRightShoulder;

	/
	/// 接口相关变量、函数

	// 重载接口
	virtual bool GetCameraParameters_Implementation(float& TP_FOV, float& FP_FOV) override;
};

就是将参数传到接口中。

// Copyright Epic Games, Inc. All Rights Reserved.

#include "ALSVLearnDemoCharacter.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"

//
// AALSVLearnDemoCharacter

AALSVLearnDemoCharacter::AALSVLearnDemoCharacter()
{
	……
	……
	……

	ThirdPersonFOV = 90.f;
	FirstPersonFOV = 90.f;
}


……
……
……


bool AALSVLearnDemoCharacter::GetCameraParameters_Implementation(float& TP_FOV, float& FP_FOV)
{
	TP_FOV = ThirdPersonFOV;
	FP_FOV = FirstPersonFOV;
	return bRightShoulder;
}

然后再到ALS_AnimMan_Character角色中将另一个函数进行重载实现。

#pragma once

#include "CoreMinimal.h"
#include "../ALSVLearnDemoCharacter.h"
#include "ALS_AnimMan_Character.generated.h"

/**
 * 
 */
UCLASS()
class ALSVLEARNDEMO_API AALS_AnimMan_Character : public AALSVLearnDemoCharacter
{
	GENERATED_BODY()
public:
	
	//
	/// 接口相关函数

	virtual FTransform Get3PPivotTarget_Implementation() override;
};

源文件:

#include "ALS_AnimMan_Character.h"

FTransform AALS_AnimMan_Character::Get3PPivotTarget_Implementation()
{
	// 使用具体的两个插槽计算角色的中心点更加精准。
	// 比如说胶囊体不会缩小,当角色蹲下的时候,我们获取人物的位置的时候并不会发生改变,
	// 			但如果是根据骨骼位置然后求中心点的话,这个位置就会跟着发生改变。
	const FVector Location = (GetMesh()->GetSocketLocation(TEXT("head")) + GetMesh()->GetSocketLocation(TEXT("root")))
		/ 2.f;
	
	return FTransform(GetActorRotation(), Location, FVector(1.f, 1.f, 1.f));
}

添加摄像机移动逻辑

重载UpdateViewTargetInternal更新摄像机位置。

#pragma once

#include "CoreMinimal.h"
#include "Camera/PlayerCameraManager.h"
#include "ALS_CameraManager.generated.h"

class UALS_PlayerCameraBehavior;
/**
 * 
 */
UCLASS()
class ALSVLEARNDEMO_API AALS_CameraManager : public APlayerCameraManager
{
	GENERATED_BODY()

public:
	AALS_CameraManager();
	
	UFUNCTION(BlueprintCallable)
	void OnPossess(APawn* NewPawn);

	// 更新摄像机位置的函数
	virtual void UpdateViewTargetInternal(FTViewTarget& OutVT, float DeltaTime) override;
	
	/**
	 * @brief 自定义摄像机的信息
	 * @param Location 摄像机的位置
	 * @param Rotation 摄像机的旋转值
	 * @param FOV 摄像机镜头所覆盖的位置
	 */
	UFUNCTION(BlueprintCallable)
	void CustomCameraBehavior(FVector& Location, FRotator& Rotation, float& FOV);
	
	/**
	 * @brief 获取摄像机曲线的值
	 */
	UFUNCTION(BlueprintCallable)
	float GetCameraBehaviorParam(FName CurveName);

	/**
	 * @brief 计算轴无关延迟
	 * @param CurrentLocation 目前位置
	 * @param TargetLocation  目标位置
	 * @param CameraRotation  摄像机位置
	 * @param LagSpeeds  延迟速度
	 * @return 延迟之后的位置
	 */
	UFUNCTION(BlueprintCallable)
	FVector CalculateAxisIndependentLag(FVector CurrentLocation, FVector TargetLocation, FRotator CameraRotation, FVector LagSpeeds);
	
	
	/**
	 * @brief 摄像机将要旋转到的旋转度  也就相当于当前摄像机的旋转值
	 */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	FRotator TargetCameraRotation;

	/**
	 * @brief 摄像机将要移动到的位置 相当于当前摄像机的位置
	 */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	FVector TargetCameraLocation;

	/**
	 * @brief 平滑目标锚点信息
	 */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	FTransform SmoothedPivotTarget;

	/**
	 * @brief 设置锚点位置信息
	 */
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
	FVector PivotLocation;
	
	
	/// 组件相关属性
	
	UFUNCTION(Category="Components", BlueprintCallable)
	USkeletalMeshComponent* GetCameraBehaviorComp() const { return CameraBehaviorComp; }
	
	///
	/// 对一些类的引用

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	APawn* ControllerPawn;

	UPROPERTY()
	UALS_PlayerCameraBehavior* PlayerCameraBehavior;
	
private:
	/**
	 * @brief 添加摄像机骨骼,这样的话,人物运行状态机的时候摄像机也会进行状态机,这样就会有一个相对高的匹配度。
	 */
	UPROPERTY(Category="Components", VisibleAnywhere, BlueprintReadOnly, meta=(AllowPrivateAccess = "true"))
	USkeletalMeshComponent* CameraBehaviorComp;
};

源文件:

// Fill out your copyright notice in the Description page of Project Settings.


#include "ALS_CameraManager.h"

#include "ALS_PlayerCameraBehavior.h"
#include "ALSVLearnDemo/Interfaces/ALS_Camera.h"
#include "Kismet/KismetMathLibrary.h"

AALS_CameraManager::AALS_CameraManager()
{
	CameraBehaviorComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CameraBehaviorComp"));
	CameraBehaviorComp->SetupAttachment(RootComponent);
}

/**
 * @brief 摄像机创建了之后,控制器会给他一个所控制的对象,如果这个对象具有 ”ALS_Character“ 标签的话,使用自己定义的摄像机移动逻辑。没有就用父类的。
 */
void AALS_CameraManager::UpdateViewTargetInternal(FTViewTarget& OutVT, float DeltaTime)
{
	if (!IsValid(OutVT.Target)) return;
	
	if (OutVT.Target->ActorHasTag(TEXT("ALS_Character")))
	{
		CustomCameraBehavior(OutVT.POV.Location, OutVT.POV.Rotation, OutVT.POV.FOV);
	}
	else
	{
		Super::UpdateViewTargetInternal(OutVT, DeltaTime);
	}
}

void AALS_CameraManager::OnPossess(APawn* NewPawn)
{
	ControllerPawn = NewPawn;

	//
	/// 第二篇更新: 引用动画实例,将玩家控制器、控制的Pawn放入蓝图设置。

	PlayerCameraBehavior = Cast<UALS_PlayerCameraBehavior>(CameraBehaviorComp->GetAnimInstance());
	
	//
}

void AALS_CameraManager::CustomCameraBehavior(FVector& Location, FRotator& Rotation, float& FOV)
{
	//
	/// 第二篇更新:  自定义摄像机移动方式

	IALS_Camera* CameraInterface = Cast<IALS_Camera>(ControllerPawn);
	if (!CameraInterface) return;
	
	// 使用接口函数,获取轴心位置、 第一、三人称镜头所覆盖的范围
	// FOV是指镜头所能覆盖的范围,
	const FTransform PivotTarget = CameraInterface->Execute_Get3PPivotTarget(ControllerPawn);
	float TP_FOV, FP_FOV;
	CameraInterface->Execute_GetCameraParameters(ControllerPawn, TP_FOV, FP_FOV);

	// 更新摄像机目标旋转度的值
	const FRotator CurrentRotation = GetCameraRotation();
	const FRotator TargetRotation = GetOwningPlayerController()->GetControlRotation();
	TargetCameraRotation = FMath::RInterpTo(CurrentRotation, TargetRotation, GetWorld()->DeltaTimeSeconds,
	                                        GetCameraBehaviorParam(TEXT("RotationLagSpeed")));

	// 添加了 PivotLagSpeed 值之后,摄像机左右移动时会有一个平滑过渡的效果,向左向右移动,人物停止移动之后镜头得过一会才会停止。
 	// 更新平滑目标平滑锚点位置信息 
	const FVector LagLocation = CalculateAxisIndependentLag(SmoothedPivotTarget.GetTranslation(),
	                                                        PivotTarget.GetTranslation(),
	                                                        TargetCameraRotation,
	                                                        FVector(GetCameraBehaviorParam(TEXT("PivotLagSpeed_X")),
	                                                                GetCameraBehaviorParam(TEXT("PivotLagSpeed_Y")),
	                                                                GetCameraBehaviorParam(TEXT("PivotLagSpeed_Z"))));
	SmoothedPivotTarget = FTransform(PivotTarget.GetRotation(), LagLocation, FVector(1.f, 1.f, 1.f));

	// 设置局部轴心偏移
	FVector ForwardVector = SmoothedPivotTarget.GetRotation().GetForwardVector() * GetCameraBehaviorParam(TEXT("PivotOffset_X"));
	FVector RightVector = SmoothedPivotTarget.GetRotation().GetRightVector() * GetCameraBehaviorParam(TEXT("PivotOffset_Y"));
	FVector UpVector = SmoothedPivotTarget.GetRotation().GetUpVector() * GetCameraBehaviorParam(TEXT("PivotOffset_Z"));
	PivotLocation = SmoothedPivotTarget.GetTranslation() + ForwardVector + RightVector + UpVector;

	// CameraOffset 存储的是摄像机与对象之间的位置偏差值。 就相当于之前用的角色后面跟随的摄像头的相对位置。
	// 设置局部摄像机偏移位置
	ForwardVector = TargetCameraRotation.Vector() * GetCameraBehaviorParam(TEXT("CameraOffset_X"));
	RightVector = UKismetMathLibrary::GetRightVector(TargetCameraRotation) * GetCameraBehaviorParam(TEXT("CameraOffset_Y"));
	UpVector = UKismetMathLibrary::GetUpVector(TargetCameraRotation) * GetCameraBehaviorParam(TEXT("CameraOffset_Z"));
	TargetCameraLocation = PivotLocation + ForwardVector + RightVector + UpVector;

	// step 6 TODO DEBUG


	Location = TargetCameraLocation;
	Rotation = TargetCameraRotation;
	FOV = TP_FOV;

	//
}

float AALS_CameraManager::GetCameraBehaviorParam(FName CurveName)
{
	if (PlayerCameraBehavior)
	{
		return PlayerCameraBehavior->GetCurveValue(CurveName);
	}
	return 0.f;
}

FVector AALS_CameraManager::CalculateAxisIndependentLag(FVector CurrentLocation, FVector TargetLocation,
                                                        FRotator CameraRotation, FVector LagSpeeds)
{
	// 获取摄像头的横向旋转角度
	const FRotator CameraRotationYaw(0.f, CameraRotation.Yaw, 0.f);

	const FVector CurrentLocation_UnRotateVector = CameraRotationYaw.UnrotateVector(CurrentLocation);
	const FVector TargetLocation_UnRotateVector = CameraRotationYaw.UnrotateVector(TargetLocation);

	FVector Location;
	Location.X = FMath::FInterpTo(CurrentLocation_UnRotateVector.X, TargetLocation_UnRotateVector.X,
	                              GetWorld()->DeltaTimeSeconds, LagSpeeds.X);
	Location.Y = FMath::FInterpTo(CurrentLocation_UnRotateVector.Y, TargetLocation_UnRotateVector.Y,
	                              GetWorld()->DeltaTimeSeconds, LagSpeeds.Y);
	Location.Z = FMath::FInterpTo(CurrentLocation_UnRotateVector.Z, TargetLocation_UnRotateVector.Z,
	                              GetWorld()->DeltaTimeSeconds, LagSpeeds.Z);

	return CameraRotationYaw.RotateVector(Location);
}

函数设计思路讲解:

在函数里面为什么要加UnrotatorVector 和 RotatorVector,想了好久都不知道为什么,如果有大佬知道这个希望您能够在评论区评论一下。

编译运行之后将摄像机骨骼加载进去。

创建动画蓝图

先创建一个UAnimInstance的C++类:ALS_PlayerCameraBehavior
先放在一边,之后会用到。

右键


修改参数,运行就完成啦!

以上是关于AdvancedLocomotionSystemV第二篇 C++ 实现镜头跟随人物缓慢移动的主要内容,如果未能解决你的问题,请参考以下文章

AdvancedLocomotionSystemV第八篇 C++ 实现角色翻滚和跳跃动作

AdvancedLocomotionSystemV第八篇 C++ 实现角色翻滚和跳跃动作

AdvancedLocomotionSystemV第八篇 C++ 实现角色翻滚和跳跃动作

AdvancedLocomotionSystemV第七篇 C++ 实现角色蹲伏和跑步细节

AdvancedLocomotionSystemV第七篇 C++ 实现角色蹲伏和跑步细节

AdvancedLocomotionSystemV第二篇 C++ 实现镜头跟随人物缓慢移动