custom c++ 3rd person template, how to manually reset the camera behind the Character?

Hi, I have a question on how to reset the camera behind the character with a custom c++ 3rd person template.

First here how my c++ 3rd person template are setup.

I customize the 3rd person camera to have the Yaw rotation of the Character align with the camera Yaw rotation and mouse rotation X axis. I also make the character strafe with left and right arrow (or A and D key) instead of rotating the Yaw Character rotation. So up to this point, I achieved all this by simply changing 2 variable default value in the My own ACharacter class constructor.


bUseControllerRotationYaw = true;
GetCharacterMovement()->bOrientRotationToMovement = false;

On top of this I’m trying to implemented a free camera mod, where the pawn now only move with the arrow key (or wasd) and the camera is turning freely around the character. To do so I bounded a action on the right mouse button click and a other one on the release of the same button. Here the code for those 2 action function I added and the modification on the 2 function for movement action (MoveForward and MoveRight) I did to make this work.



void AATPreProtoCharacter::MoveForward(float Value)
{
	if (!bUseControllerRotationYaw)
	{
		// find out which way is forward
		const FRotator Rotation = GetActorRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get forward vector
		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
		AddMovementInput(Direction, Value);
	}
	else
	{
		if ((Controller != NULL) && (Value != 0.0f))
		{
			// find out which way is forward
			const FRotator Rotation = Controller->GetControlRotation();
			const FRotator YawRotation(0, Rotation.Yaw, 0);

			// get forward vector
			const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
			AddMovementInput(Direction, Value);
		}
	}
}

void AATPreProtoCharacter::MoveRight(float Value)
{
	if (!bUseControllerRotationYaw)
	{
		// find out which way is right
		const FRotator Rotation = GetActorRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get right vector 
		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
		// add movement in that direction
		AddMovementInput(Direction, Value);
	}
	else
	{
		if ((Controller != NULL) && (Value != 0.0f))
		{
			// find out which way is right
			const FRotator Rotation = Controller->GetControlRotation();
			const FRotator YawRotation(0, Rotation.Yaw, 0);

			// get right vector 
			const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
			// add movement in that direction
			AddMovementInput(Direction, Value);
		}
	}
}

//Action bound to the Right mouse button press
void AATPreProtoCharacter::CameraOnly() 
{
	bUseControllerRotationYaw = false;
}

//Action bound to the Right mouse button release
void AATPreProtoCharacter::StopCameraOnly()
{
	bUseControllerRotationYaw = true;
}


As you can see its very simple. But the problems with this part is that my Character then automatically rotate to camera Yaw rotation. What I want is the opposite, the cameras automatically rotate back to character Yaw rotation.

After searching a lot on the forum and the answer hub a came with the fallowing solution.


void AATPreProtoCharacter::StopCameraOnly()
{
	if (Controller != NULL) 
	{
		const FRotator CameraRotation = CameraBoom->GetComponentRotation();
		const FRotator ActorRotation = GetActorRotation();
		/*const FRotator TempRotation(0.f, 90.0f, 0.f);
		
		FQuat AQuat = FQuat(ActorRotation);
		FQuat BQuat = FQuat(TempRotation);

		const FRotator NewRotation(BQuat*AQuat);*/

		FRotator TargetRotation = ActorRotation - CameraRotation;
		TargetRotation.Normalize();

		AddControllerYawInput(TargetRotation.Yaw);
	}

	bUseControllerRotationYaw = true;
}

But all my attempt as fail so fare, I cannot get the right calculation to set the camera rotation behind the character with the AddControllerYawInput(float) function. All what I have try result in the character and the camera pointing on the wrong (random) direction in the end.

So, is someone have a solution to get the right float value to point to my character rotation with the AddControllerYawInput? Because this don’t work for me yet (also tested with different value for the temp rotation variable and without it).



const FRotator CameraRotation = CameraBoom->GetComponentRotation();
const FRotator ActorRotation = GetActorRotation();
const FRotator TempRotation(0.f, 90.0f, 0.f);

FQuat AQuat = FQuat(ActorRotation);
FQuat BQuat = FQuat(TempRotation);
const FRotator NewRotation(BQuat*AQuat);

FRotator TargetRotation = NewRotation - CameraRotation;
TargetRotation.Normalize();

ddControllerYawInput(TargetRotation.Yaw);


Here the complete code of my own character c++ class if it can help some one:

.cpp



// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#include "ATPreProto.h"
#include "ATPreProtoCharacter.h"
#include "Kismet/GameplayStatics.h"

//////////////////////////////////////////////////////////////////////////
// AATPreProtoCharacter

AATPreProtoCharacter::AATPreProtoCharacter(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

	// set our turn rates for input
	BaseTurnRate = 45.f; // DEFAULT 45
	BaseLookUpRate = 45.f; // DEFAULT 45

	// Set our zoom properties
	MaxZoom = 800;
	MinZoom = 200.f;
	ClipZoom = 100.f;
	TargetZoom = 400.f;

	// Don't rotate when the controller rotates. Let that just affect the camera.
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = true; // DEFAULT FALSE
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = false; // Character moves in the direction of input... DEFAULT TRUE
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
	GetCharacterMovement()->JumpZVelocity = 300.f; // DEFAULT 600
	GetCharacterMovement()->AirControl = 1.0f; // DEFAULT 0.2

	// Create a camera boom (pulls in towards the player if there is a collision)
	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->AttachTo(RootComponent);
	CameraBoom->TargetArmLength = TargetZoom; // The camera follows at this distance behind the character	DEFAULT 300
	CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
	
	// Create a follow camera
	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->AttachTo(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation
	FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm
	
	// Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) 
	// are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}

//////////////////////////////////////////////////////////////////////////
// Input

void AATPreProtoCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	// Set up gameplay key bindings
	check(InputComponent);
	InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
	InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);

	InputComponent->BindAction("CameraOnly", IE_Pressed, this, &AATPreProtoCharacter::CameraOnly);
	InputComponent->BindAction("CameraOnly", IE_Released, this, &AATPreProtoCharacter::StopCameraOnly);

	InputComponent->BindAction("ZoomIn", IE_Pressed, this, &AATPreProtoCharacter::ZoomIn);
	InputComponent->BindAction("ZoomOut", IE_Pressed, this, &AATPreProtoCharacter::ZoomOut);

	InputComponent->BindAxis("MoveForward", this, &AATPreProtoCharacter::MoveForward);
	InputComponent->BindAxis("MoveRight", this, &AATPreProtoCharacter::MoveRight);

	// We have 2 versions of the rotation bindings to handle different kinds of devices differently
	// "turn" handles devices that provide an absolute delta, such as a mouse.
	// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
	InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
	InputComponent->BindAxis("TurnRate", this, &AATPreProtoCharacter::TurnAtRate);
	InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
	InputComponent->BindAxis("LookUpRate", this, &AATPreProtoCharacter::LookUpAtRate);

	// handle touch devices
	InputComponent->BindTouch(IE_Pressed, this, &AATPreProtoCharacter::TouchStarted);
	InputComponent->BindTouch(IE_Released, this, &AATPreProtoCharacter::TouchStopped);
}


void AATPreProtoCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
	// jump, but only on the first touch
	if (FingerIndex == ETouchIndex::Touch1)
	{
		Jump();
	}
}

void AATPreProtoCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
	if (FingerIndex == ETouchIndex::Touch1)
	{
		StopJumping();
	}
}

void AATPreProtoCharacter::TurnAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}

void AATPreProtoCharacter::LookUpAtRate(float Rate)
{
	// calculate delta for this frame from the rate information
	AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}

void AATPreProtoCharacter::MoveForward(float Value)
{
	if (!bUseControllerRotationYaw)
	{
		// find out which way is forward
		const FRotator Rotation = GetActorRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get forward vector
		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
		AddMovementInput(Direction, Value);
	}
	else
	{
		if ((Controller != NULL) && (Value != 0.0f))
		{
			// find out which way is forward
			const FRotator Rotation = Controller->GetControlRotation();
			const FRotator YawRotation(0, Rotation.Yaw, 0);

			// get forward vector
			const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
			AddMovementInput(Direction, Value);
		}
	}
}

void AATPreProtoCharacter::MoveRight(float Value)
{
	if (!bUseControllerRotationYaw)
	{
		// find out which way is right
		const FRotator Rotation = GetActorRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get right vector 
		const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
		// add movement in that direction
		AddMovementInput(Direction, Value);
	}
	else
	{
		if ((Controller != NULL) && (Value != 0.0f))
		{
			// find out which way is right
			const FRotator Rotation = Controller->GetControlRotation();
			const FRotator YawRotation(0, Rotation.Yaw, 0);

			// get right vector 
			const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
			// add movement in that direction
			AddMovementInput(Direction, Value);
		}
	}
}

void AATPreProtoCharacter::CameraOnly()
{
	bUseControllerRotationYaw = false;
}

void AATPreProtoCharacter::StopCameraOnly()
{
	if (Controller != NULL) 
	{
		const FRotator CameraRotation = CameraBoom->GetComponentRotation();
		const FRotator ActorRotation = GetActorRotation();
		/*const FRotator TempRotation(0.f, 90.0f, 0.f);
		
		FQuat AQuat = FQuat(ActorRotation);
		FQuat BQuat = FQuat(TempRotation);

		const FRotator NewRotation(BQuat*AQuat);*/

		FRotator TargetRotation = ActorRotation - CameraRotation;
		TargetRotation.Normalize();

		AddControllerYawInput(TargetRotation.Yaw);
	}

	bUseControllerRotationYaw = true;
}

void AATPreProtoCharacter::ZoomIn()
{
	if (TargetZoom > MinZoom)
	{
		TargetZoom -= ClipZoom;

		if (TargetZoom < MinZoom)
		{
			TargetZoom = MinZoom;
		}
	}
}

void AATPreProtoCharacter::ZoomOut()
{
	if (TargetZoom < MaxZoom)
	{
		TargetZoom += ClipZoom;

		if (TargetZoom > MaxZoom)
		{
			TargetZoom = MaxZoom;
		}
	}
}

void AATPreProtoCharacter::Tick(float deltaTime)
{
	Super::Tick(deltaTime);

	CheckZoom(deltaTime);
}

void AATPreProtoCharacter::CheckZoom(float deltaTime)
{
	if (CameraBoom != NULL)
	{
		CameraBoom->TargetArmLength = (FMath::FInterpTo(CameraBoom->TargetArmLength, TargetZoom, deltaTime, 3.0f));
	}
}


h.



// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/Character.h"
#include "ATPreProtoCharacter.generated.h"


UCLASS(config=Game)
class AATPreProtoCharacter : public ACharacter
{
	GENERATED_BODY()

	/** Camera boom positioning the camera behind the character */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	class USpringArmComponent* CameraBoom;

	/** Follow camera */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
	class UCameraComponent* FollowCamera;

public:
	AATPreProtoCharacter(const FObjectInitializer& ObjectInitializer);

	/** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
	float BaseTurnRate;

	/** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera)
	float BaseLookUpRate;

	/** Base zoom properties */
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float MaxZoom;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float MinZoom;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float ClipZoom;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera)
	float TargetZoom;

protected:

	/** Called for forwards/backward input */
	void MoveForward(float Value);

	/** Called for side to side input */
	void MoveRight(float Value);

	/** 
	 * Called via input to turn at a given rate. 
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void TurnAtRate(float Rate);

	/**
	 * Called via input to turn look up/down at a given rate. 
	 * @param Rate	This is a normalized rate, i.e. 1.0 means 100% of desired turn rate
	 */
	void LookUpAtRate(float Rate);

	/** Handler for when a touch input begins. */
	void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);

	/** Handler for when a touch input stops. */
	void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);

	/** Called for rotation of camera only (no character roation) */
	void CameraOnly();

	/** Called to stop the rotation of camera only (character can rotate) */
	void StopCameraOnly();

	/** Called to zoom camera in */
	void ZoomIn();

	/** Called to zoom camera out */
	void ZoomOut();

	/** Override tick function for smoth camera zoom */
	void Tick(float deltaTime) override;

	/** Called for smoth camera zoom */
	void CheckZoom(float deltaTime);

protected:
	// APawn interface
	virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;
	// End of APawn interface

public:
	/** Returns CameraBoom subobject **/
	FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
	/** Returns FollowCamera subobject **/
	FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};