Camera Shake
Github Link: https://github.com/Harrison1/unrealcpp/tree/master/CameraShake
For this tutorial we are using the standard first person C++ template with the starter content.
In this Unreal Engine 4 C++ tutorial we'll learn how to shake the camera every time we fire the gun.
First start by creating a new Camera Shake class and name it MyCameraShake
. You'll have to click Show all classes to select the CameraShake
class.
Add Camera Shake
Next, go into the .h
file and add the constructor function so we can use in the .cpp
file.
Add Constructor to Header
UCLASS()
class UNREALCPP_API UMyCameraShake : public UCameraShake
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
UMyCameraShake();
};
Next, enter the .cpp
file to set the values for MyCameraShake
. In the constructor function we'll set our default values. The default values we define will give the player a random violent shake. You'll adjust the numbers depending on your preference. Here is the link to the CameraShake
class in the UE4 documentation that provides all of it's variables and functions. We'll use FMath::RandRange
to provide a random shake every time that affects the Pitch
and Yaw
values.
Constructor Function
...
UMyCameraShake::UMyCameraShake()
{
OscillationDuration = 0.25f;
OscillationBlendInTime = 0.05f;
OscillationBlendOutTime = 0.05f;
RotOscillation.Pitch.Amplitude = FMath::RandRange(5.0f, 10.0f);
RotOscillation.Pitch.Frequency = FMath::RandRange(25.0f, 35.0f);
RotOscillation.Yaw.Amplitude = FMath::RandRange(5.0f, 10.0f);
RotOscillation.Yaw.Frequency = FMath::RandRange(25.0f, 35.0f);
}
Next, we need to move into the character's .h
file. In the public
section add in a new variable of TSubclassOf<UCameraShake>
and name it MyShake
.
add TSubclassOf
...
public
...
UPROPERTY(EditAnywhere)
TSubclassOf<UCameraShake> MyShake;
Next, in the character's .cpp
file we will call the camera shake every time the gun is fired. In the OnFire
function, first check if MyShake
is not NULL
and then we'll grab the PlayerCameraManager
and then run it's PlayCameraShake
function.
Play Camera Shake
void AUnrealCPPCharacter::OnFire()
{
...
if (AnimInstance != NULL && MyShake != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
GetWorld()->GetFirstPlayerController()->PlayerCameraManager->PlayCameraShake(MyShake, 1.0f);
}
...
Now go back into the editor and compile the code. Find your character and in the details panel add the new MyCameraShake
class to the MyShake
variable.
Add Shake Class to Character
Push play and now every time the gun fires, the camera will shake. You might have to restart your editor for the character class to function properly.
Below is the final code.
MyCameraShake.h
#pragma once
#include "CoreMinimal.h"
#include "Camera/CameraShake.h"
#include "MyCameraShake.generated.h"
/**
*
*/
UCLASS()
class UNREALCPP_API UMyCameraShake : public UCameraShake
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
UMyCameraShake();
};
MyCameraShake.cpp
#include "MyCameraShake.h"
// Sets default values
UMyCameraShake::UMyCameraShake()
{
OscillationDuration = 0.25f;
OscillationBlendInTime = 0.05f;
OscillationBlendOutTime = 0.05f;
RotOscillation.Pitch.Amplitude = FMath::RandRange(5.0f, 10.0f);
RotOscillation.Pitch.Frequency = FMath::RandRange(25.0f, 35.0f);
RotOscillation.Yaw.Amplitude = FMath::RandRange(5.0f, 10.0f);
RotOscillation.Yaw.Frequency = FMath::RandRange(25.0f, 35.0f);
}
UnrealCPPCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "UnrealCPPCharacter.generated.h"
class UInputComponent;
UCLASS(config=Game)
class AUnrealCPPCharacter : public ACharacter
{
GENERATED_BODY()
/** Pawn mesh: 1st person view (arms; seen only by self) */
UPROPERTY(VisibleDefaultsOnly, Category=Mesh)
class USkeletalMeshComponent* Mesh1P;
/** Gun mesh: 1st person view (seen only by self) */
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
class USkeletalMeshComponent* FP_Gun;
/** Location on gun mesh where projectiles should spawn. */
UPROPERTY(VisibleDefaultsOnly, Category = Mesh)
class USceneComponent* FP_MuzzleLocation;
/** First person camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FirstPersonCameraComponent;
public:
AUnrealCPPCharacter();
protected:
virtual void BeginPlay();
public:
/** 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;
/** Gun muzzle's offset from the characters location */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
FVector GunOffset;
/** Projectile class to spawn */
UPROPERTY(EditDefaultsOnly, Category=Projectile)
TSubclassOf<class AUnrealCPPProjectile> ProjectileClass;
/** Sound to play each time we fire */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Gameplay)
class USoundBase* FireSound;
/** AnimMontage to play each time we fire */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
class UAnimMontage* FireAnimation;
UPROPERTY(EditAnywhere)
TSubclassOf<UCameraShake> MyShake;
protected:
/** Fires a projectile. */
void OnFire();
/** Handles moving forward/backward */
void MoveForward(float Val);
/** Handles stafing movement, left and right */
void MoveRight(float Val);
/**
* 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);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;
// End of APawn interface
FORCEINLINE class USkeletalMeshComponent* GetMesh1P() const { return Mesh1P; }
/** Returns FirstPersonCameraComponent subobject **/
FORCEINLINE class UCameraComponent* GetFirstPersonCameraComponent() const { return FirstPersonCameraComponent; }
};
UnrealCPPCharacter.cpp
#include "UnrealCPPCharacter.h"
#include "UnrealCPPProjectile.h"
#include "Animation/AnimInstance.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/InputSettings.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Kismet/GameplayStatics.h"
#include "MotionControllerComponent.h"
#include "XRMotionControllerBase.h"
DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);
//////////////////////////////////////////////////////////////////////////
// AUnrealCPPCharacter
AUnrealCPPCharacter::AUnrealCPPCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->SetupAttachment(GetCapsuleComponent());
FirstPersonCameraComponent->RelativeLocation = FVector(-39.56f, 1.75f, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;
// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->SetupAttachment(FirstPersonCameraComponent);
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
Mesh1P->RelativeRotation = FRotator(1.9f, -19.19f, 5.2f);
Mesh1P->RelativeLocation = FVector(-0.5f, -4.4f, -155.7f);
// Create a gun mesh component
FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
// FP_Gun->SetupAttachment(Mesh1P, TEXT("GripPoint"));
FP_Gun->SetupAttachment(RootComponent);
FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
FP_MuzzleLocation->SetupAttachment(FP_Gun);
FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));
// Default offset from the character location for projectiles to spawn
GunOffset = FVector(100.0f, 0.0f, 10.0f);
// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P, FP_Gun, and VR_Gun
// are set in the derived blueprint asset named MyCharacter to avoid direct content references in C++.
}
void AUnrealCPPCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
//Attach gun mesh component to Skeleton, doing it here because the skeleton is not yet created in the constructor
FP_Gun->AttachToComponent(Mesh1P, FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), TEXT("GripPoint"));
Mesh1P->SetHiddenInGame(false, true);
}
//////////////////////////////////////////////////////////////////////////
// Input
void AUnrealCPPCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// set up gameplay key bindings
check(PlayerInputComponent);
// Bind jump events
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
// Bind fire event
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AUnrealCPPCharacter::OnFire);
// Bind movement events
PlayerInputComponent->BindAxis("MoveForward", this, &AUnrealCPPCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AUnrealCPPCharacter::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
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AUnrealCPPCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AUnrealCPPCharacter::LookUpAtRate);
}
void AUnrealCPPCharacter::OnFire()
{
// try and fire a projectile
if (ProjectileClass != NULL)
{
UWorld* const World = GetWorld();
if (World != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
const FVector SpawnLocation = ((FP_MuzzleLocation != nullptr) ? FP_MuzzleLocation->GetComponentLocation() : GetActorLocation()) + SpawnRotation.RotateVector(GunOffset);
//Set Spawn Collision Handling Override
FActorSpawnParameters ActorSpawnParams;
ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
// spawn the projectile at the muzzle
World->SpawnActor<AUnrealCPPProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams);
}
}
// try and play the sound if specified
if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}
// try and play a firing animation if specified
if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL && MyShake != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
GetWorld()->GetFirstPlayerController()->PlayerCameraManager->PlayCameraShake(MyShake, 1.0f);
}
}
}
void AUnrealCPPCharacter::MoveForward(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}
void AUnrealCPPCharacter::MoveRight(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}
void AUnrealCPPCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AUnrealCPPCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}