Compare commits

...

6 Commits
main ... evan

Author SHA1 Message Date
Vyktori
cbb0bc5fc2 Pathfinding Updates
- Improved Navigation / Path Generation
* Still need to set up the agent radius to fit the arm or whatever dynamic component on a robot
2025-04-24 21:32:16 -04:00
Vyktori
966c3ed39d Navigation Updates 2025-04-23 12:06:59 -04:00
Vyktori
e79570aeae Robot Movement WIP
- Robot Movement semi functional for CombinedInput types
2025-04-22 10:14:20 -04:00
Vyktori
a028ed8caf Robot Nav Path Spline Updates
- Robots now generate the spline based on their input type
- There is now a curve generated with the spline with speed values for any distance along the spline
2025-04-21 11:14:12 -04:00
Vyktori
b0fec0cc7a Robot Movement Updates
- Fixed Input Toggles
- Imported SoArm100
2025-04-18 12:09:26 -04:00
Vyktori
b31dad8ca5 *WIP* Robot Base Class, LuckyWorldSubsystem, LuckyRobotMovementComponent
- Robot Base Class -> Handles inputs, has Mujoco settings, creates proper movement component
- LuckyWorldSubsystem -> Has Get and Ready functions for many Robot values/updates
- LuckyRobotMovementComponent -> Base component for robot movement. Can read any value or input from the owning robot. Can be reused on similar robots or custom components can be created if needed.
2025-04-17 00:00:14 -04:00
75 changed files with 1736 additions and 7 deletions

File diff suppressed because one or more lines are too long

View File

@ -324,3 +324,4 @@ MaxAgentRadius=100.000000
+ActiveGameNameRedirects=(OldGameName="/Script/TP_VehicleAdv",NewGameName="/Script/LuckyWorldV2")
+ActiveGameNameRedirects=(OldGameName="Luckyrobots",NewGameName="/Script/LuckyWorldV2")
+ActiveGameNameRedirects=(OldGameName="/Script/Luckyrobots",NewGameName="/Script/LuckyWorldV2")

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -50,14 +50,14 @@ class LUCKYMUJOCO_API AMujocoVolumeActor : public AActor
UPROPERTY(Transient, VisibleAnywhere, Category = "Mujoco | Debug")
TArray<TSoftObjectPtr<UMujocoTendonComponent>> TendonComponents;
UFUNCTION(BlueprintCallable, Category = "Mujoco")
void InitializeMujoco();
template <typename ComponentType> void AssignComponentsToArray(UWorld* World, TArray<TSoftObjectPtr<ComponentType>>& ComponentArray);
public:
AMujocoVolumeActor();
UFUNCTION(BlueprintCallable, Category = "Mujoco")
void InitializeMujoco();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mujoco | Simulation", meta = (Min = 0, Max = 100, ClampMin = 0, ClampMax = 100))
int32 FrameSkip = 0;

View File

@ -0,0 +1,132 @@
// Fill out your copyright notice in the Description page of Project Settings.
#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)
{
Super::PerformMovement(DeltaTime, InputHandlingMethod, InputType, InputValues);
// we can not do this because the rear actuator is not being used, so Turn would always override Move every frame with a value of 0.f
//Move(InputValues.X);
//Turn(InputValues.Y * TurnMod);
switch (InputType) {
case ERobotInputType::None:
switch (PreviousInputType)
{
case ERobotInputType::None:
StopMovement();
break;
case ERobotInputType::Move:
if (FMath::IsNearlyZero(InputValues.X))
{
PreviousInputType = ERobotInputType::None;
StopMovement();
}
else
{
PreviousInputType = ERobotInputType::Move;
Move(DeltaTime, InputValues);
}
break;
case ERobotInputType::Turn:
if (FMath::IsNearlyZero(InputValues.Y))
{
PreviousInputType = ERobotInputType::None;
StopMovement();
}
else
{
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(DeltaTime, InputValues);
}
break;
case ERobotInputType::Turn:
{
PreviousInputType = ERobotInputType::Turn;
//Turn(DeltaTime, InputValues.Y);
Move(DeltaTime, InputValues);
}
break;
case ERobotInputType::Pathfinding:
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 DeltaTime, const FVector2D& InputValues) const
{
if (GetOwningRobot() && GetMujocoVolumeActor())
{
const FVector2D InterpolatedInputs = FMath::Vector2DInterpTo(PreviousSpeedValues, InputValues, DeltaTime, 3.f);
float Input_0 = InterpolatedInputs.X;
float Input_1 = InterpolatedInputs.X;
const float TargetSpeed = GetTargetSpeed();
const float TurnMod = GetTurnModifier() * IsPathfinding() ? 2.f : 1.f;
Input_0 -= InterpolatedInputs.Y * TargetSpeed * TurnMod;
Input_1 += InterpolatedInputs.Y * TargetSpeed * TurnMod;
Input_0 = FMath::Min<float>(Input_0, TargetSpeed) * 0.5f;
Input_1 = FMath::Min<float>(Input_1, TargetSpeed) * 0.5f;
GetMujocoVolumeActor()->SetActuatorValueByIndex(0, Input_0);
GetMujocoVolumeActor()->SetActuatorValueByIndex(1, Input_1);
}
}
void ULRMC_Stretch::Turn(const float DeltaTime, const float InValue) const
{
if (GetMujocoVolumeActor())
{
const float TurnMod = GetOwningRobot() ? GetOwningRobot()->GetTurnModifier() : 1.f;
GetMujocoVolumeActor()->SetActuatorValueByIndex(0, InValue * -TurnMod);
GetMujocoVolumeActor()->SetActuatorValueByIndex(1, InValue * TurnMod);
}
}
void ULRMC_Stretch::StopMovement()
{
if (GetMujocoVolumeActor())
{
GetMujocoVolumeActor()->SetActuatorValueByIndex(0, 0.f);
GetMujocoVolumeActor()->SetActuatorValueByIndex(1, 0.f);
}
}

View File

@ -0,0 +1,333 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.h"
#include "Actors/MujocoVolumeActor.h"
#include "Components/SplineComponent.h"
#include "Gameplay/Robot/LuckyRobotPawnBase.h"
#include "Kismet/KismetMathLibrary.h"
ULuckyRobotMovementComponent::ULuckyRobotMovementComponent()
{
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = false;
}
void ULuckyRobotMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
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;
PreviousSpeedValues = CurrentSpeedValues;
UE_LOG(LogTemp, Warning, TEXT("CurrentRobotSpeed (%f)"), CurrentRobotSpeed);
}
}
void ULuckyRobotMovementComponent::BeginPlay()
{
Super::BeginPlay();
}
void ULuckyRobotMovementComponent::InitializeMovementComponent(ALuckyRobotPawnBase* InOwningRobot)
{
if (IsValid(InOwningRobot))
{
OwningRobot = InOwningRobot;
if (GetOwningRobot())
{
RobotSettings = GetOwningRobot()->RobotSettings;
MujocoVolumeActor = GetOwningRobot()->GetMujocoVolumeActor();
PreviousLocation = GetRobotLocation();
}
SetComponentTickEnabled(true);
}
}
FTransform ULuckyRobotMovementComponent::GetRobotTransform() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->GetTransform();
}
return FTransform::Identity;
}
FVector ULuckyRobotMovementComponent::GetRobotLocation() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->GetActorLocation();
}
return FVector::ZeroVector;
}
FVector ULuckyRobotMovementComponent::GetRobotForwardVector() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->GetActorForwardVector();
}
return FVector::ZeroVector;
}
FVector ULuckyRobotMovementComponent::GetRobotRightVector() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->GetActorRightVector();
}
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());
}
void ULuckyRobotMovementComponent::InterpolateSpeedValues(const float DeltaTime, const float Acceleration, const float Deceleration)
{
if (GetOwningRobot())
{
FVector2D TargetSpeedValues = GetOwningRobot()->GetMovementInputValues() * GetOwningRobot()->GetTargetSpeed();
const FVector Current = FVector(CurrentSpeedValues.X, CurrentSpeedValues.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)
{
switch (GetOwningRobot()->GetCurrentInputType()) {
case ERobotInputType::None:
break;
case ERobotInputType::Move:
Target.Y = 0.f;
break;
case ERobotInputType::Turn:
Target.X = 0.f;
break;
case ERobotInputType::Pathfinding:
break;
}
}
const bool bMovement = !FMath::IsNearlyZero(Target.X, 0.01f);
const bool bTurning = !FMath::IsNearlyZero(Target.Y, 0.01f);
const float MoveRate = UKismetMathLibrary::FInterpTo(Current.X, Target.X, DeltaTime, bMovement ? Acceleration : Deceleration);
const float TurnRate = UKismetMathLibrary::FInterpTo(Current.Y, Target.Y, DeltaTime, bTurning ? Acceleration : Deceleration);
CurrentSpeedValues = FVector2D(MoveRate, TurnRate);
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);
GetOwningRobot()->SetPathfindingInputValues();
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())
{
constexpr float ForwardOffset = 10.f;
const float ForwardPathCheck = FMath::Clamp<float>(GetTargetSpeed(), 15.f, 50.f) + ForwardOffset;
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);
}
float ULuckyRobotMovementComponent::GetTurnModifier() const
{
if (GetOwningRobot())
{
return GetOwningRobot()->GetTurnModifier();
}
return 1.f;
}

View File

@ -0,0 +1,196 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Gameplay/Robot/Data/RobotSettings.h"
#include "Components/SplineComponent.h"
#include "Gameplay/Robot/LuckyRobotPawnBase.h"
#include "Kismet/KismetMathLibrary.h"
void FRobotSplinePath::GenerateSplinePath(ALuckyRobotPawnBase* InRobot, const FRobotNavPath& InNavPath)
{
if (IsValid(InRobot) && !InNavPath.NavPoints.IsEmpty())
{
Robot = InRobot;
NavPath = InNavPath;
if (GetRobot())
{
USplineComponent* OldSplinePath = GetSplinePath();
if (UActorComponent* NewComp = GetRobot()->AddComponentByClass(USplineComponent::StaticClass(), false, FTransform::Identity, false))
{
if (USplineComponent* NewSplineComp = Cast<USplineComponent>(NewComp))
{
NewSplineComp->ClearSplinePoints();
NewSplineComp->SetAbsolute(true, true, true);
int32 PointIndex = 0;
for (auto const& NavPoint : NavPath.NavPoints)
{
NewSplineComp->AddSplinePointAtIndex(NavPoint.Location, PointIndex, ESplineCoordinateSpace::World, false);
PointIndex++;
}
NewSplineComp->UpdateSpline();
SplinePath = NewSplineComp;
RegenerateSplinePathValues();
// begin debug
if (GetSplinePath())
{
GetSplinePath()->SetVisibility(true);
GetSplinePath()->SetHiddenInGame(false);
}
// end debug
}
}
if (IsValid(OldSplinePath))
{
OldSplinePath->DestroyComponent();
}
}
}
}
void FRobotSplinePath::RegenerateSplinePathValues()
{
if (GetRobot() && GetSplinePath())
{
constexpr float SplinePredictionTime = 1.f;
constexpr float SplinePredictionDistanceMin = 64.f;
const float SpeedLimit = GetRobot()->RobotSettings.SpeedLimit;
const float TurnSpeedLimit = GetRobot()->RobotSettings.TurnSpeedLimit;
const float TurnSpeedMod = UKismetMathLibrary::SafeDivide(TurnSpeedLimit, SpeedLimit);
const float TargetSpeed = FMath::Min<float>(GetRobot()->GetTargetSpeed(), SpeedLimit);
const float SplinePredictionDistance = FMath::Max<float>(SplinePredictionTime * TargetSpeed, SplinePredictionDistanceMin);
constexpr float TangentClamp = 128.f;
const int32 SplinePoints = GetSplinePath()->GetNumberOfSplinePoints();
const bool bSingleInput = GetRobot()->RobotSettings.InputHandlingMethod == ERobotInputHandlingMethod::SingleInput;
if (bSingleInput)
{
for (int32 x = 0; x < SplinePoints; x++)
{
GetSplinePath()->SetTangentAtSplinePoint(x, FVector::ZeroVector, ESplineCoordinateSpace::Local, false);
}
}
else
{
for (int32 x = 0; x < SplinePoints; x++)
{
if (x < SplinePoints)
{
GetSplinePath()->SetSplinePointType(x, ESplinePointType::CurveCustomTangent, false);
const FVector PointLoc1 = GetSplinePath()->GetSplinePointAt(x - 1, ESplineCoordinateSpace::World).Position;
const FVector PointLoc2 = GetSplinePath()->GetSplinePointAt(x, ESplineCoordinateSpace::World).Position;
const FVector PointLoc3 = GetSplinePath()->GetSplinePointAt(x + 1, ESplineCoordinateSpace::World).Position;
const float Distance1 = FVector::Dist(PointLoc1, PointLoc2);
const float Distance2 = FVector::Dist(PointLoc2, PointLoc3);
constexpr float PointDistanceMod = 0.33f;
const float TangentMin = FMath::Min<float>(SplinePredictionDistance * 10.f, TangentClamp);
const FVector ArriveTangent = (PointLoc3 - PointLoc1).GetSafeNormal() * FMath::Min<float>(Distance1 * PointDistanceMod, TangentMin);
const FVector LeaveTangent = (PointLoc3 - PointLoc1).GetSafeNormal() * FMath::Min<float>(Distance2 * PointDistanceMod, TangentMin);
UE_LOG(LogTemp, Warning, TEXT("RegenerateSplinePathValues ArriveTangent (%s)"), *ArriveTangent.ToString());
UE_LOG(LogTemp, Warning, TEXT("RegenerateSplinePathValues LeaveTangent (%s)"), *LeaveTangent.ToString());
GetSplinePath()->SetTangentsAtSplinePoint(x, ArriveTangent, LeaveTangent, ESplineCoordinateSpace::World, false);
}
}
}
GetSplinePath()->UpdateSpline();
const float SplineLength = GetSplinePath()->GetSplineLength();
SplinePathSpeedModifier.EditorCurveData.Reset();
constexpr float MinSpeedMod = 0.4f;
constexpr float SplinePrecision = 10.f;
if (bSingleInput)
{
const float SectionSpeedClamp = FMath::Min<float>(SpeedLimit * MinSpeedMod, TargetSpeed);
const float DecelerationStep = SplinePredictionDistance + SplinePrecision;
SplinePathSpeedModifier.EditorCurveData.AddKey(0.f, SectionSpeedClamp);
SplinePathSpeedModifier.EditorCurveData.AddKey(SplineLength, TargetSpeed * MinSpeedMod);
const float FirstSectionEnd = GetSplinePath()->GetDistanceAlongSplineAtSplinePoint(1);
if (FirstSectionEnd >= DecelerationStep)
{
SplinePathSpeedModifier.EditorCurveData.AddKey(SplinePrecision, TargetSpeed);
}
for (int32 y = 1; y < SplinePoints; y++)
{
const float SectionStart = GetSplinePath()->GetDistanceAlongSplineAtSplinePoint(y - 1);
const float SectionEnd = GetSplinePath()->GetDistanceAlongSplineAtSplinePoint(y);
const float SectionDistance = SectionEnd - SectionStart;
if (SectionDistance >= DecelerationStep)
{
SplinePathSpeedModifier.EditorCurveData.AddKey(SectionEnd - SplinePredictionDistance, SectionSpeedClamp);
SplinePathSpeedModifier.EditorCurveData.AddKey(SectionEnd - DecelerationStep, TargetSpeed);
}
else
{
SplinePathSpeedModifier.EditorCurveData.AddKey(SectionStart, SectionSpeedClamp);
}
SplinePathSpeedModifier.EditorCurveData.AddKey(SectionEnd, SectionSpeedClamp);
if (y < SplinePoints)
{
const float NextSectionEnd = GetSplinePath()->GetDistanceAlongSplineAtSplinePoint(y + 1);
const float NextSectionDistance = NextSectionEnd - SectionEnd;
if (NextSectionDistance >= DecelerationStep)
{
SplinePathSpeedModifier.EditorCurveData.AddKey(SectionEnd + SplinePrecision, TargetSpeed);
}
}
}
}
else
{
const int32 SplineIntervals = UKismetMathLibrary::SafeDivide(SplineLength, SplinePrecision);
for (int32 z = SplineIntervals; z >= 0; z--)
{
if (z == 0)
{
SplinePathSpeedModifier.EditorCurveData.AddKey(0.f, TargetSpeed * MinSpeedMod);
}
else if (z == SplineIntervals)
{
SplinePathSpeedModifier.EditorCurveData.AddKey(SplineLength, TargetSpeed * MinSpeedMod);
}
else
{
constexpr float MaxSpeedMod = 1.f;
const float SplineDistance = z * SplinePrecision;
const float DistanceToPathEnd = SplineLength - SplineDistance;
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>(TangentClamp, IntervalTangent.Length());
const float IntervalAlpha = FMath::Max<float>(1.f - UKismetMathLibrary::SafeDivide(IntervalCurve, TangentClamp), 0.f);
const float SpeedMod = (MaxSpeedMod - IntervalAlpha) * PathEndMod;
const float ClampedSpeed = FMath::Max<float>(SpeedLimit * SpeedMod, SpeedLimit * TurnSpeedMod);
UE_LOG(LogTemp, Warning, TEXT("SpeedMod :: (%f - %f) * %f = %f"), MaxSpeedMod, IntervalAlpha, PathEndMod, SpeedMod);
SplinePathSpeedModifier.EditorCurveData.AddKey(SplineDistance, ClampedSpeed);
}
}
}
PathfindingDestination = GetSplinePath()->GetWorldLocationAtDistanceAlongSpline(SplineLength);
// begin debug
const int32 SplineIntervals = UKismetMathLibrary::SafeDivide(SplineLength, SplinePrecision);
for (int32 z = SplineIntervals; z >= 0; z--)
{
UE_LOG(LogTemp, Warning, TEXT("SpeedCurveValue [%f :: %f]"), z * SplinePrecision, SplinePathSpeedModifier.EditorCurveData.Eval(z * SplinePrecision));
}
// end debug
}
}

View File

@ -0,0 +1,430 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Gameplay/Robot/LuckyRobotPawnBase.h"
#include "Actors/MujocoVolumeActor.h"
#include "Core/LuckyRobotsGameInstance.h"
#include "Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.h"
#include "Subsystems/LuckyWorldSubsystem.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
ALuckyRobotPawnBase::ALuckyRobotPawnBase()
{
PrimaryActorTick.bCanEverTick = true;
}
void ALuckyRobotPawnBase::InitializeMujocoVolumeActor()
{
RemoveMujocoVolumeActor();
if (GetWorld() && IsValid(RobotSettings.MujocoSettings.Get()))
{
if (AMujocoVolumeActor* SpawnedVolume = GetWorld()->SpawnActorDeferred<AMujocoVolumeActor>(RobotSettings.MujocoSettings.Get(), FTransform::Identity, nullptr, nullptr, ESpawnActorCollisionHandlingMethod::AlwaysSpawn))
{
// do anything here we need to for the MujocoVolumeActor before we finalize spawning
SpawnedVolume->FinishSpawning(FTransform::Identity);
MujocoVolumeSettings = SpawnedVolume;
if (GetMujocoVolumeActor())
{
GetMujocoVolumeActor()->InitializeMujoco();
}
}
}
}
void ALuckyRobotPawnBase::InitializeMovementComponent()
{
if (!GetRobotMovementComponent() && IsValid(RobotSettings.MovementComponentClass.Get()))
{
if (UActorComponent* NewComp = AddComponentByClass(RobotSettings.MovementComponentClass.Get(), false, FTransform::Identity, false))
{
if (ULuckyRobotMovementComponent* NewMoveComp = Cast<ULuckyRobotMovementComponent>(NewComp))
{
RobotMovementComponent = NewMoveComp;
if (GetRobotMovementComponent())
{
GetRobotMovementComponent()->InitializeMovementComponent(this);
}
}
}
}
}
void ALuckyRobotPawnBase::BeginPlay()
{
InitializeMujocoVolumeActor();
InitializeMovementComponent();
if (GetLuckyWorldSubsystem())
{
GetLuckyWorldSubsystem()->RegisterLuckyRobot(this);
GetLuckyWorldSubsystem()->TargetSpeedChanged.AddUniqueDynamic(this, &ALuckyRobotPawnBase::SetTargetSpeed);
GetLuckyWorldSubsystem()->RobotNavPathChanged.AddUniqueDynamic(this, &ALuckyRobotPawnBase::SetRobotNavPath);
}
Super::BeginPlay();
}
bool ALuckyRobotPawnBase::HasMovementValue() const
{
return !FMath::IsNearlyZero(CachedMovementInputValues.X, 0.01f);
}
bool ALuckyRobotPawnBase::HasTurnValue() const
{
return !FMath::IsNearlyZero(CachedMovementInputValues.Y, 0.01f);
}
void ALuckyRobotPawnBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
// need to check NaviPoint to see if it is paused
TaskTime += DeltaTime;
// if (HasTaskExpired())
// {
// IncrementCurrentTask();
// }
UE_LOG(LogTemp, Warning, TEXT("ROBOT TARGET SPEED %f"), TargetSpeed);
}
void ALuckyRobotPawnBase::NotifyControllerChanged()
{
Super::NotifyControllerChanged();
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
}
void ALuckyRobotPawnBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
if (!GetEnhancedInputComponent())
{
EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent);
}
if (GetEnhancedInputComponent())
{
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Started, this, &ALuckyRobotPawnBase::MoveStart);
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ALuckyRobotPawnBase::Move);
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Completed, this, &ALuckyRobotPawnBase::MoveStop);
EnhancedInputComponent->BindAction(TurnAction, ETriggerEvent::Started, this, &ALuckyRobotPawnBase::TurnStart);
EnhancedInputComponent->BindAction(TurnAction, ETriggerEvent::Triggered, this, &ALuckyRobotPawnBase::Turn);
EnhancedInputComponent->BindAction(TurnAction, ETriggerEvent::Completed, this, &ALuckyRobotPawnBase::TurnStop);
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ALuckyRobotPawnBase::Look);
EnhancedInputComponent->BindAction(TogglePathfindingAction, ETriggerEvent::Started, this, &ALuckyRobotPawnBase::TogglePathfinding);
}
}
void ALuckyRobotPawnBase::MoveStart(const FInputActionValue& Value)
{
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: MoveStart"));
CachedMovementInputValues.X = Value.Get<float>();
if (CurrentInputType != ERobotInputType::Move)
{
if (CurrentInputType == ERobotInputType::Turn && HasTurnValue())
{
PreviousInputType = ERobotInputType::Turn;
}
else
{
PreviousInputType = ERobotInputType::None;
}
CurrentInputType = ERobotInputType::Move;
}
}
void ALuckyRobotPawnBase::Move(const FInputActionValue& Value)
{
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: Move (%f)"), Value.Get<float>());
CachedMovementInputValues.X = Value.Get<float>();
}
void ALuckyRobotPawnBase::MoveStop(const FInputActionValue& Value)
{
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: MoveStop"));
CachedMovementInputValues.X = Value.Get<float>();
if (CurrentInputType == ERobotInputType::Move)
{
switch (PreviousInputType) {
case ERobotInputType::None:
CurrentInputType = ERobotInputType::None;
PreviousInputType = ERobotInputType::None;
break;
case ERobotInputType::Move:
if (PreviousInputType == ERobotInputType::Turn && HasTurnValue())
{
CurrentInputType = ERobotInputType::Turn;
PreviousInputType = HasMovementValue() ? ERobotInputType::Move : ERobotInputType::None;
}
break;
case ERobotInputType::Turn:
CurrentInputType = ERobotInputType::Turn;
PreviousInputType = HasMovementValue() ? ERobotInputType::Move : ERobotInputType::None;
break;
case ERobotInputType::Pathfinding:
break;
}
}
}
void ALuckyRobotPawnBase::TurnStart(const FInputActionValue& Value)
{
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: TurnStart"));
CachedMovementInputValues.Y = Value.Get<float>();
if (CurrentInputType != ERobotInputType::Turn)
{
if (CurrentInputType == ERobotInputType::Move && HasMovementValue())
{
PreviousInputType = ERobotInputType::Move;
}
else
{
PreviousInputType = ERobotInputType::None;
}
CurrentInputType = ERobotInputType::Turn;
}
}
void ALuckyRobotPawnBase::Turn(const FInputActionValue& Value)
{
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: Turn (%f)"), Value.Get<float>());
CachedMovementInputValues.Y = Value.Get<float>();
}
void ALuckyRobotPawnBase::TurnStop(const FInputActionValue& Value)
{
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: TurnStop"));
CachedMovementInputValues.Y = Value.Get<float>();
if (CurrentInputType == ERobotInputType::Turn)
{
const bool bHasTurnInput = !FMath::IsNearlyZero(CachedMovementInputValues.Y, 0.01f);
switch (PreviousInputType) {
case ERobotInputType::None:
CurrentInputType = ERobotInputType::None;
PreviousInputType = ERobotInputType::None;
break;
case ERobotInputType::Move:
CurrentInputType = ERobotInputType::Move;
PreviousInputType = bHasTurnInput ? ERobotInputType::Turn : ERobotInputType::None;
break;
case ERobotInputType::Turn:
if (PreviousInputType == ERobotInputType::Move && !FMath::IsNearlyZero(CachedMovementInputValues.X, 0.01f))
{
CurrentInputType = ERobotInputType::Move;
PreviousInputType = bHasTurnInput ? ERobotInputType::Turn : ERobotInputType::None;
}
break;
case ERobotInputType::Pathfinding:
break;
}
}
}
void ALuckyRobotPawnBase::Look(const FInputActionValue& Value)
{
UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: Look (%s)"), *Value.Get<FVector2D>().ToString());
}
void ALuckyRobotPawnBase::TogglePathfinding(const FInputActionValue& Value)
{
PreviousInputType = ERobotInputType::None;
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())
{
GetMujocoVolumeActor()->Destroy();
MujocoVolumeSettings.Reset();
}
}
void ALuckyRobotPawnBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
RemoveMujocoVolumeActor();
}
ULuckyWorldSubsystem* ALuckyRobotPawnBase::GetLuckyWorldSubsystem()
{
if (!LuckyWorldSubsystem.Get() && GetWorld())
{
LuckyWorldSubsystem = GetWorld()->GetSubsystem<ULuckyWorldSubsystem>();
}
return LuckyWorldSubsystem.Get();
}
void ALuckyRobotPawnBase::SetRobotNavPath(const FRobotNavPath& InRobotNavPath)
{
CurrentSplinePath.GenerateSplinePath(this, InRobotNavPath);
Internal_RobotNavPathChanged(CurrentSplinePath);
}
void ALuckyRobotPawnBase::Internal_RobotNavPathChanged(const FRobotSplinePath& NewNavPath)
{
// testing
BeginPathfinding();
//
RobotNavPathChanged(NewNavPath);
}
int32 ALuckyRobotPawnBase::GetTaskCount()
{
if (GetLuckyRobotsGameInstance())
{
return GetLuckyRobotsGameInstance()->GetTaskNum();
}
return -1;
}
bool ALuckyRobotPawnBase::HasTaskExpired()
{
if (GetLuckyRobotsGameInstance())
{
return TaskTime >= GetFastEndTaskTime();
}
return false;
}
ULuckyRobotsGameInstance* ALuckyRobotPawnBase::GetLuckyRobotsGameInstance()
{
if (!LuckyGameInstance.Get() && GetWorld() && GetWorld()->GetGameInstance())
{
LuckyGameInstance = Cast<ULuckyRobotsGameInstance>(GetWorld()->GetGameInstance());
}
return LuckyGameInstance.Get();
}
bool ALuckyRobotPawnBase::GetInfiniteTime()
{
if (GetLuckyRobotsGameInstance())
{
return GetLuckyRobotsGameInstance()->bInfiniteTime;
}
return false;
}
float ALuckyRobotPawnBase::GetFastEndTaskTime()
{
if (GetLuckyRobotsGameInstance())
{
return GetLuckyRobotsGameInstance()->FastEndTaskTime;
}
return 0.f;
}
bool ALuckyRobotPawnBase::SetActuators(const TArray<FStretchRobotActuator>& InActuators)
{
Actuators.Empty();
Actuators = InActuators;
return !Actuators.IsEmpty();
}
// SPEED CONFIG
void ALuckyRobotPawnBase::SetTargetSpeed(const float InTargetSpeed)
{
const float OldValue = TargetSpeed;
TargetSpeed = InTargetSpeed;
Internal_TargetSpeedChanged(OldValue);
}
void ALuckyRobotPawnBase::Internal_TargetSpeedChanged(const float OldValue)
{
GetCurrentNavPath().RegenerateSplinePathValues();
TargetSpeedChanged(TargetSpeed, OldValue);
}
// TORQUE LIMITER
void ALuckyRobotPawnBase::SetTorqueLimiter(const float InTorqueLimiter)
{
const float OldValue = TorqueLimiter;
TorqueLimiter = InTorqueLimiter;
Internal_TorqueLimiterChanged(OldValue);
}
void ALuckyRobotPawnBase::Internal_TorqueLimiterChanged(const float OldValue)
{
TorqueLimiterChanged(TorqueLimiter, OldValue);
}
// CURRENT TASK INDEX
void ALuckyRobotPawnBase::SetCurrentTaskIndex(const int32 InCurrentTaskIndex)
{
const int32 OldValue = CurrentTaskIndex;
CurrentTaskIndex = InCurrentTaskIndex;
Internal_CurrentTaskIndexChanged(OldValue);
}
void ALuckyRobotPawnBase::IncrementCurrentTask()
{
TaskTime = 0.f;
SetCurrentTaskIndex(GetCurrentTaskIndex() + 1);
}
void ALuckyRobotPawnBase::Internal_CurrentTaskIndexChanged(const int32 OldValue)
{
if (GetLuckyWorldSubsystem())
{
GetLuckyWorldSubsystem()->UpdateCurrentTaskIndex(CurrentTaskIndex);
}
CurrentTaskIndexChanged(CurrentTaskIndex, OldValue);
}

View File

@ -0,0 +1,104 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Subsystems/LuckyWorldSubsystem.h"
#include "NavigationPath.h"
#include "Gameplay/Robot/LuckyRobotPawnBase.h"
void ULuckyWorldSubsystem::CheckSimulationReady()
{
if (!bSimulationReady)
{
bool bReady = true;
if (!GetLuckyRobot())
{
UE_LOG(LogTemp, Log, TEXT("CheckSimulationReady -> LuckyRobot IS NOT READY"));
bReady = false;
}
// add any other false checks here if they are not ready
if (bReady)
{
bSimulationReady = true;
if (LuckyRobotSimulationReady.IsBound())
{
LuckyRobotSimulationReady.Broadcast();
}
}
}
}
void ULuckyWorldSubsystem::RegisterLuckyRobot(ALuckyRobotPawnBase* InLuckyRobot)
{
if (InLuckyRobot)
{
LuckyRobotPawn = InLuckyRobot;
if (LuckyRobotReady.IsBound())
{
LuckyRobotReady.Broadcast(GetLuckyRobot());
//LuckyRobotReady.Clear();
}
CheckSimulationReady();
}
}
void ULuckyWorldSubsystem::UpdateCurrentTaskIndex(const float InCurrentTaskIndex)
{
Robot_CurrentTaskIndex = InCurrentTaskIndex;
if (CurrentTaskIndexChanged.IsBound())
{
CurrentTaskIndexChanged.Broadcast(Robot_CurrentTaskIndex);
}
}
void ULuckyWorldSubsystem::UpdateTargetSpeed(const float InTargetSpeed)
{
Robot_TargetSpeed = InTargetSpeed;
if (TargetSpeedChanged.IsBound())
{
TargetSpeedChanged.Broadcast(Robot_TargetSpeed);
}
}
void ULuckyWorldSubsystem::UpdateTorqueLimiter(const float InTorqueLimiter)
{
Robot_TorqueLimiter = InTorqueLimiter;
if (TorqueLimiterChanged.IsBound())
{
TorqueLimiterChanged.Broadcast(Robot_TorqueLimiter);
}
}
void ULuckyWorldSubsystem::UpdateRobotNavPath(const FRobotNavPath& InRobotNavPath)
{
RobotNavPath = InRobotNavPath;
if (RobotNavPathChanged.IsBound())
{
RobotNavPathChanged.Broadcast(RobotNavPath);
}
}
FRobotNavPath ULuckyWorldSubsystem::GenerateRobotNavPath_Navigation(UNavigationPath* InNavPath)
{
FRobotNavPath NewNavPath = FRobotNavPath();
if (IsValid(InNavPath))
{
for (auto const& Point : InNavPath->PathPoints)
{
FRobotNavPoint NewPoint = FRobotNavPoint();
NewPoint.Location = Point;
NewNavPath.NavPoints.Add(NewPoint);
}
}
return NewNavPath;
}

View File

@ -0,0 +1,24 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "LuckyRobotMovementComponent.h"
#include "LRMC_Stretch.generated.h"
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class LUCKYWORLDV2_API ULRMC_Stretch : public ULuckyRobotMovementComponent
{
GENERATED_BODY()
public:
virtual void PerformMovement(float DeltaTime, const ERobotInputHandlingMethod InputHandlingMethod, const ERobotInputType InputType, const FVector2D& InputValues) override;
protected:
ERobotInputType PreviousInputType = ERobotInputType::None;
void Move(const float DeltaTime, const FVector2D& InputValues) const;
void Turn(const float DeltaTime, const float InValue) const;
virtual void StopMovement() override;
};

View File

@ -0,0 +1,80 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Gameplay/Robot/Data/RobotSettings.h"
#include "LuckyRobotMovementComponent.generated.h"
class AMujocoVolumeActor;
class ALuckyRobotPawnBase;
UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent), BlueprintType, Blueprintable)
class LUCKYWORLDV2_API ULuckyRobotMovementComponent : public UActorComponent
{
GENERATED_BODY()
public:
ULuckyRobotMovementComponent();
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
virtual void PerformMovement(float DeltaTime, const ERobotInputHandlingMethod InputHandlingMethod, const ERobotInputType InputType, const FVector2D& SpeedValues);
void InitializeMovementComponent(ALuckyRobotPawnBase* InOwningRobot);
ALuckyRobotPawnBase* GetOwningRobot() const { return OwningRobot.Get(); }
AMujocoVolumeActor* GetMujocoVolumeActor() const { return MujocoVolumeActor.Get(); }
UFUNCTION(BlueprintPure, Category = "Robot Values")
FTransform GetRobotTransform() const;
UFUNCTION(BlueprintPure, Category = "Robot Values")
FVector GetRobotLocation() const;
UFUNCTION(BlueprintPure, Category = "Robot Values")
FVector GetRobotForwardVector() const;
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;
float GetTurnModifier() const;
protected:
virtual void BeginPlay() override;
TWeakObjectPtr<ALuckyRobotPawnBase> OwningRobot;
TWeakObjectPtr<AMujocoVolumeActor> MujocoVolumeActor;
FRobotSettings RobotSettings = FRobotSettings();
ERobotInputType PathfindingInputType = ERobotInputType::None;
virtual void InterpolateSpeedValues(const float DeltaTime, const float Acceleration, const float Deceleration);
FVector2D CurrentSpeedValues = FVector2D::ZeroVector;
FVector2D PreviousSpeedValues = 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

@ -0,0 +1,103 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "RobotSettings.generated.h"
class ALuckyRobotPawnBase;
class USplineComponent;
class ULuckyRobotMovementComponent;
class AMujocoVolumeActor;
UENUM(BlueprintType)
enum class ERobotInputType : uint8
{
None UMETA(DisplayName = "None"),
Move UMETA(DisplayName = "Move"),
Turn UMETA(DisplayName = "Turn"),
Pathfinding UMETA(DisplayName = "Pathfinding")
};
UENUM(BlueprintType)
enum class ERobotInputHandlingMethod : uint8
{
None UMETA(DisplayName = "None"),
SingleInput UMETA(DisplayName = "SingleInput"),
CombinedInput UMETA(DisplayName = "CombinedInput")
};
USTRUCT(BlueprintType)
struct FRobotSettings
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Settings")
float SpeedLimit = 15.f;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Settings")
float TurnSpeedLimit = 10.f;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Settings")
TSubclassOf<AMujocoVolumeActor> MujocoSettings;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Settings")
TSubclassOf<ULuckyRobotMovementComponent> MovementComponentClass;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Settings")
ERobotInputHandlingMethod InputHandlingMethod = ERobotInputHandlingMethod::CombinedInput;
};
USTRUCT(BlueprintType)
struct FRobotNavPoint
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Nav Point")
FVector Location = FVector::ZeroVector;
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Nav Point")
bool bDestination = false;
};
USTRUCT(BlueprintType)
struct FRobotNavPath
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Nav Path")
TArray<FRobotNavPoint> NavPoints;
};
USTRUCT(BlueprintType)
struct FRobotSplinePath
{
GENERATED_USTRUCT_BODY()
UPROPERTY(BlueprintReadOnly, Category = "Robot Spline Path")
TWeakObjectPtr<ALuckyRobotPawnBase> Robot;
UPROPERTY(BlueprintReadOnly, Category = "Robot Spline Path")
FRobotNavPath NavPath = FRobotNavPath();
UPROPERTY(BlueprintReadOnly, Category = "Robot Spline Path")
TWeakObjectPtr<USplineComponent> SplinePath;
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();
USplineComponent* GetSplinePath() const { return SplinePath.Get(); }
FRobotSplinePath() {}
FRobotSplinePath(ALuckyRobotPawnBase* InRobot, const FRobotNavPath& InNavPath)
{
GenerateSplinePath(InRobot, InNavPath);
}
};

View File

@ -0,0 +1,225 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Data/RobotSettings.h"
#include "GameFramework/Pawn.h"
#include "LuckyRobotPawnBase.generated.h"
class ULuckyWorldSubsystem;
class AMujocoVolumeActor;
struct FGoalsTaskData;
struct FStretchRobotActuator;
class ULuckyRobotsGameInstance;
class ULuckyRobotMovementComponent;
class UInputMappingContext;
class UInputAction;
struct FInputActionValue;
UCLASS()
class LUCKYWORLDV2_API ALuckyRobotPawnBase : public APawn
{
GENERATED_BODY()
public:
ALuckyRobotPawnBase();
virtual void Tick(float DeltaTime) override;
virtual void NotifyControllerChanged() override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
ULuckyRobotsGameInstance* GetLuckyRobotsGameInstance();
// should this be replaced with bInfiniteTask ? and instead of setting a bool -and- loop count, we just say 0 loops is endless/infinite and the GetInfiniteTime just checks if loop count == 0
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
bool GetInfiniteTime();
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
float GetFastEndTaskTime();
UFUNCTION()
bool SetActuators(const TArray<FStretchRobotActuator>& InActuators);
// SPEED CONFIG
UFUNCTION()
void SetTargetSpeed(const float InTargetSpeed = 0.f);
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
float GetTargetSpeed() const { return TargetSpeed; }
void Internal_TargetSpeedChanged(const float OldValue = 0.f);
UFUNCTION(BlueprintImplementableEvent, Category = "Lucky Robot Pawn")
void TargetSpeedChanged(const float NewValue = 0.f, const float OldValue = 0.f);
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
ULuckyRobotMovementComponent* GetRobotMovementComponent() const { return RobotMovementComponent.Get(); }
// TORQUE LIMITER
UFUNCTION()
void SetTorqueLimiter(const float InTorqueLimiter = 0.f);
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
float GetTorqueLimiter() const { return TorqueLimiter; }
void Internal_TorqueLimiterChanged(const float OldValue = 0.f);
UFUNCTION(BlueprintImplementableEvent, Category = "Lucky Robot Pawn")
void TorqueLimiterChanged(const float NewValue = 0.f, const float OldValue = 0.f);
// CURRENT TASK INDEX
UFUNCTION(BlueprintCallable, Category = "Lucky Robot Pawn")
void SetCurrentTaskIndex(const int32 InCurrentTaskIndex = -1);
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
int32 GetCurrentTaskIndex() const { return CurrentTaskIndex; }
UFUNCTION(BlueprintCallable, Category = "Lucky Robot Pawn")
void IncrementCurrentTask();
void Internal_CurrentTaskIndexChanged(const int32 OldValue = -1);
UFUNCTION(BlueprintImplementableEvent, Category = "Lucky Robot Pawn")
void CurrentTaskIndexChanged(const int32 NewValue = 0, const int32 OldValue = 0);
// TASK LIST
// we should have the game instance broadcast a delegate with the TaskList and we just bind to it here and update when it changes
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
int32 GetTaskCount();
// TASK TIME
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
bool HasTaskExpired();
// MUJOCO
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
AMujocoVolumeActor* GetMujocoVolumeActor() const { return MujocoVolumeSettings.Get(); }
UFUNCTION(BlueprintCallable, Category = "Lucky Robot Pawn")
void InitializeMujocoVolumeActor();
UFUNCTION(BlueprintCallable, Category = "Lucky Robot Pawn")
void InitializeMovementComponent();
// ROBOT DATA
UPROPERTY(BlueprintReadOnly, EditDefaultsOnly, Category = "Lucky Robot Pawn")
FRobotSettings RobotSettings = FRobotSettings();
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
ULuckyWorldSubsystem* GetLuckyWorldSubsystem();
// NAV ROBOT PATH CHANGED
UFUNCTION()
void SetRobotNavPath(const FRobotNavPath& InRobotNavPath);
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
FRobotSplinePath& GetCurrentNavPath() { return CurrentSplinePath; }
void Internal_RobotNavPathChanged(const FRobotSplinePath& NewNavPath);
UFUNCTION(BlueprintImplementableEvent, Category = "Lucky Robot Pawn")
void RobotNavPathChanged(const FRobotSplinePath& NewNavPath);
// INPUTS
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
UEnhancedInputComponent* GetEnhancedInputComponent() const { return EnhancedInputComponent.Get(); }
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputMappingContext* DefaultMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputAction* MoveAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputAction* TurnAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputAction* LookAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
UInputAction* TogglePathfindingAction;
void MoveStart(const FInputActionValue& Value);
void Move(const FInputActionValue& Value);
void MoveStop(const FInputActionValue& Value);
void TurnStart(const FInputActionValue& Value);
void Turn(const FInputActionValue& Value);
void TurnStop(const FInputActionValue& Value);
void Look(const FInputActionValue& Value);
void TogglePathfinding(const FInputActionValue& Value);
void BeginPathfinding();
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
ERobotInputType GetCurrentInputType() const { return CurrentInputType; }
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
FVector2D GetMovementInputValues() const { return CachedMovementInputValues; }
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
float GetAccelerationRate() const { return AccelerationRate; }
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
float GetDecelerationRate() const { return DecelerationRate; }
UFUNCTION(BlueprintPure, Category = "Lucky Robot Pawn")
float GetTurnModifier() const { return TurnModifier; }
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Lucky Robot Pawn")
float AccelerationRate = 1.5f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Lucky Robot Pawn")
float DecelerationRate = 3.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
float TargetSpeed = 0.f;
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;
void RemoveMujocoVolumeActor();
TWeakObjectPtr<ULuckyRobotsGameInstance> LuckyGameInstance;
TWeakObjectPtr<AMujocoVolumeActor> MujocoVolumeSettings;
TWeakObjectPtr<ULuckyRobotMovementComponent> RobotMovementComponent;
TWeakObjectPtr<ULuckyWorldSubsystem> LuckyWorldSubsystem;
TWeakObjectPtr<UEnhancedInputComponent> EnhancedInputComponent;
TArray<FStretchRobotActuator> Actuators;
FVector2D CachedMovementInputValues = FVector2D::ZeroVector;
ERobotInputType PreviousInputType = ERobotInputType::None;
ERobotInputType CurrentInputType = ERobotInputType::None;
float TorqueLimiter = 0.85f;
int32 CurrentTaskIndex = -1;
float TaskTime = 0.f;
UPROPERTY()
FRobotSplinePath CurrentSplinePath = FRobotSplinePath();
};

View File

@ -0,0 +1,97 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Gameplay/Robot/Data/RobotSettings.h"
#include "Subsystems/WorldSubsystem.h"
#include "LuckyWorldSubsystem.generated.h"
class UNavigationPath;
class AMujocoVolumeActor;
class ALuckyRobotPawnBase;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FLuckyRobotSimulationReadyDelegate);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLuckyRobotPawnReadyDelegate, ALuckyRobotPawnBase*, LuckyRobot);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLuckyRobotCurrentTaskChangedDelegate, const int32, NewTaskIndex);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLuckyRobotTargetSpeedChangedDelegate, const float, NewTargetSpeed);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLuckyRobotTorqueLimiterChangedDelegate, const float, NewTorqueLimiter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLuckyRobotNavPathChangedDelegate, const FRobotNavPath&, NewRobotNavPath);
UCLASS()
class LUCKYWORLDV2_API ULuckyWorldSubsystem : public UWorldSubsystem
{
GENERATED_BODY()
public:
// GENERAL
UPROPERTY(BlueprintAssignable, Category = "Lucky World Subsystem")
FLuckyRobotSimulationReadyDelegate LuckyRobotSimulationReady;
void CheckSimulationReady();
UFUNCTION(BlueprintCallable, Category = "Lucky World Subsystem")
bool IsSimulationReady() const { return bSimulationReady; }
// LUCKY ROBOT
void RegisterLuckyRobot(ALuckyRobotPawnBase* InLuckyRobot);
UFUNCTION(BlueprintPure, Category = "Lucky World Subsystem")
ALuckyRobotPawnBase* GetLuckyRobot() const { return LuckyRobotPawn.Get(); }
UPROPERTY(BlueprintAssignable, Category = "Lucky World Subsystem")
FLuckyRobotPawnReadyDelegate LuckyRobotReady;
// CURRENT TASK
void UpdateCurrentTaskIndex(const float InCurrentTaskIndex = -1);
UFUNCTION(BlueprintPure, Category = "Lucky World Subsystem")
float GetCurrentTaskIndex() const { return Robot_CurrentTaskIndex; }
UPROPERTY(BlueprintAssignable, Category = "Lucky World Subsystem")
FLuckyRobotCurrentTaskChangedDelegate CurrentTaskIndexChanged;
// TARGET SPEED
void UpdateTargetSpeed(const float InTargetSpeed = 0.f);
UFUNCTION(BlueprintPure, Category = "Lucky World Subsystem")
float GetTargetSpeed() const { return Robot_TargetSpeed; }
UPROPERTY(BlueprintAssignable, Category = "Lucky World Subsystem")
FLuckyRobotTargetSpeedChangedDelegate TargetSpeedChanged;
// TORQUE LIMITER
void UpdateTorqueLimiter(const float InTorqueLimiter = 0.f);
UFUNCTION(BlueprintPure, Category = "Lucky World Subsystem")
float GetTorqueLimiter() const { return Robot_TorqueLimiter; }
UPROPERTY(BlueprintAssignable, Category = "Lucky World Subsystem")
FLuckyRobotTorqueLimiterChangedDelegate TorqueLimiterChanged;
// ROBOT NAV PATH
UFUNCTION(BlueprintCallable, Category = "Lucky World Subsystem")
void UpdateRobotNavPath(const FRobotNavPath& InRobotNavPath);
UFUNCTION(BlueprintPure, Category = "Lucky World Subsystem")
FRobotNavPath GetRobotNavPath() const { return RobotNavPath; }
UPROPERTY(BlueprintAssignable, Category = "Lucky World Subsystem")
FLuckyRobotNavPathChangedDelegate RobotNavPathChanged;
UFUNCTION(BlueprintCallable, Category = "Lucky World Subsystem")
FRobotNavPath GenerateRobotNavPath_Navigation(UNavigationPath* InNavPath);
protected:
TWeakObjectPtr<ALuckyRobotPawnBase> LuckyRobotPawn;
TWeakObjectPtr<AMujocoVolumeActor> MujocoVolumeActor;
UPROPERTY()
FRobotNavPath RobotNavPath = FRobotNavPath();
float Robot_TargetSpeed = 0.f;
float Robot_TorqueLimiter = 0.85f;
int32 Robot_CurrentTaskIndex = -1;
bool bSimulationReady = false;
};