OLS/Source/OLSAnimation/Private/Components/OLSLocomotionComponent.cpp

304 lines
8.0 KiB
C++
Raw Normal View History

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-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);
}
}
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;
}