Robot Movement WIP

- Robot Movement semi functional for CombinedInput types
This commit is contained in:
Vyktori 2025-04-22 10:14:20 -04:00
parent a028ed8caf
commit e79570aeae
9 changed files with 341 additions and 22 deletions

View File

@ -4,6 +4,7 @@
#include "Gameplay/Robot/Components/Movement/LRMC_Stretch.h"
#include "Actors/MujocoVolumeActor.h"
#include "Gameplay/Robot/LuckyRobotPawnBase.h"
#include "Kismet/KismetMathLibrary.h"
void ULRMC_Stretch::PerformMovement(float DeltaTime, const ERobotInputHandlingMethod InputHandlingMethod, const ERobotInputType InputType, const FVector2D& InputValues)
@ -19,58 +20,119 @@ void ULRMC_Stretch::PerformMovement(float DeltaTime, const ERobotInputHandlingMe
switch (PreviousInputType)
{
case ERobotInputType::None:
StopMovement();
break;
case ERobotInputType::Move:
if (FMath::IsNearlyZero(InputValues.X))
{
PreviousInputType = ERobotInputType::None;
StopMovement();
}
else
{
Move(InputValues.X);
PreviousInputType = ERobotInputType::Move;
Move(DeltaTime, InputValues);
}
break;
case ERobotInputType::Turn:
if (FMath::IsNearlyZero(InputValues.Y))
{
PreviousInputType = ERobotInputType::None;
StopMovement();
}
else
{
Turn(InputValues.Y);
PreviousInputType = ERobotInputType::Turn;
Turn(DeltaTime, InputValues.Y);
}
break;
case ERobotInputType::Pathfinding:
if (FMath::IsNearlyZero(InputValues.X))
{
PreviousInputType = ERobotInputType::None;
}
else
{
PreviousInputType = ERobotInputType::Move;
Move(DeltaTime, InputValues);
}
break;
}
break;
case ERobotInputType::Move:
{
PreviousInputType = ERobotInputType::Move;
Move(InputValues.X);
Move(DeltaTime, InputValues);
}
break;
case ERobotInputType::Turn:
{
PreviousInputType = ERobotInputType::Turn;
Turn(InputValues.Y);
Turn(DeltaTime, InputValues.Y);
}
break;
case ERobotInputType::Pathfinding:
break;
switch (PathfindingInputType) {
case ERobotInputType::None:
break;
case ERobotInputType::Move:
PreviousInputType = ERobotInputType::Move;
Move(DeltaTime, InputValues);
break;
case ERobotInputType::Turn:
PreviousInputType = ERobotInputType::Turn;
Turn(DeltaTime, InputValues.Y);
break;
case ERobotInputType::Pathfinding:
break;
}
}
}
void ULRMC_Stretch::Move(const float InValue) const
void ULRMC_Stretch::Move(const float DeltaTime, const FVector2D& InputValues) const
{
if (GetMujocoVolumeActor())
if (GetOwningRobot() && GetMujocoVolumeActor())
{
GetMujocoVolumeActor()->SetActuatorValueByIndex(0, InValue);
GetMujocoVolumeActor()->SetActuatorValueByIndex(1, InValue);
float Input_0 = InputValues.X;
float Input_1 = InputValues.X;
const float TargetSpeed = GetTargetSpeed();
const FVector Forward = GetRobotForwardVector();
const FVector DirectionToTarget = (PathfindingTargetLocation - GetRobotLocation()).GetSafeNormal();
const float DirectionDot = FVector::DotProduct(Forward, DirectionToTarget);
const bool bRight = FVector::DotProduct(DirectionToTarget, GetRobotRightVector()) >= 0.f;
const float DirectionDelta = FMath::Abs<float>(1.f - DirectionDot);
const float DirectionOffset = FMath::Min<float>(DirectionDelta * TargetSpeed * 10.f, TargetSpeed) * 2.f;
UE_LOG(LogTemp, Warning, TEXT("RobotMove :: DirectionOffset (%f)"), DirectionOffset);
if (!FMath::IsNearlyZero(DirectionOffset, 0.01f))
{
if (bRight)
{
Input_1 -= DirectionOffset;
}
else
{
Input_0 -= DirectionOffset;
}
}
const float TargetSpeedMod = GetTargetSpeedMod() * 0.5f;
Input_0 = FMath::Min<float>(Input_0, GetTargetSpeed()) * TargetSpeedMod;
Input_1 = FMath::Min<float>(Input_1, GetTargetSpeed()) * TargetSpeedMod;
UE_LOG(LogTemp, Warning, TEXT("RobotMove Inputs :: Left [%f] :: Right [%f]"), Input_0, Input_1);
GetMujocoVolumeActor()->SetActuatorValueByIndex(0, Input_0);
GetMujocoVolumeActor()->SetActuatorValueByIndex(1, Input_1);
}
}
void ULRMC_Stretch::Turn(const float InValue) const
void ULRMC_Stretch::Turn(const float DeltaTime, const float InValue) const
{
if (GetMujocoVolumeActor())
{
@ -79,3 +141,12 @@ void ULRMC_Stretch::Turn(const float InValue) const
GetMujocoVolumeActor()->SetActuatorValueByIndex(1, InValue * TurnMod);
}
}
void ULRMC_Stretch::StopMovement()
{
if (GetMujocoVolumeActor())
{
GetMujocoVolumeActor()->SetActuatorValueByIndex(0, 0.f);
GetMujocoVolumeActor()->SetActuatorValueByIndex(1, 0.f);
}
}

View File

@ -3,6 +3,7 @@
#include "Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.h"
#include "Actors/MujocoVolumeActor.h"
#include "Components/SplineComponent.h"
#include "Gameplay/Robot/LuckyRobotPawnBase.h"
#include "Kismet/KismetMathLibrary.h"
@ -19,8 +20,20 @@ void ULuckyRobotMovementComponent::TickComponent(float DeltaTime, enum ELevelTic
if (GetOwningRobot())
{
const FVector CurrentLocation = GetRobotLocation();
CurrentRobotSpeed = UKismetMathLibrary::SafeDivide(FVector::Distance(CurrentLocation, PreviousLocation), DeltaTime);
if (IsPathfinding())
{
HandlePathfindingInputs(DeltaTime);
}
InterpolateSpeedValues(DeltaTime, GetOwningRobot()->GetAccelerationRate(), GetOwningRobot()->GetDecelerationRate());
PerformMovement(DeltaTime, RobotSettings.InputHandlingMethod, GetOwningRobot()->GetCurrentInputType(), CurrentSpeedValues);
PreviousLocation = CurrentLocation;
UE_LOG(LogTemp, Warning, TEXT("CurrentRobotSpeed (%f)"), CurrentRobotSpeed);
}
}
@ -39,6 +52,7 @@ void ULuckyRobotMovementComponent::InitializeMovementComponent(ALuckyRobotPawnBa
{
RobotSettings = GetOwningRobot()->RobotSettings;
MujocoVolumeActor = GetOwningRobot()->GetMujocoVolumeActor();
PreviousLocation = GetRobotLocation();
}
SetComponentTickEnabled(true);
@ -85,10 +99,30 @@ FVector ULuckyRobotMovementComponent::GetRobotRightVector() const
return FVector::ZeroVector;
}
FRobotSplinePath ULuckyRobotMovementComponent::GetNavPath() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->GetCurrentNavPath();
}
return FRobotSplinePath();
}
bool ULuckyRobotMovementComponent::IsPathfinding() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->IsPathfinding();
}
return false;
}
void ULuckyRobotMovementComponent::PerformMovement(float DeltaTime, const ERobotInputHandlingMethod InputHandlingMethod, const ERobotInputType InputType, const FVector2D& SpeedValues)
{
//UE_LOG(LogTemp, Warning, TEXT("ULuckyRobotMovementComponent::PerformMovement IS NOT IMPLEMENTED"));
UE_LOG(LogTemp, Warning, TEXT("ULuckyRobotMovementComponent::PerformMovement [ DeltaTime (%f) :: InputHandlingMethod (%s) :: InputType (%s) :: InputValues (%s) ]"), DeltaTime, *UEnum::GetValueAsString(InputHandlingMethod), *UEnum::GetValueAsString(InputType), *SpeedValues.ToString());
//UE_LOG(LogTemp, Warning, TEXT("ULuckyRobotMovementComponent::PerformMovement [ DeltaTime (%f) :: InputHandlingMethod (%s) :: InputType (%s) :: InputValues (%s) ]"), DeltaTime, *UEnum::GetValueAsString(InputHandlingMethod), *UEnum::GetValueAsString(InputType), *SpeedValues.ToString());
}
void ULuckyRobotMovementComponent::InterpolateSpeedValues(const float DeltaTime, const float Acceleration, const float Deceleration)
@ -98,7 +132,9 @@ void ULuckyRobotMovementComponent::InterpolateSpeedValues(const float DeltaTime,
FVector2D TargetSpeedValues = GetOwningRobot()->GetMovementInputValues() * GetOwningRobot()->GetTargetSpeed();
const FVector Current = FVector(CurrentSpeedValues.X, CurrentSpeedValues.Y, 0.f);
FVector Target = FVector(TargetSpeedValues.X, TargetSpeedValues.Y, 0.f);
const FVector RawTarget = FVector(TargetSpeedValues.X, TargetSpeedValues.Y, 0.f);
FVector Target = IsPathfinding() ? RawTarget.GetClampedToMaxSize(PathfindingTargetSpeed) : RawTarget;
UE_LOG(LogTemp, Warning, TEXT("PathfindingTargetSpeed (%f)"), PathfindingTargetSpeed);
if (RobotSettings.InputHandlingMethod == ERobotInputHandlingMethod::SingleInput)
{
@ -124,6 +160,161 @@ void ULuckyRobotMovementComponent::InterpolateSpeedValues(const float DeltaTime,
CurrentSpeedValues = FVector2D(MoveRate, TurnRate);
UE_LOG(LogTemp, Warning, TEXT("ULuckyRobotMovementComponent::InterpolateSpeedValues (%s)"), *CurrentSpeedValues.ToString());
UE_LOG(LogTemp, Warning, TEXT("ULuckyRobotMovementComponent::InterpolatedSpeedValues (%s)"), *CurrentSpeedValues.ToString());
}
}
void ULuckyRobotMovementComponent::HandlePathfindingInputs(const float DeltaTime)
{
if (GetOwningRobot())
{
if (USplineComponent* SplineComp = GetNavPath().GetSplinePath())
{
constexpr float SplineDistanceTolerance = 15.f;
constexpr float SplineDirectionTolerance = 0.95f;
const bool bSingleInput = GetOwningRobot()->RobotSettings.InputHandlingMethod == ERobotInputHandlingMethod::SingleInput;
const FTransform CurrentTransform = GetOwningRobot()->GetTransform();
const float SplineDistance = SplineComp->GetDistanceAlongSplineAtLocation(CurrentTransform.GetLocation(), ESplineCoordinateSpace::World);
const FVector SplineLocation = SplineComp->GetWorldLocationAtDistanceAlongSpline(SplineDistance);
const float DistanceToSpline = FVector::Distance(CurrentTransform.GetLocation(), SplineLocation);
PathfindingTargetLocation = bSingleInput ? GetSplineTargetPointLocation(SplineDistance) : GetSplineTargetLocation(SplineDistance);
PathfindingDestination = GetOwningRobot()->GetPathfindingDestination();
DrawDebugLine(GetWorld(), PathfindingTargetLocation, PathfindingTargetLocation + FVector(0.f, 0.f, 100.f), bSingleInput ? FColor::Purple : FColor::Yellow, false, 0.02f, 0, 3.f);
DrawDebugLine(GetWorld(), CurrentTransform.GetLocation(), CurrentTransform.GetLocation() + GetRobotForwardVector() * 100.f, FColor::Green, false, 0.02f, 0, 3.f);
DrawDebugLine(GetWorld(), CurrentTransform.GetLocation(), CurrentTransform.GetLocation() + GetRobotForwardVector() * -100.f, FColor::Blue, false, 0.02f, 0, 3.f);
if (FVector::Distance(CurrentTransform.GetLocation(), PathfindingDestination) <= PathfindingDestinationTolerance)
{
GetOwningRobot()->PathfindingComplete(true);
return;
}
PathfindingTargetSpeed = GetSplineSpeed(SplineDistance);
if (DistanceToSpline > SplineDistanceTolerance)
{
if (bSingleInput)
{
const FVector DirectionToSpline = (SplineLocation - CurrentTransform.GetLocation()).GetSafeNormal();
const float DirectionDot = FVector::DotProduct(GetRobotForwardVector(), DirectionToSpline);
if (DirectionDot >= SplineDirectionTolerance)
{
GetOwningRobot()->SetPathfindingInputValues(FVector2D(1.f, 0.f));
PathfindingInputType = ERobotInputType::Move;
}
else
{
const bool bTurnRight = FVector::DotProduct(GetRobotRightVector(), DirectionToSpline) >= 0.f;
const float DirectionDiscrepancy = (2.f - DirectionDot + 1.f) * bTurnRight ? -1.f : 1.f;
const float TurnRate = UKismetMathLibrary::FInterpTo(1.f, DirectionDiscrepancy, DeltaTime, 1.f);
GetOwningRobot()->SetPathfindingInputValues(FVector2D(0.f, TurnRate));
PathfindingInputType = ERobotInputType::Turn;
}
}
}
else
{
const FVector DirectionToPath = (PathfindingTargetLocation - CurrentTransform.GetLocation()).GetSafeNormal();
const float DirectionDot = FVector::DotProduct(GetRobotForwardVector(), DirectionToPath);
UE_LOG(LogTemp, Warning, TEXT("DirectionDot :: %f"), DirectionDot);
const float TurnRate = FVector::DotProduct(GetRobotRightVector(), DirectionToPath) >= 0.f ? -1.f : 1.f;
if (DirectionDot >= SplineDirectionTolerance)
{
const float AdjustedTurnRate = TurnRate * (1.f - DirectionDot);
GetOwningRobot()->SetPathfindingInputValues(FVector2D(1.f, AdjustedTurnRate));
PathfindingInputType = ERobotInputType::Move;
}
else
{
if (bSingleInput)
{
GetOwningRobot()->SetPathfindingInputValues(FVector2D(0.f, TurnRate));
PathfindingInputType = ERobotInputType::Turn;
}
else
{
const float AdjustedTurnRate = TurnRate * (1.f - DirectionDot);
GetOwningRobot()->SetPathfindingInputValues(FVector2D(GetOwningRobot()->GetMovementInputValues().X, AdjustedTurnRate));
PathfindingInputType = ERobotInputType::Move;
}
}
}
}
}
}
FVector ULuckyRobotMovementComponent::GetSplineTargetLocation(const float CurrentDistance) const
{
if (GetNavPath().GetSplinePath())
{
const float ForwardPathCheck = FMath::Clamp<float>(GetTargetSpeed() * 0.5f, 20.f, 50.f);
const float SplineCheckDistance = CurrentDistance + ForwardPathCheck;
if (SplineCheckDistance >= GetNavPath().GetSplinePath()->GetSplineLength())
{
return PathfindingDestination;
}
return GetNavPath().GetSplinePath()->GetWorldLocationAtDistanceAlongSpline(CurrentDistance + ForwardPathCheck);
}
return GetRobotLocation();
}
FVector ULuckyRobotMovementComponent::GetSplineTargetPointLocation(const float CurrentDistance) const
{
if (GetNavPath().GetSplinePath())
{
const int32 SplinePoints = GetNavPath().GetSplinePath()->GetNumberOfSplinePoints();
float NextSplinePointDistance = GetNavPath().GetSplinePath()->GetSplineLength();
int32 NextSplinePointIndex = 0;
for (int32 x = 0; x < SplinePoints; x++)
{
const float PointDistance = GetNavPath().GetSplinePath()->GetDistanceAlongSplineAtSplinePoint(x);
if (PointDistance > CurrentDistance && PointDistance < NextSplinePointDistance)
{
NextSplinePointDistance = PointDistance;
NextSplinePointIndex = x;
}
}
return GetNavPath().GetSplinePath()->GetWorldLocationAtSplinePoint(NextSplinePointIndex);
}
return GetRobotLocation();
}
float ULuckyRobotMovementComponent::GetSplineSpeed(const float CurrentDistance) const
{
if (GetOwningRobot())
{
return GetOwningRobot()->GetCurrentNavPath().SplinePathSpeedModifier.EditorCurveData.Eval(CurrentDistance);
}
return 5.f;
}
float ULuckyRobotMovementComponent::GetSpeedLimit() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->RobotSettings.SpeedLimit;
}
return 5.f;
}
void ULuckyRobotMovementComponent::StopMovement()
{
//
}
float ULuckyRobotMovementComponent::GetTargetSpeed() const
{
return IsPathfinding() ? PathfindingTargetSpeed : GetSpeedLimit();
}
float ULuckyRobotMovementComponent::GetTargetSpeedMod() const
{
return UKismetMathLibrary::SafeDivide(GetTargetSpeed(), CurrentRobotSpeed);
}

View File

@ -171,14 +171,16 @@ void FRobotSplinePath::RegenerateSplinePathValues()
const float PathEndMod = DistanceToPathEnd <= SplinePredictionDistance ? FMath::Max<float>(UKismetMathLibrary::SafeDivide(DistanceToPathEnd, SplinePredictionDistance), 0.f) : 1.f;
const FVector IntervalTangent = GetSplinePath()->GetTangentAtDistanceAlongSpline(SplineDistance, ESplineCoordinateSpace::Local);
const float IntervalCurve = FMath::Min<float>(IntervalTangent.Length(), TangentClamp);
const float IntervalAlpha = UKismetMathLibrary::SafeDivide(IntervalCurve, TangentClamp);
const float IntervalAlpha = FMath::Max<float>(1.f - UKismetMathLibrary::SafeDivide(IntervalCurve, TangentClamp), 0.f);
const float SpeedMod = (MaxSpeedMod - IntervalAlpha) * PathEndMod;
const float ClampedSpeed = FMath::Min<float>(SpeedLimit * SpeedMod, SpeedLimit * MinSpeedMod);
const float ClampedSpeed = FMath::Max<float>(SpeedLimit * SpeedMod, SpeedLimit * MinSpeedMod);
SplinePathSpeedModifier.EditorCurveData.AddKey(SplineDistance, ClampedSpeed);
}
}
}
PathfindingDestination = GetSplinePath()->GetWorldLocationAtDistanceAlongSpline(SplineLength);
// begin debug
const int32 SplineIntervals = UKismetMathLibrary::SafeDivide(SplineLength, SplinePrecision);

View File

@ -91,6 +91,8 @@ void ALuckyRobotPawnBase::Tick(float DeltaTime)
// {
// IncrementCurrentTask();
// }
UE_LOG(LogTemp, Warning, TEXT("ROBOT TARGET SPEED %f"), TargetSpeed);
}
void ALuckyRobotPawnBase::NotifyControllerChanged()
@ -259,6 +261,20 @@ void ALuckyRobotPawnBase::TogglePathfinding(const FInputActionValue& Value)
CurrentInputType = IsPathfinding() ? ERobotInputType::None : ERobotInputType::Pathfinding;
}
void ALuckyRobotPawnBase::BeginPathfinding()
{
PreviousInputType = ERobotInputType::None;
CurrentInputType = ERobotInputType::Pathfinding;
}
void ALuckyRobotPawnBase::PathfindingComplete(const bool bSuccess)
{
CurrentInputType = ERobotInputType::None;
PreviousInputType = ERobotInputType::Pathfinding;
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase::PathfindingComplete"));
}
void ALuckyRobotPawnBase::RemoveMujocoVolumeActor()
{
if (GetMujocoVolumeActor())
@ -294,6 +310,10 @@ void ALuckyRobotPawnBase::SetRobotNavPath(const FRobotNavPath& InRobotNavPath)
void ALuckyRobotPawnBase::Internal_RobotNavPathChanged(const FRobotSplinePath& NewNavPath)
{
// testing
BeginPathfinding();
//
RobotNavPathChanged(NewNavPath);
}

View File

@ -18,6 +18,7 @@ public:
protected:
ERobotInputType PreviousInputType = ERobotInputType::None;
void Move(const float InValue) const;
void Turn(const float InValue) const;
void Move(const float DeltaTime, const FVector2D& InputValues) const;
void Turn(const float DeltaTime, const float InValue) const;
virtual void StopMovement() override;
};

View File

@ -40,6 +40,21 @@ public:
UFUNCTION(BlueprintPure, Category = "Robot Values")
FVector GetRobotRightVector() const;
UFUNCTION(BlueprintPure, Category = "Robot Values")
FRobotSplinePath GetNavPath() const;
bool IsPathfinding() const;
void HandlePathfindingInputs(const float DeltaTime);
FVector GetSplineTargetLocation(const float CurrentDistance) const;
FVector GetSplineTargetPointLocation(const float CurrentDistance) const;
float GetSplineSpeed(const float CurrentDistance) const;
float GetSpeedLimit() const;
virtual void StopMovement();
float GetTargetSpeed() const;
float GetTargetSpeedMod() const;
protected:
virtual void BeginPlay() override;
@ -48,8 +63,16 @@ protected:
FRobotSettings RobotSettings = FRobotSettings();
//virtual void EvaluateRobotPath();
ERobotInputType PathfindingInputType = ERobotInputType::None;
virtual void InterpolateSpeedValues(const float DeltaTime, const float Acceleration, const float Deceleration);
FVector2D CurrentSpeedValues = FVector2D::ZeroVector;
float PathfindingTargetSpeed = 0.f;
float PathfindingDestinationTolerance = 10.f;
FVector PathfindingTargetLocation = FVector::ZeroVector;
FVector PathfindingDestination = FVector::ZeroVector;
float CurrentRobotSpeed = 0.f;
FVector PreviousLocation = FVector::ZeroVector;
};

View File

@ -84,6 +84,9 @@ struct FRobotSplinePath
UPROPERTY(BlueprintReadOnly, Category = "Robot Spline Path")
FRuntimeFloatCurve SplinePathSpeedModifier = FRuntimeFloatCurve();
UPROPERTY(BlueprintReadOnly, Category = "Robot Spline Path")
FVector PathfindingDestination = FVector::ZeroVector;
ALuckyRobotPawnBase* GetRobot() const { return Robot.Get(); }
void GenerateSplinePath(ALuckyRobotPawnBase* InRobot, const FRobotNavPath& InNavPath);
void RegenerateSplinePathValues();

View File

@ -158,6 +158,7 @@ public:
void Look(const FInputActionValue& Value);
void TogglePathfinding(const FInputActionValue& Value);
void BeginPathfinding();
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
ERobotInputType GetCurrentInputType() const { return CurrentInputType; }
@ -186,12 +187,19 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
float TurnModifier = 1.f;
UFUNCTION(BlueprintCallable, Category = "Input")
void SetPathfindingInputValues(const FVector2D InValues = FVector2D::ZeroVector) { CachedMovementInputValues = InValues; }
bool IsPathfinding() const { return CurrentInputType == ERobotInputType::Pathfinding; }
FVector GetPathfindingDestination() const { return CurrentSplinePath.PathfindingDestination; }
void PathfindingComplete(const bool bSuccess = false);
protected:
virtual void BeginPlay() override;
bool HasMovementValue() const;
bool HasTurnValue() const;
bool IsPathfinding() const { return CurrentInputType == ERobotInputType::Pathfinding; }
void RemoveMujocoVolumeActor();