It drives me crazy when certain games don’t add any Q/E leaning or the ability to swap your shoulder manually, and this is the first time in my life I’ve ever been in a position or remotely had the knowledge to actually do something about this haha.
I was moving the Spring Arm’s socket offset back and forth trying to find a nice over-the-shoulder view, and noticed that as long as I center the spring arm itself, the offsets on the Y axis are more or less a mirror. So I thought I could probably make a swap toggle. But then it’s pretty jarring if you don’t smooth it out, so I tried to interpolate it as well.
Header declarations:
// ----------------------------------
// Camera.
UPROPERTY()
USpringArmComponent* CameraSpringArm;
void SwapShoulder();
void MoveToShoulder(float DeltaTime);
bool bRightShoulder = true;
float RightShoulderOffset;
float LeftShoulderOffset;
UPROPERTY(EditAnywhere, Category="View")
float ShoulderSwapSpeed = 10;
Then we start implementing. First we’ll get the spring arm, and set the two target offsets we want.
// --------------------------------------------------------------
// Called when the game starts or when spawned
void AShooterCharacter::BeginPlay()
{
Super::BeginPlay();
// Get reference to the spring arm so we can shoulder swap.
CameraSpringArm = Cast<USpringArmComponent>(GetComponentByClass(USpringArmComponent::StaticClass()));
// As long as the spring arm in the BP is centered around the player,
// we can just multiply it's existing offset by -1 to swap it.
RightShoulderOffset = CameraSpringArm->SocketOffset.Y;
LeftShoulderOffset = RightShoulderOffset * -1;
}
Add input binding for the toggle:
PlayerInputComponent->BindAction(TEXT("SwapShoulder"), IE_Pressed, this, &AShooterCharacter::SwapShoulder);
A method to handle swapping our boolean back and forth when we hit our keybind:
// --------------------------------------------------------------
/// Swap over-the-shoulder view between left and right, toggled by input.
void AShooterCharacter::SwapShoulder()
{
if (bRightShoulder) {
bRightShoulder = false;
}
else {
bRightShoulder = true;
}
}
Then the function that’s called on Tick so it’s smooth:
// --------------------------------------------------------------
/// Interpolate towards desired shoulder offset.
void AShooterCharacter::MoveToShoulder(float DeltaTime)
{
float CurrentPos = CameraSpringArm->SocketOffset.Y;
if (bRightShoulder) {
CameraSpringArm->SocketOffset.Y = FMath::FInterpTo(CurrentPos, RightShoulderOffset, DeltaTime, ShoulderSwapSpeed);
}
else {
CameraSpringArm->SocketOffset.Y = FMath::FInterpTo(CurrentPos, LeftShoulderOffset, DeltaTime, ShoulderSwapSpeed);
}
}
And that’s it!
I don’t know if setting the SocketOffset every frame like that would be a performance consideration or not. If it were, I’m not sure how to selectively call it. Maybe every time you hit toggle, a timer starts and calls MoveToShoulder() every frame until the timer stops, once the transition has been made. Something like that?