While converting my player class to C++, I was having trouble implementing a zoom feature which prior to conversion, worked perfectly fine in the Blueprint version. After a little too much trouble, I decided to strip all relevant code out of my player class, and copy/paste the code from the C++ Third Person template. For whatever reason, this did not change anything…
Now here’s the weird part:
If I use the mouse wheel to zoom in/out, then Alt+Tab
into the editor with the game running, select my player class from the World Outliner, and take a peek at the SpringArm component, I can see the value of TargetArmLength
being updated, but there is no visible change in the actual game. Even stranger than that is when I manually edit the value in the details panel, then it actually does visibly show the changes…
WTF? Why!?
My project’s input settings look like this:
And here’s the stripped-down version of my player class:
Player.h
#pragma once
#include "GameFramework/SpringArmComponent.h"
#include "Player.generated.h"
class ABasePlayer;
UCLASS(Config = Game)
class APlayer : public ABasePlayer // NOTE: ABasePlayer is currently an empty class deriving from ACharacter
{
GENERATED_BODY()
/* Private Components */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* CameraBoom;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
public:
/* Class Constructor */
ATOPAvatarPlayer();
/* Overrides */
virtual void SetupPlayerInputComponent(UInputComponent *InInputComponent) override;
virtual void BeginPlay() override;
/* Input */
void Turn(float Value);
void LookUp(float Value);
void Zoom(float Value);
/* Public Class Functions */
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
/* Exposed Variables */
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MinimumTurnAngle;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MaximumTurnAngle;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MinimumLookUpAngle;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MaximumLookUpAngle;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MinimumZoom;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
float MaximumZoom;
};
Player.cpp:
#include "MyGame.h"
#include "BasePlayer.h"
#include "Player.h"
/*
* Class Constructor
* =================
*/
APlayer::APlayer()
{
this->PrimaryActorTick.Target = this;
this->PrimaryActorTick.bCanEverTick = true;
this->PrimaryActorTick.bStartWithTickEnabled = true;
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
bUseControllerRotationPitch = true;
bUseControllerRotationYaw = true;
bUseControllerRotationRoll = false;
GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->AttachTo(RootComponent);
CameraBoom->TargetArmLength = 500.0f; // The camera follows at this distance behind the character
CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller
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
/* Default Class Variables */
MinimumTurnAngle = -89.5;
MaximumTurnAngle = 89.5;
MinimumLookUpAngle = -90.0;
MaximumLookUpAngle = -0.15;
MinimumZoom = 500.0;
MaximumZoom = 1500.0;
}
/*
* Input Functions
* ===============
*/
void APlayer::Turn(float Value)
{
if (Controller && (Value != 0.0f)) {
auto Rotation = Controller->GetControlRotation();
auto DesiredAngle = Value + Rotation.Yaw;
auto MinAngle = MinimumTurnAngle;
auto MaxAngle = MaximumTurnAngle;
auto NewYaw = FMath::ClampAngle(DesiredAngle, MinAngle, MaxAngle);
auto NewRotation = FRotator::MakeFromEuler(FVector(0.f, Rotation.Pitch, NewYaw));
Controller->SetControlRotation(NewRotation);
/* Just as expected, this message always displays when I move the mouse horizontally */
GEngine->AddOnScreenDebugMessage(-1, 0.1f, FColor::Red, Controller->GetControlRotation().ToCompactString());
}
}
void APlayer::LookUp(float Value)
{
if (Controller && (Value != 0.0f)) {
auto Rotation = Controller->GetControlRotation();
auto DesiredAngle = Value + Rotation.Pitch;
auto NewPitch = FMath::ClampAngle(DesiredAngle, MinimumLookUpAngle, MaximumLookUpAngle);
auto NewRotation = FRotator::MakeFromEuler(FVector(0.f, NewPitch, Rotation.Yaw));
Controller->SetControlRotation(NewRotation);
/* Just as expected, this message always displays when I move the mouse vertically */
GEngine->AddOnScreenDebugMessage(-1, 0.1f, FColor::Red, Controller->GetControlRotation().ToCompactString());
}
}
void APlayer::Zoom(float Value)
{
auto NewZoom = FMath::Clamp(CameraBoom->TargetArmLength + Value, MinimumZoom, MaximumZoom);
if (NewZoom != CameraBoom->TargetArmLength && GEngine) {
CameraBoom->TargetArmLength = NewZoom;
/* Just as expected, this message always displays when I use the mouse wheel */
GEngine->AddOnScreenDebugMessage(-1, 0.1f, FColor::Red, FString::FromInt(CameraBoom->TargetArmLength));
}
}
/*
* Overrided Functions
* ===================
*/
void APlayer::SetupPlayerInputComponent(UInputComponent *InInputComponent)
{
this->InputComponent->BindAxis("Turn", this, &APlayer::Turn);
this->InputComponent->BindAxis("LookUp", this, &APlayer::LookUp);
this->InputComponent->BindAxis("Zoom", this, &APlayer::Zoom);
}
void APlayer::BeginPlay()
{
/* Dirty hack to get the Tick function to work in this class */
auto level = GetWorld()->GetLevel(0);
this->PrimaryActorTick.RegisterTickFunction(level);
}