The whole of Pilot Movement :)

This commit is contained in:
Rafal 2024-10-28 18:52:03 +00:00
parent 84a981a673
commit 79d92fd2cb
18 changed files with 505 additions and 69 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/FirstPerson/HUD/WBP_DashesRemaining.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/FirstPerson/Input/Actions/IA_Sprint.uasset (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,46 +1,63 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "MonolithCharacter.h"
#include "Animation/AnimInstance.h"
#include "PilotCamera.h"
#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/LocalPlayer.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "InputActionValue.h"
#include "Engine/LocalPlayer.h"
DEFINE_LOG_CATEGORY(LogTemplateCharacter);
//////////////////////////////////////////////////////////////////////////
// AMonolithCharacter
void AMonolithCharacter::StartRechargingDash()
{
if (DashRechargeHandle.IsValid()) return;
GetWorld()->GetTimerManager().SetTimer(DashRechargeHandle, this
, &AMonolithCharacter::DashRecharged, DashRechargeTime);
}
void AMonolithCharacter::DashRecharged()
{
DashesRemaining++;
UpdateDashUI(DashesRemaining);
DashRechargeHandle.Invalidate();
if (DashesRemaining < 4)
{
StartRechargingDash();
}
}
AMonolithCharacter::AMonolithCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);
// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UPilotCamera>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->SetupAttachment(GetCapsuleComponent());
FirstPersonCameraComponent->SetRelativeLocation(FVector(-10.f, 0.f, 60.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->SetRelativeRotation(FRotator(0.9f, -19.19f, 5.2f));
Mesh1P->SetRelativeLocation(FVector(-30.f, 0.f, -150.f));
}
void AMonolithCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
Mesh1P = Cast<USkeletalMeshComponent>(GetComponentByClass(USkeletalMeshComponent::StaticClass()));
CharMove = Cast<UCharacterMovementComponent>(GetComponentByClass(UCharacterMovementComponent::StaticClass()));
if (CharMove) WalkSpeed = CharMove->MaxWalkSpeed;
}
void AMonolithCharacter::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
if (CharMove == nullptr) return;
CharMove->IsFalling() ? MoveState.Remove(GROUNDED) : MoveState.Add(GROUNDED);
MoveState.VelocityMag = CharMove->Velocity.Length();
MoveState.VelocityMag < VelocityTolerance ? MoveState.Add(STILL) : MoveState.Remove(STILL);
}
//////////////////////////////////////////////////////////////////////////// Input
@ -56,6 +73,15 @@ void AMonolithCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputC
// Moving
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMonolithCharacter::Move);
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Completed, this, &AMonolithCharacter::StopMove);
// Sprinting
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Triggered, this, &AMonolithCharacter::Sprint);
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &AMonolithCharacter::StopSprint);
// Dashing
EnhancedInputComponent->BindAction(HorDashAction, ETriggerEvent::Triggered, this, &AMonolithCharacter::HorizontalDash);
EnhancedInputComponent->BindAction(VerDashAction, ETriggerEvent::Triggered, this, &AMonolithCharacter::VerticalDash);
// Looking
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMonolithCharacter::Look);
@ -66,21 +92,112 @@ void AMonolithCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputC
}
}
static void UpdateMoveState(FMoveState& MoveState, int Forwards, int Sideways)
{
if (Sideways && !Forwards)
{ // STRAFING
if (Sideways > 0)
{
MoveState.Add(STRAFE_R);
MoveState.Remove(STRAFE_L);
}
else
{
MoveState.Add(STRAFE_L);
MoveState.Remove(STRAFE_R);
}
MoveState.Remove(FORWARD);
}
else if (Forwards > 0)
{ // GOING ROUGHLY FORWARD
MoveState.Add(FORWARD);
MoveState.Remove(STRAFE_L);
MoveState.Remove(STRAFE_R);
}
else
{
MoveState.Remove(FORWARD);
MoveState.Remove(STRAFE_L);
MoveState.Remove(STRAFE_R);
}
}
void AMonolithCharacter::Move(const FInputActionValue& Value)
{
// input is a Vector2D
FVector2D MovementVector = Value.Get<FVector2D>();
MovementVector = Value.Get<FVector2D>();
if (Controller != nullptr)
{
// add movement
AddMovementInput(GetActorForwardVector(), MovementVector.Y);
AddMovementInput(GetActorRightVector(), MovementVector.X);
UE_LOG(LogTemp, Warning, TEXT("X: %f, Y: %f"), (float)MovementVector.X, (float)MovementVector.Y);
UpdateMoveState(MoveState, MovementVector.Y, MovementVector.X);
}
}
void AMonolithCharacter::StopMove()
{
MoveState.Remove(STRAFE_L);
MoveState.Remove(STRAFE_R);
MoveState.Remove(FORWARD);
MoveState.Remove(SPRINT);
}
void AMonolithCharacter::Sprint(const FInputActionValue& Value)
{
if (CharMove == nullptr || MoveState.Is(SPRINT)) return;
if (!MoveState.Is(FORWARD)
|| !MoveState.Is(GROUNDED))
{
StopSprint();
}
else
{
MoveState.Add(SPRINT);
CharMove->MaxWalkSpeed = SprintSpeed;
}
}
void AMonolithCharacter::StopSprint()
{
MoveState.Remove(SPRINT);
CharMove->MaxWalkSpeed = WalkSpeed;
}
void AMonolithCharacter::HorizontalDash(const FInputActionValue& Value)
{
if (DashesRemaining <= 0) return;
DashesRemaining--;
UpdateDashUI(DashesRemaining);
FVector Impulse;
if (MovementVector.Y)
{
Impulse = MovementVector.Y > 0
? GetActorForwardVector() : -GetActorForwardVector();
}
else
{
Impulse = MovementVector.X > 0
? GetActorRightVector() : -GetActorRightVector();
}
Impulse *= DashImpulse;
Impulse.Z += DashImpulse * 0.25f;
CharMove->AddImpulse(Impulse, true);
StartRechargingDash();
}
void AMonolithCharacter::VerticalDash(const FInputActionValue& Value)
{
if (DashesRemaining <= 0) return;
DashesRemaining--;
UpdateDashUI(DashesRemaining);
FVector Impulse(0, 0, DashImpulse * 0.55f);
CharMove->AddImpulse(Impulse, true);
StartRechargingDash();
}
void AMonolithCharacter::Look(const FInputActionValue& Value)
{
// input is a Vector2D
@ -92,4 +209,4 @@ void AMonolithCharacter::Look(const FInputActionValue& Value)
AddControllerYawInput(LookAxisVector.X);
AddControllerPitchInput(LookAxisVector.Y);
}
}
}

View File

@ -5,12 +5,15 @@
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "MoveState.h"
#include "PilotCamera.h"
#include "PilotCameraShake.h"
#include "MonolithCharacter.generated.h"
class UInputComponent;
class UCharacterMovementComponent;
class USkeletalMeshComponent;
class UPilotCamera;
class UInputAction;
class UInputComponent;
class UInputMappingContext;
struct FInputActionValue;
@ -21,14 +24,9 @@ class AMonolithCharacter : public ACharacter
{
GENERATED_BODY()
/** Pawn mesh: 1st person view (arms; seen only by self) */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Mesh, meta = (AllowPrivateAccess = "true"))
USkeletalMeshComponent* Mesh1P;
/** First person camera */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
UPilotCamera* FirstPersonCameraComponent;
friend UPilotCamera;
friend UPilotCameraShake;
/** Jump Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* JumpAction;
@ -37,34 +35,82 @@ class AMonolithCharacter : public ACharacter
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* MoveAction;
int fortnite = 69;
/** Sprint Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* SprintAction;
/** Horizontal Dash Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* HorDashAction;
/** Vertical Dash Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
UInputAction* VerDashAction;
UPROPERTY(EditDefaultsOnly, Category=Input, meta=(AllowPrivateAccess = "true"))
float SprintSpeed = 750.f;
float WalkSpeed = 600.f;
// Velocity Below this Value will be Ignored for Camera Effects
UPROPERTY(EditAnywhere, Category = "MoveState", meta=(AllowPrivateAccess = "true"))
float VelocityTolerance = 300.f;
// Amount of impulse to add when dashing
UPROPERTY(EditAnywhere, Category = "MoveState", meta=(AllowPrivateAccess = "true"))
float DashImpulse = 600.f;
public:
AMonolithCharacter();
USkeletalMeshComponent* Mesh1P;
UCharacterMovementComponent* CharMove;
FMoveState MoveState;
FVector2D MovementVector;
protected:
virtual void BeginPlay();
void BeginPlay();
void Tick(float DeltaSeconds) override;
public:
/** Look Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* LookAction;
protected:
/** Called for movement input */
void Move(const FInputActionValue& Value);
void StopMove();
/** Called for SPRINT input */
void Sprint(const FInputActionValue& Value);
void StopSprint();
/** Called for Dashing inputs */
void HorizontalDash(const FInputActionValue& Value);
void VerticalDash(const FInputActionValue& Value);
/** Called for looking input */
void Look(const FInputActionValue& Value);
protected:
// APawn interface
virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;
// End of APawn interface
// Time to recharge 1 dash
UPROPERTY(EditDefaultsOnly, Category = "MoveState", meta = (AllowPrivateAccess = "true"))
float DashRechargeTime = 2.5f;
FTimerHandle DashRechargeHandle;
int DashesRemaining = 4;
void StartRechargingDash();
void DashRecharged();
UFUNCTION(BlueprintImplementableEvent)
void UpdateDashUI(int dashesRemaining);
public:
/** Returns Mesh1P subobject **/
USkeletalMeshComponent* GetMesh1P() const { return Mesh1P; }
AMonolithCharacter();
/** Look Input Action */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* LookAction;
USkeletalMeshComponent* GetMesh1P()
{
if (Mesh1P) return Mesh1P;
Mesh1P = Cast<USkeletalMeshComponent>(GetComponentByClass(USkeletalMeshComponent::StaticClass()));
return Mesh1P;
}
};

View File

@ -0,0 +1,33 @@
#pragma once
using State = unsigned short;
#define STRAFE_L 0x1
#define STRAFE_R 0x2
#define SPRINT 0x4
#define GROUNDED 0x8
#define FORWARD 0x10
#define STILL 0x20
class FMoveState
{
State MoveState = 0;
public:
bool Is(State state) const
{
return MoveState & state;
}
void Add(State state)
{
MoveState += Is(state) ? 0 : state;
}
void Remove(State state)
{
MoveState -= Is(state) ? state : 0;
}
double VelocityMag = 0;
};

View File

@ -1,23 +1,82 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "PilotCamera.h"
#include "MonolithPlayerController.h"
#include "MonolithCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "MoveState.h"
void UPilotCamera::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (m_Movement == nullptr)
{
AMonolithCharacter* PlayerChar = Cast<AMonolithCharacter>
if (TickType == LEVELTICK_ViewportsOnly
|| (!isMoveStateSet() && !TrySetMoveState())) return;
// TODO - Set FOV based on Velocity mag
UpdateFOV(DeltaTime);
// TODO - Add Camera Roll if Strafing
UpdateRoll(DeltaTime);
// if (MoveState->Is(STRAFE_L)) UE_LOG(LogTemp, Warning, TEXT("Strafing Left"));
// if (MoveState->Is(STRAFE_R)) UE_LOG(LogTemp, Warning, TEXT("Strafing Right"));
// if (MoveState->Is(SPRINT)) UE_LOG(LogTemp, Warning, TEXT("Sprinting"));
// if (MoveState->Is(GROUNDED)) UE_LOG(LogTemp, Warning, TEXT("On the floor"));
// if (MoveState->Is(FORWARD)) UE_LOG(LogTemp, Warning, TEXT("Going FORWARD"));
}
bool UPilotCamera::TrySetMoveState()
{
AMonolithCharacter* PlayerChar = Cast<AMonolithCharacter>
(
Cast<AMonolithPlayerController>(GetWorld()->GetFirstPlayerController())->GetPawn()
);
if (PlayerChar == nullptr) return;
m_Movement = PlayerChar->GetComponentByClass<UCharacterMovementComponent>();
if (m_Movement == nullptr) return;
}
UE_LOG(LogTemp, Warning, TEXT("Acc: %f, Vel: %f"), m_Movement->GetCurrentAcceleration().X, m_Movement->Velocity.X);
if ( PlayerChar == nullptr) return false;
MoveState = &PlayerChar->MoveState;
return MoveState != nullptr;
}
void UPilotCamera::UpdateFOV(float DeltaTime)
{
const float targetFOV = GetTargetFOV();
if (FieldOfView == targetFOV) return;
float linearModifier = 1 - (FMath::Abs(targetFOV - FieldOfView)) / targetFOV;
float rateOfChange = RateOfChange * DeltaTime * linearModifier;
bool increasing = targetFOV > FieldOfView;
rateOfChange *= increasing ? 1 : -1;
float newFOV = FieldOfView + rateOfChange;
if ((increasing && newFOV > targetFOV) || (!increasing && newFOV < targetFOV))
{
newFOV = targetFOV;
}
SetFieldOfView(newFOV);
}
void UPilotCamera::UpdateRoll(float DeltaTime)
{
FRotator rot = GetRelativeRotation();
if (MoveState->Is(STRAFE_R))
{
TargetRoll = MaxStrafeRoll;
}
else if (MoveState->Is(STRAFE_L))
{
TargetRoll = -MaxStrafeRoll;
}
else
{
TargetRoll = 0;
}
float rateOfChange = RollRateOfChange * DeltaTime;
bool increasing = TargetRoll > rot.Roll;
rateOfChange *= increasing ? 1 : -1;
rot.Roll += rateOfChange;
if ((increasing && rot.Roll > TargetRoll) || (!increasing && rot.Roll < TargetRoll))
{
rot.Roll = TargetRoll;
}
SetRelativeRotation(rot);
}

View File

@ -3,19 +3,59 @@
#pragma once
#include "CoreMinimal.h"
#include "MoveState.h"
#include "Camera/CameraComponent.h"
#include "PilotCamera.generated.h"
class UCharacterMovementComponent;
class FMoveState;
UCLASS()
UCLASS(Blueprintable, ClassGroup=Camera, meta=(BlueprintSpawnableComponent))
class MONOLITH_API UPilotCamera : public UCameraComponent
{
GENERATED_BODY()
private:
void TickComponent(float DeltaTime
, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction);
using FOVs = std::tuple<float, float, float>;
#define ARE(Stand, Walk, Sprint) std::make_tuple(Stand, Walk, Sprint)
UCharacterMovementComponent* m_Movement;
void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction);
inline bool isMoveStateSet() const;
bool TrySetMoveState();
// FOV
FOVs TargetFOVs = ARE(87.f, 92.f, 100.f);
const float RateOfChange = 30;
float GetTargetFOV() const;
void UpdateFOV(float DeltaTime);
// Roll
float TargetRoll = 0;
// Max amount of roll applied when strafing
UPROPERTY(EditDefaultsOnly, Category = "Move State", meta = (AllowPrivateAccess = "true"))
float MaxStrafeRoll = 3;
// Amount of roll applied per second
UPROPERTY(EditDefaultsOnly, Category = "Move State", meta = (AllowPrivateAccess = "true"))
float RollRateOfChange = 15;
void UpdateRoll(float DeltaTime);
// TODO - Implement these
void ApplyImpulseFOV();
void ImpactCameraShake();
FMoveState* MoveState;
};
inline float UPilotCamera::GetTargetFOV() const
{
auto[stand, walk, sprint] = TargetFOVs;
if (MoveState->Is(SPRINT)) return sprint;
if (!MoveState->Is(STILL)
&& !MoveState->Is(STRAFE_R)
&& !MoveState->Is(STRAFE_L)) return walk;
return stand;
}
inline bool UPilotCamera::isMoveStateSet() const
{
return MoveState != nullptr;
}

View File

@ -0,0 +1,62 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "PilotCameraShake.h"
#include "MonolithCharacter.h"
#include "MonolithPlayerController.h"
#include "MoveState.h"
void UPilotCameraShake::BeginPlay()
{
Super::BeginPlay();
// For some reason camera shakes dont override tick :((
GetWorld()->GetTimerManager().SetTimer(TickHandle, this
, &UPilotCameraShake::UpdateCameraShake, 0.25f, true);
}
bool UPilotCameraShake::TrySetMoveState()
{
AMonolithCharacter* PlayerChar = Cast<AMonolithCharacter>
(
Cast<AMonolithPlayerController>(GetWorld()->GetFirstPlayerController())->GetPawn()
);
if ( PlayerChar == nullptr) return false;
MoveState = &PlayerChar->MoveState;
return MoveState != nullptr;
}
void UPilotCameraShake::UpdateCameraShake()
{
if (CantUpdate()) return;
if (!MoveState->Is(GROUNDED))
{
if (CurrentState == Floating) return;
CurrentState = Floating;
SetCameraShakeBase(FloatingShake);
}
else if (MoveState->Is(SPRINT))
{
if (CurrentState == Running) return;
CurrentState = Running;
SetCameraShakeBase(RunningShake);
}
else if (MoveState->Is(STILL))
{
if (CurrentState == Idling) return;
CurrentState = Idling;
SetCameraShakeBase(IdleShake);
}
else
{
if (CurrentState == Walking) return;
CurrentState = Walking;
SetCameraShakeBase(WalkingShake);
}
}
void UPilotCameraShake::SetCameraShakeBase(TSubclassOf<UCameraShakeBase> ShakeBase)
{
StopAllCameraShakes();
StartCameraShake(ShakeBase);
}

View File

@ -0,0 +1,55 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Camera/CameraShakeSourceComponent.h"
#include "PilotCameraShake.generated.h"
class FMoveState;
UCLASS(Blueprintable, ClassGroup=Camera, meta=(BlueprintSpawnableComponent))
class MONOLITH_API UPilotCameraShake : public UCameraShakeSourceComponent
{
GENERATED_BODY()
enum EState{Idling, Walking, Running, Floating};
void BeginPlay() override;
bool CantUpdate();
bool isMoveStateSet() const;
bool TrySetMoveState();
UFUNCTION(BlueprintCallable)
void UpdateCameraShake();
void SetCameraShakeBase(TSubclassOf<UCameraShakeBase> ShakeBase);
FTimerHandle TickHandle;
FMoveState* MoveState;
EState CurrentState = Idling;
UPROPERTY(EditDefaultsOnly, Category = "Cam Shake", meta=(AllowPrivateAccess = "true"))
TSubclassOf<UCameraShakeBase> RunningShake;
UPROPERTY(EditDefaultsOnly, Category = "Cam Shake", meta=(AllowPrivateAccess = "true"))
TSubclassOf<UCameraShakeBase> WalkingShake;
UPROPERTY(EditDefaultsOnly, Category = "Cam Shake", meta=(AllowPrivateAccess = "true"))
TSubclassOf<UCameraShakeBase> FloatingShake;
UPROPERTY(EditDefaultsOnly, Category = "Cam Shake", meta=(AllowPrivateAccess = "true"))
TSubclassOf<UCameraShakeBase> IdleShake;
};
inline bool UPilotCameraShake::CantUpdate()
{
return !isMoveStateSet() && !TrySetMoveState();
}
inline bool UPilotCameraShake::isMoveStateSet() const
{
return MoveState != nullptr;
}