2024-09-22 21:11:19 +00:00
|
|
|
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
|
|
|
|
|
|
|
|
|
|
|
#include "Components/OLSLocomotionComponent.h"
|
|
|
|
|
|
|
|
#include "Data/OLSEnumLibrary.h"
|
|
|
|
#include "Interfaces/OLSAnimationInterface.h"
|
|
|
|
#include "Interfaces/OLSMoveableInterface.h"
|
|
|
|
#include "Kismet/KismetMathLibrary.h"
|
|
|
|
|
|
|
|
|
|
|
|
UOLSLocomotionComponent::UOLSLocomotionComponent(const FObjectInitializer& objectInitializer)
|
|
|
|
{
|
|
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::PostInitProperties()
|
|
|
|
{
|
|
|
|
Super::PostInitProperties();
|
|
|
|
|
|
|
|
Gaits.Add(EOLSGait::EWalk, 0.f);
|
|
|
|
Gaits.Add(EOLSGait::EJog, 0.f);
|
|
|
|
Gaits.Add(EOLSGait::ESprint, 0.f);
|
|
|
|
}
|
|
|
|
|
2024-11-07 20:35:04 +00:00
|
|
|
// Called every frame
|
|
|
|
void UOLSLocomotionComponent::TickComponent(float deltaTime, ELevelTick tickType,
|
|
|
|
FActorComponentTickFunction* thisTickFunction)
|
2024-09-22 21:11:19 +00:00
|
|
|
{
|
2024-11-07 20:35:04 +00:00
|
|
|
Super::TickComponent(deltaTime, tickType, thisTickFunction);
|
2024-09-22 21:11:19 +00:00
|
|
|
|
|
|
|
// ...
|
|
|
|
|
2024-11-07 20:35:04 +00:00
|
|
|
UpdateEssentialValues(deltaTime);
|
|
|
|
UpdateGait(deltaTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::Initialize()
|
|
|
|
{
|
2024-09-22 21:11:19 +00:00
|
|
|
if (const TObjectPtr<APawn> owningPawn = Cast<APawn>(GetOwner()))
|
|
|
|
{
|
|
|
|
OwningPawn = owningPawn;
|
|
|
|
MoveableInterface.SetObject(owningPawn);
|
|
|
|
MoveableInterface.SetInterface(Cast<IOLSMoveableInterface>(owningPawn));
|
|
|
|
|
|
|
|
if (const IOLSAnimationInterface* animInterface = Cast<IOLSAnimationInterface>(owningPawn))
|
|
|
|
{
|
|
|
|
OwningAnimInstance = animInterface->GetAnimInstance();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MaxSpeed = Gaits.FindChecked(EOLSGait::EJog);
|
|
|
|
|
|
|
|
SetDesiredGait(DesiredGait, true);
|
|
|
|
SetRotationMode(DesiredRotationMode, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLSGait UOLSLocomotionComponent::GetAllowedGait() const
|
|
|
|
{
|
|
|
|
if (Stance == EOLSStance::EStanding)
|
|
|
|
{
|
|
|
|
if (RotationMode != EOLSRotationMode::EAiming)
|
|
|
|
{
|
|
|
|
if (DesiredGait == EOLSGait::ESprint)
|
|
|
|
{
|
|
|
|
return CanSprint() ? EOLSGait::ESprint : EOLSGait::EJog;
|
|
|
|
}
|
|
|
|
return DesiredGait;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (DesiredGait == EOLSGait::ESprint)
|
|
|
|
{
|
|
|
|
return EOLSGait::EJog;
|
|
|
|
}
|
|
|
|
|
|
|
|
return DesiredGait;
|
|
|
|
}
|
|
|
|
|
|
|
|
EOLSGait UOLSLocomotionComponent::GetActualGait(const EOLSGait& allowedLocomotionState) const
|
|
|
|
{
|
|
|
|
// This should never happen.
|
|
|
|
if (Gaits.IsEmpty())
|
|
|
|
{
|
|
|
|
return DesiredGait;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (allowedLocomotionState == EOLSGait::ESprint)
|
|
|
|
{
|
|
|
|
return EOLSGait::ESprint;
|
|
|
|
}
|
|
|
|
else if (allowedLocomotionState == EOLSGait::EJog && CanJog())
|
|
|
|
{
|
|
|
|
return EOLSGait::EJog;
|
|
|
|
}
|
|
|
|
|
|
|
|
return EOLSGait::EWalk;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::UpdateEssentialValues(const float deltaTime)
|
|
|
|
{
|
|
|
|
if (!OwningPawn || !MoveableInterface)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// These values represent how the capsule is moving as well as how it wants to move, and therefore are essential
|
|
|
|
// for any data driven animation system. They are also used throughout the system for various functions,
|
|
|
|
// so I found it is easiest to manage them all in one place.
|
|
|
|
|
|
|
|
const FVector& currentVel = GetVelocity();
|
|
|
|
|
|
|
|
// Set the amount of Acceleration.
|
|
|
|
Acceleration = MoveableInterface->GetCurrentAcceleration();
|
|
|
|
|
|
|
|
// Determine if the character is moving by getting it's speed. The Speed equals the length of the horizontal (x y)
|
|
|
|
// velocity, so it does not take vertical movement into account. If the character is moving, update the last
|
|
|
|
// velocity rotation. This value is saved because it might be useful to know the last orientation of movement
|
|
|
|
// even after the character has stopped.
|
|
|
|
Speed = currentVel.Size2D();
|
2024-10-26 01:11:59 +00:00
|
|
|
|
2024-09-22 21:11:19 +00:00
|
|
|
// Determine if the character has movement input by getting its movement input amount.
|
|
|
|
// The Movement Input Amount is equal to the current acceleration divided by the max acceleration so that
|
|
|
|
// it has a range of 0-1, 1 being the maximum possible amount of input, and 0 being none.
|
|
|
|
// If the character has movement input, update the Last Movement Input Rotation.
|
|
|
|
MovementInputAmount = FMath::GetMappedRangeValueClamped(FVector2D(0.f, MoveableInterface->GetMaxAcceleration()),
|
|
|
|
FVector2D(0.f, 1.f),
|
|
|
|
MoveableInterface->GetCurrentAcceleration().Size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::UpdateGait(const float deltaTime)
|
|
|
|
{
|
|
|
|
const EOLSGait allowedLocomotionState = GetAllowedGait();
|
|
|
|
|
|
|
|
const EOLSGait actualLocomotionState = GetActualGait(allowedLocomotionState);
|
|
|
|
|
|
|
|
SetGait(actualLocomotionState);
|
|
|
|
}
|
|
|
|
|
|
|
|
FVector UOLSLocomotionComponent::GetVelocity() const
|
|
|
|
{
|
|
|
|
return (OwningPawn ? OwningPawn->GetVelocity() : FVector::ZeroVector);
|
|
|
|
}
|
|
|
|
|
|
|
|
float UOLSLocomotionComponent::GetAnimCurve(const FName& curveName) const
|
|
|
|
{
|
|
|
|
return (curveName.IsValid() && OwningAnimInstance ? OwningAnimInstance->GetCurveValue(curveName) : 0.f);
|
|
|
|
}
|
|
|
|
|
|
|
|
const EOLSStance& UOLSLocomotionComponent::GetStance() const
|
|
|
|
{
|
|
|
|
return Stance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::SetStance(EOLSStance newStance, bool shouldForce)
|
|
|
|
{
|
|
|
|
if (shouldForce || Stance != newStance)
|
|
|
|
{
|
|
|
|
const EOLSStance prev = Stance;
|
|
|
|
Stance = newStance;
|
|
|
|
OnStanceChanged(prev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const EOLSGait& UOLSLocomotionComponent::GetGait() const
|
|
|
|
{
|
|
|
|
return Gait;
|
|
|
|
}
|
|
|
|
|
|
|
|
const EOLSGait& UOLSLocomotionComponent::GetDesiredGait() const
|
|
|
|
{
|
|
|
|
return DesiredGait;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::SetGait(EOLSGait newGait, bool shouldForce)
|
|
|
|
{
|
|
|
|
if (shouldForce || Gait != newGait)
|
|
|
|
{
|
|
|
|
const EOLSGait prev = Gait;
|
|
|
|
Gait = newGait;
|
|
|
|
OnGaitChanged(prev);
|
|
|
|
|
|
|
|
if (MoveableInterface)
|
|
|
|
{
|
|
|
|
MoveableInterface->SetMaxWalkSpeed(Gaits.FindChecked(newGait));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const EOLSRotationMode& UOLSLocomotionComponent::GetRotationMode() const
|
|
|
|
{
|
|
|
|
return RotationMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::SetRotationMode(EOLSRotationMode newRotationMode, bool shouldForce)
|
|
|
|
{
|
|
|
|
if (shouldForce || RotationMode != newRotationMode)
|
|
|
|
{
|
|
|
|
const EOLSRotationMode prevRotationMode = RotationMode;
|
|
|
|
RotationMode = newRotationMode;
|
|
|
|
OnRotationModeChanged(prevRotationMode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-11-28 17:57:33 +00:00
|
|
|
float UOLSLocomotionComponent::GetMaxSpeedByGait(const EOLSGait gait) const
|
|
|
|
{
|
|
|
|
return Gaits.FindChecked(gait);
|
|
|
|
}
|
|
|
|
|
|
|
|
const float& UOLSLocomotionComponent::GetMovementInputAmount() const
|
|
|
|
{
|
|
|
|
return MovementInputAmount;
|
|
|
|
}
|
|
|
|
|
2024-09-22 21:11:19 +00:00
|
|
|
void UOLSLocomotionComponent::SetDesiredGait(const EOLSGait newGait, const bool shouldForce /* = false */)
|
|
|
|
{
|
|
|
|
if (shouldForce || DesiredGait != newGait)
|
|
|
|
{
|
|
|
|
const EOLSGait& prevDesiredGait = DesiredGait;
|
|
|
|
DesiredGait = newGait;
|
|
|
|
OnDesiredGaitChanged(prevDesiredGait);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::SetDesiredStance(const EOLSStance newStance)
|
|
|
|
{
|
|
|
|
DesiredStance = newStance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::SetDesiredRotationMode(const EOLSRotationMode newRotationMode)
|
|
|
|
{
|
|
|
|
DesiredRotationMode = newRotationMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::OnStanceChanged(EOLSStance previousStance)
|
|
|
|
{
|
|
|
|
OnStanceChangedNativeDelegate.Broadcast(previousStance);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::OnDesiredGaitChanged(EOLSGait previousDesiredGait)
|
|
|
|
{
|
|
|
|
OnDesiredGaitChangedNativeDelegate.Broadcast(previousDesiredGait);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::OnGaitChanged(EOLSGait previousGait)
|
|
|
|
{
|
|
|
|
OnGaitChangedNativeDelegate.Broadcast(previousGait);
|
|
|
|
}
|
|
|
|
|
|
|
|
void UOLSLocomotionComponent::OnRotationModeChanged(EOLSRotationMode prevRotationMode)
|
|
|
|
{
|
|
|
|
OnRotationModeChangedNativeDelegate.Broadcast(prevRotationMode);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UOLSLocomotionComponent::CanSprint() const
|
|
|
|
{
|
|
|
|
if (RotationMode == EOLSRotationMode::EAiming || !MoveableInterface)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool hasValidInput = MovementInputAmount > 0.9f;
|
|
|
|
if (RotationMode == EOLSRotationMode::EVelocityDirection)
|
|
|
|
{
|
|
|
|
return hasValidInput;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (RotationMode == EOLSRotationMode::ELookingDirection)
|
|
|
|
{
|
|
|
|
const TObjectPtr<APawn> owningPawn = Cast<APawn>(GetOwner());
|
|
|
|
if (owningPawn)
|
|
|
|
{
|
|
|
|
const FRotator velocityRot = owningPawn->GetVelocity().ToOrientationRotator();
|
|
|
|
FRotator delta = velocityRot - owningPawn->GetControlRotation();
|
|
|
|
delta.Normalize();
|
|
|
|
return (hasValidInput && FMath::Abs(delta.Yaw) < 50.f);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UOLSLocomotionComponent::CanJog() const
|
|
|
|
{
|
|
|
|
if (!MoveableInterface)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2024-11-14 22:32:46 +00:00
|
|
|
|
2024-11-14 22:47:23 +00:00
|
|
|
const bool isCrouching = (GetStance() == EOLSStance::EStanding);
|
2024-11-14 22:32:46 +00:00
|
|
|
const bool canJog = (MovementInputAmount > .8f && isCrouching);
|
|
|
|
return canJog;
|
2024-09-22 21:11:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FOLSStanceNativeDelegate& UOLSLocomotionComponent::GetOnStanceChangedNativeDelegate()
|
|
|
|
{
|
|
|
|
return OnStanceChangedNativeDelegate;
|
|
|
|
}
|
|
|
|
|
|
|
|
FOLSGaitNativeDelegate& UOLSLocomotionComponent::GetOnDesiredGaitChangedNativeDelegate()
|
|
|
|
{
|
|
|
|
return OnDesiredGaitChangedNativeDelegate;
|
|
|
|
}
|
|
|
|
|
|
|
|
FOLSGaitNativeDelegate& UOLSLocomotionComponent::GetOnGaitChangedNativeDelegate()
|
|
|
|
{
|
|
|
|
return OnGaitChangedNativeDelegate;
|
|
|
|
}
|
|
|
|
|
|
|
|
FOLSRotationModeStateNativeDelegate& UOLSLocomotionComponent::GetOnRotationModeChangedNativeDelegate()
|
|
|
|
{
|
|
|
|
return OnRotationModeChangedNativeDelegate;
|
|
|
|
}
|