This repository has been archived on 2025-04-18. You can view files and clone it, but cannot push or open issues or pull requests.
LegumeMix/Source/LegumeMix/Private/Player/LMMovementComponent.cpp
TjgL 7c7977ed33 Implemented slide mechanic
Signed-off-by: TjgL <lithmoneo@gmail.com>
2025-03-12 10:15:56 +01:00

265 lines
7.6 KiB
C++

// Fill out your copyright notice in the Description page of Project Settings.
#include "Player/LMMovementComponent.h"
#include "Components/CapsuleComponent.h"
#include "Player/LMPlayer.h"
#pragma region Saved Move
ULMMovementComponent::FSavedMove_Legumix::FSavedMove_Legumix()
{
Saved_bPrevWantsToCrouch = 0;
}
// Checks the current move and the new move to see if they can be combined
bool ULMMovementComponent::FSavedMove_Legumix::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const
{
return FSavedMove_Character::CanCombineWith(NewMove, InCharacter, MaxDelta);
}
void ULMMovementComponent::FSavedMove_Legumix::Clear()
{
FSavedMove_Character::Clear();
}
uint8 ULMMovementComponent::FSavedMove_Legumix::GetCompressedFlags() const
{
return FSavedMove_Character::GetCompressedFlags();
}
void ULMMovementComponent::FSavedMove_Legumix::SetMoveFor(ACharacter* C, float InDeltaTime, FVector const& NewAccel, FNetworkPredictionData_Client_Character& ClientData)
{
FSavedMove_Character::SetMoveFor(C, InDeltaTime, NewAccel, ClientData);
const ULMMovementComponent* CharacterMovement = Cast<ULMMovementComponent>(C->GetCharacterMovement());
Saved_bPrevWantsToCrouch = CharacterMovement->Safe_bPrevWantsToCrouch;
}
void ULMMovementComponent::FSavedMove_Legumix::PrepMoveFor(ACharacter* C)
{
FSavedMove_Character::PrepMoveFor(C);
ULMMovementComponent* CharacterMovement = Cast<ULMMovementComponent>(C->GetCharacterMovement());
CharacterMovement->Safe_bPrevWantsToCrouch = Saved_bPrevWantsToCrouch;
}
#pragma endregion
#pragma region Client Network Prediction Data
ULMMovementComponent::FNetworkPredictionData_Client_Legumix::FNetworkPredictionData_Client_Legumix(const UCharacterMovementComponent& ClientMovement)
: Super(ClientMovement)
{
}
FSavedMovePtr ULMMovementComponent::FNetworkPredictionData_Client_Legumix::AllocateNewMove()
{
return FSavedMovePtr(new FSavedMove_Legumix());
}
#pragma endregion
#pragma region CMC
ULMMovementComponent::ULMMovementComponent()
{
PrimaryComponentTick.bCanEverTick = true;
NavAgentProps.bCanCrouch = true;
}
FNetworkPredictionData_Client* ULMMovementComponent::GetPredictionData_Client() const
{
check(PawnOwner != nullptr);
if (ClientPredictionData == nullptr)
{
ULMMovementComponent* MutableThis = const_cast<ULMMovementComponent*>(this);
MutableThis->ClientPredictionData = new FNetworkPredictionData_Client_Legumix(*this);
MutableThis->ClientPredictionData->MaxSmoothNetUpdateDist = 92.f;
MutableThis->ClientPredictionData->MaxSmoothNetUpdateDist = 140.f;
}
return ClientPredictionData;
}
void ULMMovementComponent::InitializeComponent()
{
Super::InitializeComponent();
CharacterOwner = Cast<ALMPlayer>(GetOwner());
}
void ULMMovementComponent::UpdateFromCompressedFlags(uint8 Flags)
{
Super::UpdateFromCompressedFlags(Flags);
}
void ULMMovementComponent::OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity)
{
Super::OnMovementUpdated(DeltaSeconds, OldLocation, OldVelocity);
Safe_bPrevWantsToCrouch = bWantsToCrouch;
}
bool ULMMovementComponent::IsMovingOnGround() const
{
return Super::IsMovingOnGround() || IsCustomMovementMode(ECustomMovementModes::ECMM_Sliding);
}
bool ULMMovementComponent::CanCrouchInCurrentState() const
{
return Super::CanCrouchInCurrentState() && IsMovingOnGround();
}
void ULMMovementComponent::UpdateCharacterStateBeforeMovement(float DeltaSeconds)
{
UE_LOG(LogTemp, Log, TEXT("Velocity %f vs %f"), Velocity.SizeSquared(), pow(SlideMinSpeed, 2))
if (MovementMode == EMovementMode::MOVE_Walking && Safe_bPrevWantsToCrouch)
{
FHitResult PotentialSlideSurface;
UE_LOG(LogTemp, Display, TEXT("PotentialSlideSurface"));
if (Velocity.SizeSquared() > pow(SlideMinSpeed, 2) && GetSlideSurface(PotentialSlideSurface))
{
EnterSlide();
}
}
if (IsCustomMovementMode(ECustomMovementModes::ECMM_Sliding) && !bWantsToCrouch)
{
ExitSlide();
}
Super::UpdateCharacterStateBeforeMovement(DeltaSeconds);
}
void ULMMovementComponent::PhysCustom(float DeltaTime, int32 Iterations)
{
Super::PhysCustom(DeltaTime, Iterations);
switch (CustomMovementMode)
{
case ECustomMovementModes::ECMM_Sliding:
PhysSlide(DeltaTime, Iterations);
break;
default:
UE_LOG(LogTemp, Fatal, TEXT("Invalid Movement Mode"))
}
}
bool ULMMovementComponent::IsCustomMovementMode(ECustomMovementModes InCustomMovementMode) const
{
return MovementMode == EMovementMode::MOVE_Custom && CustomMovementMode == InCustomMovementMode;
}
void ULMMovementComponent::EnterSlide()
{
UE_LOG(LogTemp, Error, TEXT("Entering Slide"));
bWantsToCrouch = true;
Velocity += Velocity.GetSafeNormal2D() * SlideEnterImpulse;
SetMovementMode(EMovementMode::MOVE_Custom, ECustomMovementModes::ECMM_Sliding);
}
void ULMMovementComponent::ExitSlide()
{
UE_LOG(LogTemp, Error, TEXT("Exiting Slide"));
bWantsToCrouch = false;
FQuat NewRotation = FRotationMatrix::MakeFromXZ(UpdatedComponent->GetForwardVector().GetSafeNormal2D(), FVector::UpVector).ToQuat();
FHitResult Hit;
SafeMoveUpdatedComponent(FVector::ZeroVector, NewRotation, true, Hit);
SetMovementMode(EMovementMode::MOVE_Walking);
}
void ULMMovementComponent::PhysSlide(float DeltaTime, int32 Iterations)
{
if (DeltaTime < MIN_TICK_TIME)
{
return;
}
// RestorePreAdditiveRootMotionVelocity();
FHitResult SurfaceHit;
if (!GetSlideSurface(SurfaceHit) || Velocity.SizeSquared() < pow(SlideMinSpeed, 2))
{
ExitSlide();
StartNewPhysics(DeltaTime, Iterations);
return;
}
// Surface Gravity
Velocity += SlideGravityForce * FVector::DownVector * DeltaTime;
// Strafe
if (FMath::Abs(FVector::DotProduct(Acceleration.GetSafeNormal(), UpdatedComponent->GetRightVector())) > .5f)
{
Acceleration = Acceleration.ProjectOnTo(UpdatedComponent->GetRightVector());
}
else
{
Acceleration = FVector::ZeroVector;
}
// Calc Velocity
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
{
// bFluid == true because friction isn't applied if we are not in a fluid
CalcVelocity(DeltaTime, SlideFriction, true, GetMaxBrakingDeceleration());
}
// ApplyRootMotionToVelocity(DeltaTime);
// Perform Move
Iterations++;
bJustTeleported = false;
FVector OldLocation = UpdatedComponent->GetComponentLocation();
FQuat OldRotation = UpdatedComponent->GetComponentRotation().Quaternion();
FHitResult Hit(1.f);
FVector Adjusted = Velocity * DeltaTime;
FVector VelPlaneDir = FVector::VectorPlaneProject(Velocity, SurfaceHit.Normal).GetSafeNormal();
FQuat NewRotation = FRotationMatrix::MakeFromXZ(VelPlaneDir, SurfaceHit.Normal).ToQuat();
// The most important method to move the character
SafeMoveUpdatedComponent(Adjusted, NewRotation, true, Hit);
if (Hit.Time < 1.f)
{
HandleImpact(Hit, DeltaTime, Adjusted);
SlideAlongSurface(Adjusted, (1.f - Hit.Time), Hit.Normal, Hit, true);
}
FHitResult NewSurfaceHit;
if (!GetSlideSurface(NewSurfaceHit) || Velocity.SizeSquared() < pow(SlideMinSpeed, 2))
{
ExitSlide();
}
// Update Outgoing Velocity & Acceleration
if (!bJustTeleported && !HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
{
Velocity = (UpdatedComponent->GetComponentLocation() - OldLocation) / DeltaTime;
}
}
bool ULMMovementComponent::GetSlideSurface(FHitResult& Hit) const
{
FVector Start = UpdatedComponent->GetComponentLocation();
FVector End = Start + CharacterOwner->GetCapsuleComponent()->GetScaledCapsuleHalfHeight() * 2.f * FVector::DownVector;
FName ProfileName = TEXT("BlockAll");
return GetWorld()->LineTraceSingleByProfile(Hit, Start, End, ProfileName , PlayerCharacterOwner->GetIgnoreCharacterParams());
}
#pragma endregion
#pragma region Input
void ULMMovementComponent::CrouchPressed()
{
bWantsToCrouch = !bWantsToCrouch;
}
#pragma endregion