This commit is contained in:
sSebster 2025-03-12 10:21:20 +01:00
commit e6733f4c16
9 changed files with 464 additions and 8 deletions

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

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,41 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Player/LMCameraManager.h"
#include "Components/CapsuleComponent.h"
#include "Player/LMMovementComponent.h"
#include "Player/LMPlayer.h"
ALMCameraManager::ALMCameraManager()
{
}
void ALMCameraManager::UpdateViewTarget(FTViewTarget& OutVT, float DeltaTime)
{
Super::UpdateViewTarget(OutVT, DeltaTime);
if (ALMPlayer* Player = Cast<ALMPlayer>(GetOwningPlayerController()->GetPawn()))
{
ULMMovementComponent* MovementComponent = Player->GetLegumixMovementComponent();
FVector TargetCrouchOffset = FVector(0, 0,
MovementComponent->GetCrouchedHalfHeight() - Player->GetClass()->GetDefaultObject<ACharacter>()->GetCapsuleComponent()->GetScaledCapsuleHalfHeight());
FVector Offset = FMath::Lerp(FVector::ZeroVector, TargetCrouchOffset, FMath::Clamp(CrouchBlendTime / CrouchBlendDuration, 0.f, 1.f));
if (MovementComponent->IsCrouching())
{
CrouchBlendTime = FMath::Clamp(CrouchBlendTime + DeltaTime, 0.f, CrouchBlendDuration);
Offset -= TargetCrouchOffset;
}
else
{
CrouchBlendTime = FMath::Clamp(CrouchBlendTime - DeltaTime, 0.f, CrouchBlendDuration);
}
if (MovementComponent->IsMovingOnGround())
{
OutVT.POV.Location += Offset;
}
}
}

View File

@ -0,0 +1,264 @@
// 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

View File

@ -11,11 +11,15 @@
#include "Player/LMBulletInfo.h"
#include "Player/LMHealthComponent.h"
#include "Player/LMHitBox.h"
#include "Player/LMMovementComponent.h"
#include "Weapon/LMWeaponManager.h"
ALMPlayer::ALMPlayer()
ALMPlayer::ALMPlayer(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer.SetDefaultSubobjectClass<ULMMovementComponent>(CharacterMovementComponentName))
{
LegumixMovementComponent = Cast<ULMMovementComponent>(GetCharacterMovement());
PrimaryActorTick.bCanEverTick = true;
SpreadStream = FRandomStream(FMath::Rand());
@ -217,4 +221,15 @@ void ALMPlayer::SetPlayerViewOcclusionPercent()
NextPlayerViewOcclusionPercent = CumulatedDamagesOnCurrentHitPeriod / HealthAtTheCurrentHitPeriodBeginning;
}
FCollisionQueryParams ALMPlayer::GetIgnoreCharacterParams()
{
FCollisionQueryParams Params;
TArray<AActor*> ChildrenActors;
GetAllChildActors(ChildrenActors);
Params.AddIgnoredActors(ChildrenActors);
Params.AddIgnoredActor(this);
return Params;
}

View File

@ -0,0 +1,9 @@
#pragma once
UENUM(BlueprintType)
enum ECustomMovementModes
{
ECMM_None UMETA(Hidden),
ECMM_Sliding UMETA(DisplayName = "Sliding"),
ECMM_Max UMETA(Hidden),
};

View File

@ -0,0 +1,26 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Camera/PlayerCameraManager.h"
#include "LMCameraManager.generated.h"
/**
*
*/
UCLASS()
class LEGUMEMIX_API ALMCameraManager : public APlayerCameraManager
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Legumix, meta=(AllowPrivateAccess=true))
float CrouchBlendDuration = 0.5f;
float CrouchBlendTime = 0.f;
public:
ALMCameraManager();
virtual void UpdateViewTarget(FTViewTarget& OutVT, float DeltaTime) override;
};

View File

@ -0,0 +1,94 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "LMCustomMovementModes.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "LMMovementComponent.generated.h"
class ALMPlayer;
// See https://www.youtube.com/playlist?list=PLXJlkahwiwPmeABEhjwIALvxRSZkzoQpk
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class LEGUMEMIX_API ULMMovementComponent : public UCharacterMovementComponent
{
GENERATED_BODY()
class FSavedMove_Legumix : public FSavedMove_Character
{
public:
uint8 Saved_bPrevWantsToCrouch:1;
FSavedMove_Legumix();
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, FVector const& NewAccel, class FNetworkPredictionData_Client_Character& ClientData) override;
virtual void PrepMoveFor(ACharacter* C) override;
};
class FNetworkPredictionData_Client_Legumix : public FNetworkPredictionData_Client_Character
{
public:
FNetworkPredictionData_Client_Legumix(const UCharacterMovementComponent& ClientMovement);
typedef FNetworkPredictionData_Client_Character Super;
virtual FSavedMovePtr AllocateNewMove() override;
};
bool Safe_bPrevWantsToCrouch = false;
public:
ULMMovementComponent();
protected:
virtual void InitializeComponent() override;
public:
virtual FNetworkPredictionData_Client* GetPredictionData_Client() const override;
virtual bool IsMovingOnGround() const override;
virtual bool CanCrouchInCurrentState() const override;
protected:
virtual void UpdateFromCompressedFlags(uint8 Flags) override;
public:
virtual void UpdateCharacterStateBeforeMovement(float DeltaSeconds) override;
protected:
virtual void OnMovementUpdated(float DeltaSeconds, const FVector& OldLocation, const FVector& OldVelocity) override;
virtual void PhysCustom(float DeltaTime, int32 Iterations) override;
public:
UFUNCTION(BlueprintPure)
bool IsCustomMovementMode(ECustomMovementModes InCustomMovementMode) const;
public:
UPROPERTY(Transient)
TObjectPtr<ALMPlayer> PlayerCharacterOwner;
// Minimum speed require to slide
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Legumix|Slide")
float SlideMinSpeed = 350.f;
// Boost in velocity when entering a slide
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Legumix|Slide")
float SlideEnterImpulse = 500.f;
// Amount of force applied to the player to force it on the ground, also affects the speed of a slide on a slope.
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Legumix|Slide")
float SlideGravityForce = 5000.f;
// How fast you loose velocity as you slide.
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Legumix|Slide")
float SlideFriction = 1.3f;
private:
void EnterSlide();
void ExitSlide();
void PhysSlide(float DeltaTime, int32 Iterations);
bool GetSlideSurface(FHitResult& Hit) const;
UFUNCTION(BlueprintCallable, Category="Legumix|Slide")
void CrouchPressed();
};

View File

@ -9,6 +9,7 @@
#include "GameFramework/Character.h"
#include "LMPlayer.generated.h"
class ULMMovementComponent;
class UCameraComponent;
class ULMWeaponManager;
class ULMHealthComponent;
@ -22,9 +23,15 @@ class LEGUMEMIX_API ALMPlayer : public ACharacter
{
GENERATED_BODY()
protected:
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=Movement)
TObjectPtr<ULMMovementComponent> LegumixMovementComponent;
public:
// Sets default values for this character's properties
ALMPlayer();
ALMPlayer(const FObjectInitializer& ObjectInitializer);
ULMMovementComponent* GetLegumixMovementComponent() { return LegumixMovementComponent; }
UFUNCTION(BlueprintCallable)
bool PickUpAmmo(ALMAmmo* Ammo);
@ -92,6 +99,8 @@ public:
UFUNCTION(BlueprintNativeEvent, BlueprintCallable)
void OnPause(bool Paused);
FCollisionQueryParams GetIgnoreCharacterParams();
public:
UFUNCTION(BlueprintImplementableEvent)
@ -189,8 +198,6 @@ private:
UFUNCTION(BlueprintCallable, Category=PostProcess)
void SetPlayerViewOcclusionPercent();
private:
#if WITH_EDITORONLY_DATA
/** If set, bullet debug will be drawn. */