Add initial custom slide movement implementation

This commit is contained in:
baz 2024-01-11 19:21:15 +00:00
parent 2cdb97b7ab
commit 293358737c
3 changed files with 212 additions and 3 deletions

View File

@ -3,13 +3,22 @@
#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,
@ -51,6 +60,7 @@ void UNakatomiCMC::FSavedMove_Nakatomi::SetMoveFor(ACharacter* C, float InDeltaT
UNakatomiCMC* CharacterMovement = Cast<UNakatomiCMC>(C->GetCharacterMovement());
Saved_bWantsToSprint = CharacterMovement->Safe_bWantsToSprint;
Saved_bPrevWantsToCrouch = CharacterMovement->Safe_bPrevWantsToCrouch;
}
void UNakatomiCMC::FSavedMove_Nakatomi::PrepMoveFor(ACharacter* C)
@ -60,6 +70,7 @@ void UNakatomiCMC::FSavedMove_Nakatomi::PrepMoveFor(ACharacter* C)
UNakatomiCMC* CharacterMovement = Cast<UNakatomiCMC>(C->GetCharacterMovement());
CharacterMovement->Safe_bWantsToSprint = Saved_bWantsToSprint;
CharacterMovement->Safe_bPrevWantsToCrouch = Saved_bPrevWantsToCrouch;
}
UNakatomiCMC::FNetworkPredictionData_Client_Nakatomi::FNetworkPredictionData_Client_Nakatomi(
@ -110,6 +121,51 @@ void UNakatomiCMC::OnMovementUpdated(float DeltaSeconds, const FVector& OldLocat
MaxWalkSpeed = Walk_MaxWalkSpeed;
}
}
Safe_bPrevWantsToCrouch = bWantsToCrouch;
}
bool UNakatomiCMC::IsMovingOnGround() const
{
return Super::IsMovingOnGround() || IsCustomMovementMode(CMOVE_Slide);
}
bool UNakatomiCMC::CanCrouchInCurrentState() const
{
return Super::CanCrouchInCurrentState() && IsMovingOnGround();
}
void UNakatomiCMC::UpdateCharacterStateBeforeMovement(float DeltaSeconds)
{
if (MovementMode == MOVE_Walking && !bWantsToCrouch && Safe_bPrevWantsToCrouch)
{
FHitResult PotentialSlideSurface;
if (Velocity.SizeSquared() > pow(Slide_MinSpeed, 2) && GetSlideSurface(PotentialSlideSurface))
{
EnterSlide();
}
}
if (IsCustomMovementMode(CMOVE_Slide) && !bWantsToCrouch)
{
ExitSlide();
}
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()
@ -131,3 +187,101 @@ void UNakatomiCMC::DisableCrouch()
{
bWantsToCrouch = false;
}
bool UNakatomiCMC::IsCustomMovementMode(ECustomMovementMove InCustomMovementMode) const
{
return MovementMode == MOVE_Custom && CustomMovementMode == InCustomMovementMode;
}
void UNakatomiCMC::EnterSlide()
{
bWantsToCrouch = true;
Velocity += Velocity.GetSafeNormal2D() * Slide_EnterImpulse;
SetMovementMode(MOVE_Custom, CMOVE_Slide);
}
void UNakatomiCMC::ExitSlide()
{
bWantsToCrouch = 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);
}

View File

@ -3,9 +3,18 @@
#pragma once
#include "CoreMinimal.h"
#include "Nakatomi.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "NakatomiCMC.generated.h"
UENUM(BlueprintType)
enum ECustomMovementMove
{
CMOVE_None UMETA(Hidden),
CMOVE_Slide UMETA(DisplayName = "Slide"),
CMOVE_MAX UMETA(Hidden),
};
/**
*
*/
@ -18,8 +27,11 @@ class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent
{
typedef FSavedMove_Character Super;
// Flag
uint8 Saved_bWantsToSprint:1;
uint8 Saved_bPrevWantsToCrouch:1;
virtual bool CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const override;
virtual void Clear() override;
virtual uint8 GetCompressedFlags() const override;
@ -37,20 +49,50 @@ class NAKATOMI_API UNakatomiCMC : public UCharacterMovementComponent
virtual FSavedMovePtr AllocateNewMove() override;
};
UPROPERTY(EditDefaultsOnly) float Sprint_MaxWalkSpeed;
UPROPERTY(EditDefaultsOnly) float Walk_MaxWalkSpeed;
UPROPERTY(EditDefaultsOnly)
float Sprint_MaxWalkSpeed;
UPROPERTY(EditDefaultsOnly)
float Walk_MaxWalkSpeed;
UPROPERTY(EditDefaultsOnly)
float Slide_MinSpeed = 10.f;
UPROPERTY(EditDefaultsOnly)
float Slide_EnterImpulse = 2000.f;
UPROPERTY(EditDefaultsOnly)
float Slide_GravityForce = 5000.f;
UPROPERTY(EditDefaultsOnly)
float Slide_Friction = 1.3f;
bool Safe_bWantsToSprint;
bool Safe_bPrevWantsToCrouch;
UPROPERTY(Transient)
ANakatomiCharacter* NakatomiCharacterOwner;
public:
UNakatomiCMC();
protected:
void InitializeComponent() override;
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();
@ -63,4 +105,17 @@ public:
UFUNCTION(BlueprintCallable)
void DisableCrouch();
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;
};

View File

@ -5,7 +5,7 @@
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "HealthComponent.h"
#include "NakatomiCMC.h"
#include "Nakatomi.h"
#include "Throwable.h"
#include "Weapon.h"
#include "NakatomiCharacter.generated.h"