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

效果展示

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

查看所有文章

AdvancedLocomotionSystemV

创建接口

接口的使用我在之前的文章中讲过了, 所以这里就不讲啦。【【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,想了好久都不知道为什么,如果有大佬知道这个希望您能够在评论区评论一下。


2021年10月31日
问题已经解决:
【UE4】高级运动系统 在Calculate Axis Independent Lag 函数中作者为什么要使用 UnrotateVector 和 UnrotateVector 增加向量值?


编译运行之后将摄像机骨骼加载进去。
在这里插入图片描述

创建动画蓝图

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

右键
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
修改参数,运行就完成啦!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值