diff --git a/Content/Input/Actions/IA_Crouch.uasset b/Content/Input/Actions/IA_Crouch.uasset new file mode 100644 index 0000000..9439682 --- /dev/null +++ b/Content/Input/Actions/IA_Crouch.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:419dec41cba9ec94f064280d7d2312e3a9f99bc278ab5cb0931e2e0e58f47850 +size 1340 diff --git a/Content/Input/Actions/IA_Dash.uasset b/Content/Input/Actions/IA_Dash.uasset new file mode 100644 index 0000000..9ad6154 --- /dev/null +++ b/Content/Input/Actions/IA_Dash.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccfb829fc243dda9d932537e1ad8912bc3c6999f221565c8d1b8b01fe48ba667 +size 1330 diff --git a/Content/Input/Actions/IA_Slide.uasset b/Content/Input/Actions/IA_Slide.uasset new file mode 100644 index 0000000..952b084 --- /dev/null +++ b/Content/Input/Actions/IA_Slide.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:994df7bb660cf49d7f7dce90b19640070e8017a9b01d4f4889577c6ca55fb304 +size 1335 diff --git a/Content/Input/InputMappingContext.uasset b/Content/Input/InputMappingContext.uasset index 318dff2..03d1aef 100644 --- a/Content/Input/InputMappingContext.uasset +++ b/Content/Input/InputMappingContext.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b2c749222f80e3692e3deeea1e560981de77784e270c77eac4049875b0f33774 -size 19556 +oid sha256:c13e1a2c13c82a449522bde60ac437bf166d322908030bdb2e5222183dd992eb +size 21703 diff --git a/Content/Player/PlayerCharacter.uasset b/Content/Player/PlayerCharacter.uasset index 37f45d6..5fcf778 100644 --- a/Content/Player/PlayerCharacter.uasset +++ b/Content/Player/PlayerCharacter.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d6df619c920a959f560e32aaaf4195ea7caa5098bbd6a131b48a850e5ce0b3b9 -size 39130 +oid sha256:60bd88a6c8fdadbe4bee0834184310ecb575316c1547af900b0a3a5ee714d608 +size 41493 diff --git a/Source/Nakatomi/EnemyCharacter.cpp b/Source/Nakatomi/EnemyCharacter.cpp index c7b5d01..4a90cc3 100644 --- a/Source/Nakatomi/EnemyCharacter.cpp +++ b/Source/Nakatomi/EnemyCharacter.cpp @@ -5,7 +5,7 @@ #define COLLISION_WEAPON ECC_GameTraceChannel1 -AEnemyCharacter::AEnemyCharacter() +AEnemyCharacter::AEnemyCharacter(const FObjectInitializer& ObjectInitializer) : ANakatomiCharacter(ObjectInitializer) { RandomWeaponParameters = CreateDefaultSubobject(TEXT("Random Weapon Parameters")); diff --git a/Source/Nakatomi/EnemyCharacter.h b/Source/Nakatomi/EnemyCharacter.h index 6bd79fe..4f32743 100644 --- a/Source/Nakatomi/EnemyCharacter.h +++ b/Source/Nakatomi/EnemyCharacter.h @@ -27,7 +27,7 @@ private: FTimerHandle CooldownTimerHandle; public: - AEnemyCharacter(); + AEnemyCharacter(const FObjectInitializer& ObjectInitializer); UBehaviorTree* GetBehaviourTree(); diff --git a/Source/Nakatomi/Nakatomi.h b/Source/Nakatomi/Nakatomi.h index ddbf2e2..038523c 100644 --- a/Source/Nakatomi/Nakatomi.h +++ b/Source/Nakatomi/Nakatomi.h @@ -3,3 +3,6 @@ #pragma once #include "CoreMinimal.h" + +class ANakatomiCharacter; +class UNakatomiCMC; \ No newline at end of file diff --git a/Source/Nakatomi/NakatomiCMC.cpp b/Source/Nakatomi/NakatomiCMC.cpp new file mode 100644 index 0000000..01bfb5e --- /dev/null +++ b/Source/Nakatomi/NakatomiCMC.cpp @@ -0,0 +1,391 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "NakatomiCMC.h" + +#include +#include +#include "NakatomiCharacter.h" + +UNakatomiCMC::UNakatomiCMC(): Safe_bWantsToSprint(false) +{ + NavAgentProps.bCanCrouch = true; +} + +void UNakatomiCMC::InitializeComponent() +{ + Super::InitializeComponent(); + + NakatomiCharacterOwner = Cast(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(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(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(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(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(); +} diff --git a/Source/Nakatomi/NakatomiCMC.h b/Source/Nakatomi/NakatomiCMC.h new file mode 100644 index 0000000..e66d391 --- /dev/null +++ b/Source/Nakatomi/NakatomiCMC.h @@ -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(); +}; diff --git a/Source/Nakatomi/NakatomiCharacter.cpp b/Source/Nakatomi/NakatomiCharacter.cpp index 9c2afdd..4f1e224 100644 --- a/Source/Nakatomi/NakatomiCharacter.cpp +++ b/Source/Nakatomi/NakatomiCharacter.cpp @@ -3,12 +3,17 @@ #include "NakatomiCharacter.h" +#include "NakatomiCMC.h" + // Sets default values -ANakatomiCharacter::ANakatomiCharacter() +ANakatomiCharacter::ANakatomiCharacter(const FObjectInitializer& ObjectInitializer) : Super( + ObjectInitializer.SetDefaultSubobjectClass(ACharacter::CharacterMovementComponentName)) { // 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; + NakatomiCMC = Cast(GetCharacterMovement()); + HealthComponent = CreateDefaultSubobject(TEXT("Health Component")); HealthComponent->OnDamaged.BindUFunction(this, "OnDamaged"); HealthComponent->OnDeath.BindUFunction(this, "OnDeath"); @@ -20,6 +25,8 @@ void ANakatomiCharacter::BeginPlay() Super::BeginPlay(); SetInventoryToDefault(); + + NakatomiCMC = Cast(GetCharacterMovement()); } // Called every frame @@ -232,6 +239,11 @@ void ANakatomiCharacter::PushThrowableToInventory(TSubclassOf Throwa } } +UNakatomiCMC* ANakatomiCharacter::GetCharacterMovementComponent() +{ + return NakatomiCMC; +} + void ANakatomiCharacter::CalculateHits(TArray* hits) { } diff --git a/Source/Nakatomi/NakatomiCharacter.h b/Source/Nakatomi/NakatomiCharacter.h index 0c368f1..574f69d 100644 --- a/Source/Nakatomi/NakatomiCharacter.h +++ b/Source/Nakatomi/NakatomiCharacter.h @@ -5,6 +5,7 @@ #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "HealthComponent.h" +#include "Nakatomi.h" #include "Throwable.h" #include "Weapon.h" #include "NakatomiCharacter.generated.h" @@ -38,6 +39,10 @@ public: UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) TArray> ThrowableInventory; +protected: + UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Meta = (AllowPrivateAccess = "true")) + UNakatomiCMC* NakatomiCMC; + private: UPROPERTY(VisibleDefaultsOnly) UHealthComponent* HealthComponent = nullptr; @@ -47,9 +52,10 @@ private: UPROPERTY(EditDefaultsOnly) int MaximumThrowableInventorySize = 4; + public: // Sets default values for this character's properties - ANakatomiCharacter(); + ANakatomiCharacter(const FObjectInitializer& ObjectInitializer); protected: // Called when the game starts or when spawned @@ -98,6 +104,8 @@ public: void PushThrowableToInventory(TSubclassOf Throwable); + UNakatomiCMC* GetCharacterMovementComponent(); + protected: virtual void CalculateHits(TArray* hits); diff --git a/Source/Nakatomi/PlayerCharacter.cpp b/Source/Nakatomi/PlayerCharacter.cpp index 1ce9d3d..7f55536 100644 --- a/Source/Nakatomi/PlayerCharacter.cpp +++ b/Source/Nakatomi/PlayerCharacter.cpp @@ -7,25 +7,26 @@ #include #include -#include "InputTriggers.h" +#include "EnemyCharacter.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" -#include "GameFramework/CharacterMovementComponent.h" #include "InputMappingContext.h" -#include "EnemyCharacter.h" +#include "InputTriggers.h" +#include "InteractableComponent.h" +#include "NakatomiCMC.h" #include "WeaponThrowable.h" - +#include "GameFramework/CharacterMovementComponent.h" #define COLLISION_WEAPON ECC_GameTraceChannel1 // 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. PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.SetTickFunctionEnable(true); PrimaryActorTick.bStartWithTickEnabled = true; - + //bUseControllerRotationPitch = true; //bUseControllerRotationYaw = true; //bUseControllerRotationRoll = false; @@ -56,9 +57,8 @@ APlayerCharacter::APlayerCharacter() CameraADSSpringArmComponent->SocketOffset = {0.0f, 50.0f, 75.0f}; // Setup the character movement - UCharacterMovementComponent* CharacterMovementComponent = GetCharacterMovement(); - CharacterMovementComponent->AirControl = 1.0f; - CharacterMovementComponent->bOrientRotationToMovement = true; + GetCharacterMovement()->AirControl = 1.0f; + GetCharacterMovement()->bOrientRotationToMovement = true; // Setup the character perception component PerceptionSource = CreateDefaultSubobject(TEXT("Perception Source Stimuli")); @@ -188,6 +188,24 @@ void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputCom { 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) { - IsSpriting = true; - - SetMovementSpeed(); + if (UNakatomiCMC* cmc = GetCharacterMovementComponent()) + { + cmc->EnableSprint(); + } } void APlayerCharacter::SetWalkingCallback(const FInputActionInstance& Instance) { - IsSpriting = false; - - SetMovementSpeed(); + if (UNakatomiCMC* cmc = GetCharacterMovementComponent()) + { + cmc->DisableSprint(); + } } void APlayerCharacter::CalculateHits(TArray* hits) @@ -448,22 +468,6 @@ void APlayerCharacter::OnDeath() 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) { float value = Instance.GetValue().Get(); @@ -482,7 +486,10 @@ void APlayerCharacter::BeginAimDownSightsCallback(const FInputActionInstance& In { IsADS = true; - SetMovementSpeed(); + if (UNakatomiCMC* cmc = GetCharacterMovementComponent()) + { + cmc->EnableAds(); + } AimSensitivity = DefaultAimSensitivity * ADSAimSensitivityMultiplier; @@ -505,7 +512,10 @@ void APlayerCharacter::EndAimDownSightsCallback(const FInputActionInstance& Inst { IsADS = false; - SetMovementSpeed(); + if (UNakatomiCMC* cmc = GetCharacterMovementComponent()) + { + cmc->DisableAds(); + } 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() { if (!IsFiring || CurrentWeapon->GetAmmoCount() == 0) @@ -683,4 +741,4 @@ AThrowable* APlayerCharacter::ThrowThrowable() } return nullptr; -} +} \ No newline at end of file diff --git a/Source/Nakatomi/PlayerCharacter.h b/Source/Nakatomi/PlayerCharacter.h index 7d4323d..6405782 100644 --- a/Source/Nakatomi/PlayerCharacter.h +++ b/Source/Nakatomi/PlayerCharacter.h @@ -2,19 +2,15 @@ #pragma once -#include "Camera/CameraComponent.h" #include "CoreMinimal.h" -#include "GameFramework/SpringArmComponent.h" -#include "InputActionValue.h" #include "EnhancedInputComponent.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 "Blueprint/UserWidget.h" +#include "Camera/CameraComponent.h" +#include "Engine/EngineTypes.h" +#include "GameFramework/SpringArmComponent.h" +#include "Perception/AIPerceptionStimuliSourceComponent.h" #include "PlayerCharacter.generated.h" class UInputAction; @@ -63,6 +59,15 @@ public: UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) UInputAction* AimDownSightsAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* CrouchAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* SlideAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* DashAction; UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) TSoftObjectPtr InputMappingContext; @@ -82,9 +87,6 @@ protected: UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) float SprintSpeedMultiplier = 2.0f; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) - float ADSSpeedMultiplier = 0.5f; - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) float ADSAimSensitivityMultiplier = 0.5f; @@ -94,7 +96,7 @@ protected: UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) float DefaultAimSensitivity = 45.0f; -private: +private: UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true")) USpringArmComponent* CameraSpringArmComponent = nullptr; @@ -119,7 +121,7 @@ private: UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true")) UAIPerceptionStimuliSourceComponent* PerceptionSource; - bool IsSpriting = false; + bool IsSprinting = false; bool IsADS = false; @@ -132,7 +134,7 @@ private: public: // Sets default values for this character's properties - APlayerCharacter(); + APlayerCharacter(const FObjectInitializer& ObjectInitializer); protected: // Called when the game starts or when spawned @@ -170,6 +172,18 @@ public: void EndAimDownSightsCallback(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; @@ -206,6 +220,5 @@ protected: virtual void OnDamaged() override; virtual void OnDeath() override; - - void SetMovementSpeed(); + };