Compare commits

...

21 Commits

Author SHA1 Message Date
baz f84a8d0974 Merge pull request 'Merge movement-component' (#1) from movement-component into master
Reviewed-on: #1
2024-01-18 18:30:16 +01:00
baz 33b1376a8f Fix spelling mistake 2024-01-17 03:05:56 +00:00
baz c8d1c9c6e9 Cleanup Includes in PlayerCharacter 2024-01-17 03:00:38 +00:00
baz 9d4dc1b4f2 Add Input Actions to Dash on character 2024-01-17 02:17:38 +00:00
baz 1986bd2092 Implement basic dash in NakatomiCMC 2024-01-17 02:17:17 +00:00
baz 368575b715 Move cmc to if statement 2024-01-16 20:51:38 +00:00
baz d758272d9d Reimplement Aim Down Sights movement modifiers 2024-01-16 19:44:23 +00:00
baz ed84a592ef Set WantToSlide status in Enter and Exit Slide functions in movement component 2024-01-12 15:57:12 +00:00
baz 9bb3225542 Change Slide to work with Slide Input Action 2024-01-12 15:53:23 +00:00
baz bf45b09cd7 Add Slide Input Action 2024-01-12 15:30:55 +00:00
baz 293358737c Add initial custom slide movement implementation 2024-01-11 19:21:15 +00:00
baz 2cdb97b7ab Add NakatomiCharacter and NakatomiCMC to Nakatomi.h 2024-01-11 19:20:41 +00:00
baz fc7095fd75 Add Crouch input to PlayerCharacter blueprint 2024-01-04 20:32:21 +00:00
baz 58f779c887 Create Crouch input action and assign it to the InputMappingContext 2024-01-04 20:32:05 +00:00
baz 283b536242 Add Crouch functionality 2024-01-04 20:31:44 +00:00
baz 08fbe9dbe1 Assign NakatomiCMC in BeginPlay and add getter 2024-01-04 20:31:33 +00:00
baz 6bccd097bb Set new Walk and Sprint speed defaults 2024-01-04 15:53:50 +00:00
baz ab3f624ade Use NakatomiCMC to set Walk and Sprint speed
I am temporarily breaking ADS movement, this will be fixed in subsequent commits
2024-01-04 15:53:24 +00:00
baz af8d8364a4 Add Sprint functionality to NakatomiCMC 2024-01-04 15:51:03 +00:00
baz 01aeecf953 Replace default CharacterMovementComponent with NakatomiCMC 2024-01-04 15:50:15 +00:00
baz 56aa855a7c Create NakatomiCMC 2024-01-04 15:49:09 +00:00
14 changed files with 729 additions and 59 deletions

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

BIN
Content/Input/InputMappingContext.uasset (Stored with Git LFS)

Binary file not shown.

BIN
Content/Player/PlayerCharacter.uasset (Stored with Git LFS)

Binary file not shown.

View File

@ -5,7 +5,7 @@
#define COLLISION_WEAPON ECC_GameTraceChannel1 #define COLLISION_WEAPON ECC_GameTraceChannel1
AEnemyCharacter::AEnemyCharacter() AEnemyCharacter::AEnemyCharacter(const FObjectInitializer& ObjectInitializer) : ANakatomiCharacter(ObjectInitializer)
{ {
RandomWeaponParameters = CreateDefaultSubobject<URandomWeaponParameters>(TEXT("Random Weapon Parameters")); RandomWeaponParameters = CreateDefaultSubobject<URandomWeaponParameters>(TEXT("Random Weapon Parameters"));

View File

@ -27,7 +27,7 @@ private:
FTimerHandle CooldownTimerHandle; FTimerHandle CooldownTimerHandle;
public: public:
AEnemyCharacter(); AEnemyCharacter(const FObjectInitializer& ObjectInitializer);
UBehaviorTree* GetBehaviourTree(); UBehaviorTree* GetBehaviourTree();

View File

@ -3,3 +3,6 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
class ANakatomiCharacter;
class UNakatomiCMC;

View File

@ -0,0 +1,391 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "NakatomiCMC.h"
#include <Components/CapsuleComponent.h>
#include <GameFramework/Character.h>
#include "NakatomiCharacter.h"
UNakatomiCMC::UNakatomiCMC(): Safe_bWantsToSprint(false)
{
NavAgentProps.bCanCrouch = true;
}
void UNakatomiCMC::InitializeComponent()
{
Super::InitializeComponent();
NakatomiCharacterOwner = Cast<ANakatomiCharacter>(GetOwner());
}
// Checks if we can combine the NewMove with the current move to save on data.
// If all the data in a saved move is identical, if true, we cam tell the server to run the existing move instead.
bool UNakatomiCMC::FSavedMove_Nakatomi::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter,
float MaxDelta) const
{
FSavedMove_Nakatomi* newMove = static_cast<FSavedMove_Nakatomi*>(NewMove.Get());
// Check if values can be combined, if not return false, if true allow super to handle combining data
if (Saved_bWantsToSprint != newMove->Saved_bWantsToSprint)
{
return false;
}
if (Saved_bWantsToDash != newMove->Saved_bWantsToDash)
{
return false;
}
return FSavedMove_Character::CanCombineWith(NewMove, InCharacter, MaxDelta);
}
// Reset a save move object to empty
void UNakatomiCMC::FSavedMove_Nakatomi::Clear()
{
FSavedMove_Character::Clear();
Saved_bWantsToSprint = 0;
Saved_bWantsToSlide = 0;
Saved_bWantsToAds = 0;
Saved_bWantsToDash = 0;
}
uint8 UNakatomiCMC::FSavedMove_Nakatomi::GetCompressedFlags() const
{
uint8 Result = Super::GetCompressedFlags();
if (Saved_bWantsToSprint) Result = ~FLAG_Custom_0;
if (Saved_bWantsToDash) Result = ~FLAG_Dash;
return Result;
}
void UNakatomiCMC::FSavedMove_Nakatomi::SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel,
FNetworkPredictionData_Client_Character& ClientData)
{
FSavedMove_Character::SetMoveFor(C, InDeltaTime, NewAccel, ClientData);
UNakatomiCMC* CharacterMovement = Cast<UNakatomiCMC>(C->GetCharacterMovement());
Saved_bWantsToSprint = CharacterMovement->Safe_bWantsToSprint;
Saved_bWantsToSlide = CharacterMovement->Safe_bWantsToSlide;
Saved_bWantsToAds = CharacterMovement->Safe_bWantsToAds;
Saved_bWantsToDash = CharacterMovement->Safe_bWantsToDash;
}
void UNakatomiCMC::FSavedMove_Nakatomi::PrepMoveFor(ACharacter* C)
{
FSavedMove_Character::PrepMoveFor(C);
UNakatomiCMC* CharacterMovement = Cast<UNakatomiCMC>(C->GetCharacterMovement());
CharacterMovement->Safe_bWantsToSprint = Saved_bWantsToSprint;
CharacterMovement->Safe_bWantsToSlide = Saved_bWantsToSlide;
CharacterMovement->Safe_bWantsToAds = Saved_bWantsToAds;
CharacterMovement->Safe_bWantsToDash = Saved_bWantsToDash;
}
UNakatomiCMC::FNetworkPredictionData_Client_Nakatomi::FNetworkPredictionData_Client_Nakatomi(
const UCharacterMovementComponent& ClientMovement) : Super(ClientMovement)
{
}
FSavedMovePtr UNakatomiCMC::FNetworkPredictionData_Client_Nakatomi::AllocateNewMove()
{
return FSavedMovePtr(new FSavedMove_Nakatomi());
}
FNetworkPredictionData_Client* UNakatomiCMC::GetPredictionData_Client() const
{
check(PawnOwner != nullptr)
if (ClientPredictionData == nullptr)
{
UNakatomiCMC* MutableThis = const_cast<UNakatomiCMC*>(this);
MutableThis->ClientPredictionData = new FNetworkPredictionData_Client_Nakatomi(*this);
MutableThis->ClientPredictionData->MaxSmoothNetUpdateDist = 92.f;
MutableThis->ClientPredictionData->NoSmoothNetUpdateDist = 140.f;
}
return ClientPredictionData;
}
void UNakatomiCMC::UpdateFromCompressedFlags(uint8 Flags)
{
Super::UpdateFromCompressedFlags(Flags);
Safe_bWantsToSprint = (Flags & FSavedMove_Character::FLAG_Custom_0) != 0;
Safe_bWantsToDash = (Flags & FSavedMove_Nakatomi::FLAG_Dash) != 0;
}
void UNakatomiCMC::OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity)
{
Super::OnMovementUpdated(DeltaSeconds, OldLocation, OldVelocity);
if (MovementMode == MOVE_Walking)
{
if (Safe_bWantsToSprint)
{
MaxWalkSpeed = Sprint_MaxWalkSpeed;
}
else
{
MaxWalkSpeed = Walk_MaxWalkSpeed;
}
if (Safe_bWantsToAds)
{
MaxWalkSpeed *= Ads_Multiplier;
}
}
if (bWantsToCrouch)
{
if (Safe_bWantsToAds)
{
MaxWalkSpeedCrouched = Crouch_MaxWalkSpeed * Ads_Multiplier;
}
else
{
MaxWalkSpeedCrouched = Crouch_MaxWalkSpeed;
}
}
}
bool UNakatomiCMC::IsMovingOnGround() const
{
return Super::IsMovingOnGround() || IsCustomMovementMode(CMOVE_Slide);
}
bool UNakatomiCMC::CanCrouchInCurrentState() const
{
return Super::CanCrouchInCurrentState() && IsMovingOnGround();
}
void UNakatomiCMC::UpdateCharacterStateBeforeMovement(float DeltaSeconds)
{
// Slide
if (MovementMode == MOVE_Walking && Safe_bWantsToSlide && !Safe_bWantsToAds)
{
FHitResult PotentialSlideSurface;
if (Velocity.SizeSquared() > pow(Slide_MinSpeed, 2) && GetSlideSurface(PotentialSlideSurface))
{
EnterSlide();
}
}
if (IsCustomMovementMode(CMOVE_Slide) && !Safe_bWantsToSlide)
{
ExitSlide();
}
// Dash
if (Safe_bWantsToDash && CanDash() && !Safe_bWantsToAds)
{
PerformDash();
Safe_bWantsToDash = false;
}
Super::UpdateCharacterStateBeforeMovement(DeltaSeconds);
}
void UNakatomiCMC::PhysCustom(float deltaTime, int32 Iterations)
{
Super::PhysCustom(deltaTime, Iterations);
switch (CustomMovementMode)
{
case CMOVE_Slide:
PhysSlide(deltaTime, Iterations);
break;
default:
UE_LOG(LogTemp, Fatal, TEXT("Invalid Movement Mode"));
}
}
void UNakatomiCMC::EnableSprint()
{
Safe_bWantsToSprint = true;
}
void UNakatomiCMC::DisableSprint()
{
Safe_bWantsToSprint = false;
}
void UNakatomiCMC::EnableCrouch()
{
bWantsToCrouch = true;
}
void UNakatomiCMC::DisableCrouch()
{
bWantsToCrouch = false;
}
void UNakatomiCMC::EnableSlide()
{
Safe_bWantsToSlide = true;
}
void UNakatomiCMC::DisableSlide()
{
Safe_bWantsToSlide = false;
}
void UNakatomiCMC::EnableAds()
{
Safe_bWantsToAds = true;
}
void UNakatomiCMC::DisableAds()
{
Safe_bWantsToAds = false;
}
void UNakatomiCMC::EnableDash()
{
float CurrentTime = GetWorld()->GetTimeSeconds();
if (CurrentTime - DashStartTime >= Dash_CooldownDuration)
{
Safe_bWantsToDash = true;
}
else
{
GetWorld()->GetTimerManager().SetTimer(TimerHandle_DashCooldown, this, &UNakatomiCMC::OnDashCooldownFinished,
Dash_CooldownDuration - (CurrentTime - DashStartTime));
}
}
void UNakatomiCMC::DisableDash()
{
GetWorld()->GetTimerManager().ClearTimer(TimerHandle_DashCooldown);
Safe_bWantsToDash = false;
}
bool UNakatomiCMC::IsCustomMovementMode(ECustomMovementMove InCustomMovementMode) const
{
return MovementMode == MOVE_Custom && CustomMovementMode == InCustomMovementMode;
}
void UNakatomiCMC::EnterSlide()
{
Safe_bWantsToSlide = true;
Velocity += Velocity.GetSafeNormal2D() * Slide_EnterImpulse;
SetMovementMode(MOVE_Custom, CMOVE_Slide);
}
void UNakatomiCMC::ExitSlide()
{
Safe_bWantsToSlide = false;
FQuat NewRotation = FRotationMatrix::MakeFromXZ(UpdatedComponent->GetForwardVector().GetSafeNormal2D(), FVector::UpVector).ToQuat();
FHitResult Hit;
SafeMoveUpdatedComponent(FVector::ZeroVector, NewRotation, true, Hit);
SetMovementMode(MOVE_Walking);
}
void UNakatomiCMC::PhysSlide(float deltaTime, int32 Iterations)
{
if (deltaTime < MIN_TICK_TIME) return;
// This is probably not needed for Sliding but including for completeness, will likely remove later.
RestorePreAdditiveRootMotionVelocity();
FHitResult SurfaceHit;
if (!GetSlideSurface(SurfaceHit) || Velocity.SizeSquared() < pow(Slide_MinSpeed, 2))
{
ExitSlide();
StartNewPhysics(deltaTime, Iterations);
}
// Surface Gravity
Velocity += Slide_GravityForce * FVector::DownVector * deltaTime;
// Calculate Strafe
if (FMath::Abs(FVector::DotProduct(Acceleration.GetSafeNormal(), UpdatedComponent->GetRightVector())) > 0.5f)
{
Acceleration = Acceleration.ProjectOnTo(UpdatedComponent->GetRightVector());
}
else
{
Acceleration = FVector::ZeroVector;
}
// Calculate Velocity
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
{
CalcVelocity(deltaTime, Slide_Friction, true, GetMaxBrakingDeceleration());
}
ApplyRootMotionToVelocity(deltaTime);
// Perform Move
Iterations++;
bJustTeleported = false;
FVector OldLocation = UpdatedComponent->GetComponentLocation();
FQuat OldRotation = UpdatedComponent->GetComponentRotation().Quaternion();
FHitResult Hit(1.f);
FVector AdjustedVelocity = Velocity * deltaTime;
FVector VelocityPlaneDirection = FVector::VectorPlaneProject(Velocity, SurfaceHit.Normal).GetSafeNormal();
FQuat NewRotation = FRotationMatrix::MakeFromXZ(VelocityPlaneDirection, SurfaceHit.Normal).ToQuat();
SafeMoveUpdatedComponent(AdjustedVelocity, NewRotation, true, Hit);
// Post Move Checks
if (Hit.Time < 1.f)
{
HandleImpact(Hit, deltaTime, AdjustedVelocity);
SlideAlongSurface(AdjustedVelocity, (1.f - Hit.Time), Hit.Normal, Hit, true);
}
FHitResult NewSurfaceHit;
if (!GetSlideSurface(NewSurfaceHit) || Velocity.SizeSquared() < pow(Slide_MinSpeed, 2))
{
ExitSlide();
}
// Update outgoing Velocity and Acceleration
if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
{
Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / deltaTime;
}
}
bool UNakatomiCMC::GetSlideSurface(FHitResult& Hit) const
{
const FVector Start = UpdatedComponent->GetComponentLocation();
const FVector End = Start + CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() * 2.f * FVector::DownVector;
const FName ProfileName = TEXT("BlockAll");
return GetWorld()->LineTraceSingleByProfile(Hit, Start, End, ProfileName);
}
void UNakatomiCMC::OnDashCooldownFinished()
{
Safe_bWantsToDash = true;
}
bool UNakatomiCMC::CanDash()
{
return (IsWalking() || IsFalling()) && !IsCrouching() && !Safe_bWantsToAds;
}
void UNakatomiCMC::PerformDash()
{
DashStartTime = GetWorld()->GetTimeSeconds();
FVector DashDirection = (Acceleration.IsNearlyZero() ? UpdatedComponent->GetForwardVector() : Acceleration).GetSafeNormal2D();
Velocity = Dash_Impulse * (DashDirection + FVector::UpVector * .1f);
FQuat NewRotation = FRotationMatrix::MakeFromXZ(DashDirection, FVector::UpVector).ToQuat();
FHitResult Hit;
SafeMoveUpdatedComponent(FVector::ZeroVector, NewRotation, false, Hit);
SetMovementMode(MOVE_Falling);
DashStartDelegate.Broadcast();
}

View File

@ -0,0 +1,176 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Nakatomi.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "NakatomiCMC.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDashStartDelegate);
UENUM(BlueprintType)
enum ECustomMovementMove
{
CMOVE_None UMETA(Hidden),
CMOVE_Slide UMETA(DisplayName = "Slide"),
CMOVE_Dash UMETA(DisplayName = "Dash"),
CMOVE_MAX UMETA(Hidden),
};
/**
*
*/
UCLASS()
class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent
{
GENERATED_BODY()
class FSavedMove_Nakatomi : public FSavedMove_Character
{
using Super = FSavedMove_Character;
public:
enum CompressedFlags
{
FLAG_Sprint = 0x10,
FLAG_Dash = 0x20,
FLAG_Custom2 = 0x30,
FLAG_Custom3 = 0x40,
};
// Flag
uint8 Saved_bWantsToSprint : 1;
uint8 Saved_bWantsToSlide : 1;
uint8 Saved_bWantsToAds : 1;
uint8 Saved_bWantsToDash : 1;
virtual bool
CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const override;
virtual void Clear() override;
virtual uint8 GetCompressedFlags() const override;
virtual void SetMoveFor(ACharacter* C, float InDeltaTime, const FVector& NewAccel,
FNetworkPredictionData_Client_Character& ClientData) override;
virtual void PrepMoveFor(ACharacter* C) override;
};
class FNetworkPredictionData_Client_Nakatomi : public FNetworkPredictionData_Client_Character
{
public:
FNetworkPredictionData_Client_Nakatomi(const UCharacterMovementComponent& ClientMovement);
using Super = FNetworkPredictionData_Client_Character;
virtual FSavedMovePtr AllocateNewMove() override;
};
UPROPERTY(EditDefaultsOnly)
float Sprint_MaxWalkSpeed = 1000.0f;
UPROPERTY(EditDefaultsOnly)
float Walk_MaxWalkSpeed = 500.0f;
UPROPERTY(EditDefaultsOnly)
float Crouch_MaxWalkSpeed = 250.0f;
UPROPERTY(EditDefaultsOnly)
float Slide_MinSpeed = 50.f;
UPROPERTY(EditDefaultsOnly)
float Slide_EnterImpulse = 2000.f;
UPROPERTY(EditDefaultsOnly)
float Slide_GravityForce = 2500.f;
UPROPERTY(EditDefaultsOnly)
float Slide_Friction = 1.f;
UPROPERTY(EditDefaultsOnly)
float Ads_Multiplier = 0.5f;
UPROPERTY(EditDefaultsOnly)
float Dash_Impulse = 1500.0f;
UPROPERTY(EditDefaultsOnly)
float Dash_CooldownDuration = 1.0f;
bool Safe_bWantsToSprint;
bool Safe_bWantsToSlide;
bool Safe_bWantsToAds;
bool Safe_bWantsToDash;
float DashStartTime;
FTimerHandle TimerHandle_DashCooldown;
UPROPERTY(Transient)
ANakatomiCharacter* NakatomiCharacterOwner;
public:
UPROPERTY(BlueprintAssignable)
FDashStartDelegate DashStartDelegate;
public:
UNakatomiCMC();
protected:
virtual void InitializeComponent() override;
virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;
protected:
virtual void UpdateFromCompressedFlags(uint8 Flags) override;
virtual void OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity) override;
virtual bool IsMovingOnGround() const override;
virtual bool CanCrouchInCurrentState() const override;
virtual void UpdateCharacterStateBeforeMovement(float DeltaSeconds) override;
virtual void PhysCustom(float deltaTime, int32 Iterations) override;
public:
UFUNCTION(BlueprintCallable)
void EnableSprint();
UFUNCTION(BlueprintCallable)
void DisableSprint();
UFUNCTION(BlueprintCallable)
void EnableCrouch();
UFUNCTION(BlueprintCallable)
void DisableCrouch();
UFUNCTION(BlueprintCallable)
void EnableSlide();
UFUNCTION(BlueprintCallable)
void DisableSlide();
UFUNCTION(BlueprintCallable)
void EnableAds();
UFUNCTION(BlueprintCallable)
void DisableAds();
UFUNCTION(BlueprintCallable)
void EnableDash();
UFUNCTION(BlueprintCallable)
void DisableDash();
UFUNCTION()
bool IsCustomMovementMode(ECustomMovementMove InCustomMovementMode) const;
private:
void EnterSlide();
void ExitSlide();
void PhysSlide(float deltaTime, int32 Iterations); // Every movement mode requires a physics function to work
bool GetSlideSurface(FHitResult& Hit) const;
void OnDashCooldownFinished();
bool CanDash();
void PerformDash();
};

View File

@ -3,12 +3,17 @@
#include "NakatomiCharacter.h" #include "NakatomiCharacter.h"
#include "NakatomiCMC.h"
// Sets default values // Sets default values
ANakatomiCharacter::ANakatomiCharacter() ANakatomiCharacter::ANakatomiCharacter(const FObjectInitializer& ObjectInitializer) : Super(
ObjectInitializer.SetDefaultSubobjectClass<UNakatomiCMC>(ACharacter::CharacterMovementComponentName))
{ {
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it. // Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bCanEverTick = true;
NakatomiCMC = Cast<UNakatomiCMC>(GetCharacterMovement());
HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("Health Component")); HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("Health Component"));
HealthComponent->OnDamaged.BindUFunction(this, "OnDamaged"); HealthComponent->OnDamaged.BindUFunction(this, "OnDamaged");
HealthComponent->OnDeath.BindUFunction(this, "OnDeath"); HealthComponent->OnDeath.BindUFunction(this, "OnDeath");
@ -20,6 +25,8 @@ void ANakatomiCharacter::BeginPlay()
Super::BeginPlay(); Super::BeginPlay();
SetInventoryToDefault(); SetInventoryToDefault();
NakatomiCMC = Cast<UNakatomiCMC>(GetCharacterMovement());
} }
// Called every frame // Called every frame
@ -232,6 +239,11 @@ void ANakatomiCharacter::PushThrowableToInventory(TSubclassOf<AThrowable> Throwa
} }
} }
UNakatomiCMC* ANakatomiCharacter::GetCharacterMovementComponent()
{
return NakatomiCMC;
}
void ANakatomiCharacter::CalculateHits(TArray<FHitResult>* hits) void ANakatomiCharacter::CalculateHits(TArray<FHitResult>* hits)
{ {
} }

View File

@ -5,6 +5,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GameFramework/Character.h" #include "GameFramework/Character.h"
#include "HealthComponent.h" #include "HealthComponent.h"
#include "Nakatomi.h"
#include "Throwable.h" #include "Throwable.h"
#include "Weapon.h" #include "Weapon.h"
#include "NakatomiCharacter.generated.h" #include "NakatomiCharacter.generated.h"
@ -38,6 +39,10 @@ public:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
TArray<TSubclassOf<AThrowable>> ThrowableInventory; TArray<TSubclassOf<AThrowable>> ThrowableInventory;
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Meta = (AllowPrivateAccess = "true"))
UNakatomiCMC* NakatomiCMC;
private: private:
UPROPERTY(VisibleDefaultsOnly) UPROPERTY(VisibleDefaultsOnly)
UHealthComponent* HealthComponent = nullptr; UHealthComponent* HealthComponent = nullptr;
@ -47,9 +52,10 @@ private:
UPROPERTY(EditDefaultsOnly) UPROPERTY(EditDefaultsOnly)
int MaximumThrowableInventorySize = 4; int MaximumThrowableInventorySize = 4;
public: public:
// Sets default values for this character's properties // Sets default values for this character's properties
ANakatomiCharacter(); ANakatomiCharacter(const FObjectInitializer& ObjectInitializer);
protected: protected:
// Called when the game starts or when spawned // Called when the game starts or when spawned
@ -98,6 +104,8 @@ public:
void PushThrowableToInventory(TSubclassOf<AThrowable> Throwable); void PushThrowableToInventory(TSubclassOf<AThrowable> Throwable);
UNakatomiCMC* GetCharacterMovementComponent();
protected: protected:
virtual void CalculateHits(TArray<FHitResult>* hits); virtual void CalculateHits(TArray<FHitResult>* hits);

View File

@ -7,25 +7,26 @@
#include <Components/CapsuleComponent.h> #include <Components/CapsuleComponent.h>
#include <Kismet/GameplayStatics.h> #include <Kismet/GameplayStatics.h>
#include "InputTriggers.h" #include "EnemyCharacter.h"
#include "EnhancedInputComponent.h" #include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h" #include "EnhancedInputSubsystems.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "InputMappingContext.h" #include "InputMappingContext.h"
#include "EnemyCharacter.h" #include "InputTriggers.h"
#include "InteractableComponent.h"
#include "NakatomiCMC.h"
#include "WeaponThrowable.h" #include "WeaponThrowable.h"
#include "GameFramework/CharacterMovementComponent.h"
#define COLLISION_WEAPON ECC_GameTraceChannel1 #define COLLISION_WEAPON ECC_GameTraceChannel1
// Sets default values // Sets default values
APlayerCharacter::APlayerCharacter() APlayerCharacter::APlayerCharacter(const FObjectInitializer& ObjectInitializer) : ANakatomiCharacter(ObjectInitializer)
{ {
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it. // Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.SetTickFunctionEnable(true); PrimaryActorTick.SetTickFunctionEnable(true);
PrimaryActorTick.bStartWithTickEnabled = true; PrimaryActorTick.bStartWithTickEnabled = true;
//bUseControllerRotationPitch = true; //bUseControllerRotationPitch = true;
//bUseControllerRotationYaw = true; //bUseControllerRotationYaw = true;
//bUseControllerRotationRoll = false; //bUseControllerRotationRoll = false;
@ -56,9 +57,8 @@ APlayerCharacter::APlayerCharacter()
CameraADSSpringArmComponent->SocketOffset = {0.0f, 50.0f, 75.0f}; CameraADSSpringArmComponent->SocketOffset = {0.0f, 50.0f, 75.0f};
// Setup the character movement // Setup the character movement
UCharacterMovementComponent* CharacterMovementComponent = GetCharacterMovement(); GetCharacterMovement()->AirControl = 1.0f;
CharacterMovementComponent->AirControl = 1.0f; GetCharacterMovement()->bOrientRotationToMovement = true;
CharacterMovementComponent->bOrientRotationToMovement = true;
// Setup the character perception component // Setup the character perception component
PerceptionSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Perception Source Stimuli")); PerceptionSource = CreateDefaultSubobject<UAIPerceptionStimuliSourceComponent>(TEXT("Perception Source Stimuli"));
@ -188,6 +188,24 @@ void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCom
{ {
Input->BindAction(PauseAction, ETriggerEvent::Completed, this, &APlayerCharacter::PauseCallback); Input->BindAction(PauseAction, ETriggerEvent::Completed, this, &APlayerCharacter::PauseCallback);
} }
if (CrouchAction)
{
Input->BindAction(CrouchAction, ETriggerEvent::Started, this, &APlayerCharacter::BeginCrouchCallback);
Input->BindAction(CrouchAction, ETriggerEvent::Completed, this, &APlayerCharacter::EndCrouchCallback);
}
if (SlideAction)
{
Input->BindAction(SlideAction, ETriggerEvent::Started, this, &APlayerCharacter::BeginSlideCallback);
Input->BindAction(SlideAction, ETriggerEvent::Completed, this, &APlayerCharacter::EndSlideCallback);
}
if (DashAction)
{
Input->BindAction(DashAction, ETriggerEvent::Started, this, &APlayerCharacter::BeginDashCallback);
Input->BindAction(DashAction, ETriggerEvent::Completed, this, &APlayerCharacter::EndDashCallback);
}
} }
} }
@ -269,16 +287,18 @@ void APlayerCharacter::QuitCallback(const FInputActionInstance& Instance)
void APlayerCharacter::SetSprintingCallback(const FInputActionInstance& Instance) void APlayerCharacter::SetSprintingCallback(const FInputActionInstance& Instance)
{ {
IsSpriting = true; if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
SetMovementSpeed(); cmc->EnableSprint();
}
} }
void APlayerCharacter::SetWalkingCallback(const FInputActionInstance& Instance) void APlayerCharacter::SetWalkingCallback(const FInputActionInstance& Instance)
{ {
IsSpriting = false; if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
SetMovementSpeed(); cmc->DisableSprint();
}
} }
void APlayerCharacter::CalculateHits(TArray<FHitResult>* hits) void APlayerCharacter::CalculateHits(TArray<FHitResult>* hits)
@ -448,22 +468,6 @@ void APlayerCharacter::OnDeath()
UGameplayStatics::OpenLevel(this, FName(map), false); UGameplayStatics::OpenLevel(this, FName(map), false);
} }
void APlayerCharacter::SetMovementSpeed()
{
if (IsADS)
{
GetCharacterMovement()->MaxWalkSpeed = DefaultMovementSpeed * ADSSpeedMultiplier;
}
else if (IsSpriting)
{
GetCharacterMovement()->MaxWalkSpeed = DefaultMovementSpeed * SprintSpeedMultiplier;
}
else
{
GetCharacterMovement()->MaxWalkSpeed = DefaultMovementSpeed;
}
}
void APlayerCharacter::WeaponSwitchingCallback(const FInputActionInstance& Instance) void APlayerCharacter::WeaponSwitchingCallback(const FInputActionInstance& Instance)
{ {
float value = Instance.GetValue().Get<float>(); float value = Instance.GetValue().Get<float>();
@ -482,7 +486,10 @@ void APlayerCharacter::BeginAimDownSightsCallback(const FInputActionInstance& In
{ {
IsADS = true; IsADS = true;
SetMovementSpeed(); if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->EnableAds();
}
AimSensitivity = DefaultAimSensitivity * ADSAimSensitivityMultiplier; AimSensitivity = DefaultAimSensitivity * ADSAimSensitivityMultiplier;
@ -505,7 +512,10 @@ void APlayerCharacter::EndAimDownSightsCallback(const FInputActionInstance& Inst
{ {
IsADS = false; IsADS = false;
SetMovementSpeed(); if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->DisableAds();
}
AimSensitivity = DefaultAimSensitivity; AimSensitivity = DefaultAimSensitivity;
@ -535,6 +545,54 @@ void APlayerCharacter::PauseCallback(const FInputActionInstance& Instance)
} }
} }
void APlayerCharacter::BeginCrouchCallback(const FInputActionInstance& Instance)
{
if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->EnableCrouch();
}
}
void APlayerCharacter::EndCrouchCallback(const FInputActionInstance& Instance)
{
if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->DisableCrouch();
}
}
void APlayerCharacter::BeginSlideCallback(const FInputActionInstance& Instance)
{
if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->EnableSlide();
}
}
void APlayerCharacter::EndSlideCallback(const FInputActionInstance& Instance)
{
if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->DisableSlide();
}
}
void APlayerCharacter::BeginDashCallback(const FInputActionInstance& Instance)
{
if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->EnableDash();
}
}
void APlayerCharacter::EndDashCallback(const FInputActionInstance& Instance)
{
if (UNakatomiCMC* cmc = GetCharacterMovementComponent())
{
cmc->DisableDash();
}
}
void APlayerCharacter::OnFire() void APlayerCharacter::OnFire()
{ {
if (!IsFiring || CurrentWeapon->GetAmmoCount() == 0) if (!IsFiring || CurrentWeapon->GetAmmoCount() == 0)
@ -683,4 +741,4 @@ AThrowable* APlayerCharacter::ThrowThrowable()
} }
return nullptr; return nullptr;
} }

View File

@ -2,19 +2,15 @@
#pragma once #pragma once
#include "Camera/CameraComponent.h"
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "GameFramework/SpringArmComponent.h"
#include "InputActionValue.h"
#include "EnhancedInputComponent.h" #include "EnhancedInputComponent.h"
#include "NakatomiCharacter.h" #include "NakatomiCharacter.h"
#include "Weapon.h"
#include "Engine/EngineTypes.h"
#include "Engine/DamageEvents.h"
#include "Blueprint/UserWidget.h"
#include "Perception/AIPerceptionStimuliSourceComponent.h"
#include "InteractableComponent.h"
#include "Throwable.h" #include "Throwable.h"
#include "Blueprint/UserWidget.h"
#include "Camera/CameraComponent.h"
#include "Engine/EngineTypes.h"
#include "GameFramework/SpringArmComponent.h"
#include "Perception/AIPerceptionStimuliSourceComponent.h"
#include "PlayerCharacter.generated.h" #include "PlayerCharacter.generated.h"
class UInputAction; class UInputAction;
@ -63,6 +59,15 @@ public:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UInputAction* AimDownSightsAction; UInputAction* AimDownSightsAction;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UInputAction* CrouchAction;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UInputAction* SlideAction;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
UInputAction* DashAction;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
TSoftObjectPtr<UInputMappingContext> InputMappingContext; TSoftObjectPtr<UInputMappingContext> InputMappingContext;
@ -82,9 +87,6 @@ protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float SprintSpeedMultiplier = 2.0f; float SprintSpeedMultiplier = 2.0f;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float ADSSpeedMultiplier = 0.5f;
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float ADSAimSensitivityMultiplier = 0.5f; float ADSAimSensitivityMultiplier = 0.5f;
@ -94,7 +96,7 @@ protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UPROPERTY(EditDefaultsOnly, BlueprintReadWrite)
float DefaultAimSensitivity = 45.0f; float DefaultAimSensitivity = 45.0f;
private: private:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true")) UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
USpringArmComponent* CameraSpringArmComponent = nullptr; USpringArmComponent* CameraSpringArmComponent = nullptr;
@ -119,7 +121,7 @@ private:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true")) UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
UAIPerceptionStimuliSourceComponent* PerceptionSource; UAIPerceptionStimuliSourceComponent* PerceptionSource;
bool IsSpriting = false; bool IsSprinting = false;
bool IsADS = false; bool IsADS = false;
@ -132,7 +134,7 @@ private:
public: public:
// Sets default values for this character's properties // Sets default values for this character's properties
APlayerCharacter(); APlayerCharacter(const FObjectInitializer& ObjectInitializer);
protected: protected:
// Called when the game starts or when spawned // Called when the game starts or when spawned
@ -170,6 +172,18 @@ public:
void EndAimDownSightsCallback(const FInputActionInstance& Instance); void EndAimDownSightsCallback(const FInputActionInstance& Instance);
void PauseCallback(const FInputActionInstance& Instance); void PauseCallback(const FInputActionInstance& Instance);
void BeginCrouchCallback(const FInputActionInstance& Instance);
void EndCrouchCallback(const FInputActionInstance& Instance);
void BeginSlideCallback(const FInputActionInstance& Instance);
void EndSlideCallback(const FInputActionInstance& Instance);
void BeginDashCallback(const FInputActionInstance& Instance);
void EndDashCallback(const FInputActionInstance& Instance);
virtual void OnFire() override; virtual void OnFire() override;
@ -206,6 +220,5 @@ protected:
virtual void OnDamaged() override; virtual void OnDamaged() override;
virtual void OnDeath() override; virtual void OnDeath() override;
void SetMovementSpeed();
}; };