Last active
April 17, 2025 11:47
-
-
Save iUltimateLP/d1f9c0408f84086401d909b38054db3c to your computer and use it in GitHub Desktop.
Adds a flexible Head Bobbing component to the Unreal Engine. Supports three different camera shake states (idle, walk, run).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2019-2021 Jonathan Verbeek. All Rights Reserved. | |
#include "Player/HeadBobComponent.h" | |
UHeadBobComponent::UHeadBobComponent() | |
{ | |
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features | |
// off to improve performance if you don't need them. | |
PrimaryComponentTick.bCanEverTick = true; | |
// Activate by default | |
SetAutoActivate(true); | |
} | |
void UHeadBobComponent::BeginPlay() | |
{ | |
Super::BeginPlay(); | |
} | |
void UHeadBobComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) | |
{ | |
Super::TickComponent(DeltaTime, TickType, ThisTickFunction); | |
// Don't do anything if not active | |
if (!IsActive()) return; | |
// Get the owner's velocity | |
FVector Velocity = GetOwner()->GetVelocity(); | |
// Whether the character is moving, based on the owner's velocity | |
bool bIsMoving = Velocity.Size() != 0.f; | |
// If the character is not moving, and not in idle state | |
if (!bIsMoving && State != EHeadBobState::Idle) | |
{ | |
// Transition to idle state | |
State = EHeadBobState::Idle; | |
StartCameraShake(IdleCameraShake); | |
} | |
// If the character is walking normally, but is not in walk state | |
else if (bIsMoving && !bIsSprinting && State != EHeadBobState::Walk) | |
{ | |
// Transition to walk state | |
State = EHeadBobState::Walk; | |
StartCameraShake(WalkCameraShake); | |
} | |
// If the character is running, but is not in run state | |
else if (bIsMoving && bIsSprinting && State != EHeadBobState::Run) | |
{ | |
// Transition to run state | |
State = EHeadBobState::Run; | |
StartCameraShake(RunCameraShake); | |
} | |
} | |
void UHeadBobComponent::SetIsSprinting(bool bSprinting) | |
{ | |
bIsSprinting = bSprinting; | |
} | |
APlayerController* UHeadBobComponent::GetPlayerController() | |
{ | |
// Cast the owner to a pawn | |
APawn* OwnerAsPawn = Cast<APawn>(GetOwner()); | |
// Make sure that worked | |
if (OwnerAsPawn) | |
{ | |
// Get the pawn's controller | |
APlayerController* PC = Cast<APlayerController>(OwnerAsPawn->GetController()); | |
// Check if that PC has a camera manager | |
if (!PC->PlayerCameraManager) | |
{ | |
UE_LOG(LogTemp, Error, TEXT("%s's owner does NOT have a camera manager!"), *GetName()); | |
return nullptr; | |
} | |
// Return the PC | |
return PC; | |
} | |
else | |
{ | |
UE_LOG(LogTemp, Error, TEXT("%s can't cast owner to pawn! HeadBobComponents only work on pawns!"), *GetName()); | |
return nullptr; | |
} | |
} | |
void UHeadBobComponent::StartCameraShake(TSubclassOf<UMatineeCameraShake> Class) | |
{ | |
// Get the player controller | |
APlayerController* PC = GetPlayerController(); | |
check(PC); | |
// Stop any camera shake currently playing | |
StopCameraShake(); | |
// Make sure the given class is valid | |
if (!Class) | |
{ | |
UE_LOG(LogTemp, Error, TEXT("Cannot start empty camera shake")); | |
return; | |
} | |
// Start the new one | |
CurrentCameraShake = PC->PlayerCameraManager->StartCameraShake(Class, 1.f, ECameraShakePlaySpace::CameraLocal); | |
UE_LOG(LogTemp, Log, TEXT("Started %s"), *Class->GetName()); | |
// Flag that we started a camera shake | |
bCameraShakeActive = true; | |
} | |
void UHeadBobComponent::StopCameraShake() | |
{ | |
// Get the player controller | |
APlayerController* PC = GetPlayerController(); | |
check(PC); | |
// Really a CS playing? | |
if (bCameraShakeActive) | |
{ | |
// Not anymore | |
bCameraShakeActive = false; | |
// Stop the current camera shake | |
UE_LOG(LogTemp, Log, TEXT("Stopped %s"), *CurrentCameraShake->GetClass()->GetName()); | |
PC->PlayerCameraManager->StopCameraShake(CurrentCameraShake, false); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2019-2021 Jonathan Verbeek. All Rights Reserved. | |
#pragma once | |
#include "CoreMinimal.h" | |
#include "Components/SceneComponent.h" | |
#include "Camera/CameraShake.h" | |
#include "HeadBobComponent.generated.h" | |
// Current state of the shake state machine. Used to switch between shakes | |
UENUM(BlueprintType, Blueprintable) | |
enum EHeadBobState | |
{ | |
Idle, | |
Walk, | |
Run | |
}; | |
// This component provides head bobbing to any pawn with a camera manager attached to it. | |
UCLASS(ClassGroup=(Nexus), meta=(BlueprintSpawnableComponent)) | |
class NEXUS_API UHeadBobComponent : public UActorComponent | |
{ | |
GENERATED_BODY() | |
public: | |
// Sets default values for this component's properties | |
UHeadBobComponent(); | |
public: | |
// The idle camera shake to use | |
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Head Bob Component") | |
TSubclassOf<UMatineeCameraShake> IdleCameraShake; | |
// The walk camera shake to use | |
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Head Bob Component") | |
TSubclassOf<UMatineeCameraShake> WalkCameraShake; | |
// The run camera shake to use | |
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Head Bob Component") | |
TSubclassOf<UMatineeCameraShake> RunCameraShake; | |
protected: | |
// Called when the game starts | |
virtual void BeginPlay() override; | |
public: | |
// Called every frame | |
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; | |
public: | |
UFUNCTION(BlueprintCallable, Category = "Head Bob Component") | |
void SetIsSprinting(bool bSprinting); | |
private: | |
// Get the player controller | |
APlayerController* GetPlayerController(); | |
// Starts a camera shake | |
void StartCameraShake(TSubclassOf<UMatineeCameraShake> Class); | |
// Stops the current camera shake | |
void StopCameraShake(); | |
private: | |
// Whether any camera shake is currently active | |
bool bCameraShakeActive = false; | |
// The camera shake currently active | |
UCameraShakeBase* CurrentCameraShake; | |
// Whether the sprinting flag is set | |
bool bIsSprinting = false; | |
// Current state of the state machine | |
TEnumAsByte<EHeadBobState> State = EHeadBobState::Idle; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment