diff --git a/Plugins/LuckyMujoco/Source/LuckyMujoco/Public/Actors/MujocoVolumeActor.h b/Plugins/LuckyMujoco/Source/LuckyMujoco/Public/Actors/MujocoVolumeActor.h index 12b041e8..1d81d373 100644 --- a/Plugins/LuckyMujoco/Source/LuckyMujoco/Public/Actors/MujocoVolumeActor.h +++ b/Plugins/LuckyMujoco/Source/LuckyMujoco/Public/Actors/MujocoVolumeActor.h @@ -49,14 +49,14 @@ class LUCKYMUJOCO_API AMujocoVolumeActor : public AActor UPROPERTY(Transient, VisibleAnywhere, Category = "Mujoco | Debug") TArray> TendonComponents; - - UFUNCTION(BlueprintCallable, Category = "Mujoco") - void InitializeMujoco(); - + template void AssignComponentsToArray(UWorld* World, TArray>& ComponentArray); public: - AMujocoVolumeActor(); + 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; diff --git a/Source/LuckyWorldV2/Private/Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.cpp b/Source/LuckyWorldV2/Private/Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.cpp new file mode 100644 index 00000000..ad5f6f1f --- /dev/null +++ b/Source/LuckyWorldV2/Private/Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.cpp @@ -0,0 +1,30 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.h" +#include "Actors/MujocoVolumeActor.h" +#include "Gameplay/Robot/LuckyRobotPawnBase.h" + + +ULuckyRobotMovementComponent::ULuckyRobotMovementComponent() +{ + PrimaryComponentTick.bCanEverTick = true; +} + +void ULuckyRobotMovementComponent::BeginPlay() +{ + Super::BeginPlay(); +} + +void ULuckyRobotMovementComponent::InitializeMovementComponent(ALuckyRobotPawnBase* InOwningRobot) +{ + if (IsValid(InOwningRobot)) + { + OwningRobot = InOwningRobot; + + if (GetOwningRobot()) + { + MujocoVolumeActor = GetOwningRobot()->GetMujocoVolumeActor(); + } + } +} diff --git a/Source/LuckyWorldV2/Private/Gameplay/Robot/LuckyRobotPawnBase.cpp b/Source/LuckyWorldV2/Private/Gameplay/Robot/LuckyRobotPawnBase.cpp new file mode 100644 index 00000000..8b2bbbf3 --- /dev/null +++ b/Source/LuckyWorldV2/Private/Gameplay/Robot/LuckyRobotPawnBase.cpp @@ -0,0 +1,328 @@ +// 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(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(NewComp)) + { + RobotMovementComponent = NewMoveComp; + + if (GetRobotMovementComponent()) + { + GetRobotMovementComponent()->InitializeMovementComponent(this); + } + } + } + } +} + +void ALuckyRobotPawnBase::BeginPlay() +{ + Super::BeginPlay(); + + InitializeMujocoVolumeActor(); + InitializeMovementComponent(); + + if (GetLuckyWorldSubsystem()) + { + GetLuckyWorldSubsystem()->RegisterLuckyRobot(this); + + GetLuckyWorldSubsystem()->TargetSpeedChanged.AddUniqueDynamic(this, &ALuckyRobotPawnBase::SetTargetSpeed); + } +} + +void ALuckyRobotPawnBase::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + + // need to check NaviPoint to see if it is paused + TaskTime += DeltaTime; + + // if (HasTaskExpired()) + // { + // IncrementCurrentTask(); + // } +} + +void ALuckyRobotPawnBase::NotifyControllerChanged() +{ + Super::NotifyControllerChanged(); + + if (APlayerController* PlayerController = Cast(Controller)) + { + if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PlayerController->GetLocalPlayer())) + { + Subsystem->AddMappingContext(DefaultMappingContext, 0); + } + } +} + +void ALuckyRobotPawnBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) +{ + if (!GetEnhancedInputComponent()) + { + EnhancedInputComponent = Cast(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); + } +} + +void ALuckyRobotPawnBase::MoveStart(const FInputActionValue& Value) +{ + UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: MoveStart")); + + CachedMovementInputValues.X = Value.Get(); + + PreviousInputType = CurrentInputType; + CurrentInputType = ERobotInputType::Move; +} + +void ALuckyRobotPawnBase::Move(const FInputActionValue& Value) +{ + UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: Move (%f)"), Value.Get()); + + CachedMovementInputValues.X = Value.Get(); +} + +void ALuckyRobotPawnBase::MoveStop(const FInputActionValue& Value) +{ + UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: MoveStop")); + + CachedMovementInputValues.X = 0.f; + + if (CurrentInputType == ERobotInputType::Move && PreviousInputType == ERobotInputType::Turn && !FMath::IsNearlyZero(CachedMovementInputValues.Y, 0.01f)) + { + CurrentInputType = ERobotInputType::Turn; + } + else + { + CurrentInputType = ERobotInputType::None; + } + + PreviousInputType = ERobotInputType::None; +} + +void ALuckyRobotPawnBase::TurnStart(const FInputActionValue& Value) +{ + UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: TurnStart")); + + CachedMovementInputValues.Y = Value.Get(); + + PreviousInputType = CurrentInputType; + CurrentInputType = ERobotInputType::Turn; +} + +void ALuckyRobotPawnBase::Turn(const FInputActionValue& Value) +{ + UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: Turn (%f)"), Value.Get()); + + CachedMovementInputValues.Y = Value.Get(); +} + +void ALuckyRobotPawnBase::TurnStop(const FInputActionValue& Value) +{ + UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: TurnStop")); + + CachedMovementInputValues.Y = 0.f; + + if (CurrentInputType == ERobotInputType::Turn && PreviousInputType == ERobotInputType::Move && !FMath::IsNearlyZero(CachedMovementInputValues.X, 0.01f)) + { + CurrentInputType = ERobotInputType::Move; + } + else + { + CurrentInputType = ERobotInputType::None; + } + + PreviousInputType = ERobotInputType::None; +} + +void ALuckyRobotPawnBase::Look(const FInputActionValue& Value) +{ + UE_LOG(LogTemp, Warning, TEXT("ALuckyRobotPawnBase :: Input :: Look (%s)"), *Value.Get().ToString()); +} + +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(); + } + + return LuckyWorldSubsystem.Get(); +} + +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(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& 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) +{ + 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); +} diff --git a/Source/LuckyWorldV2/Private/Subsystems/LuckyWorldSubsystem.cpp b/Source/LuckyWorldV2/Private/Subsystems/LuckyWorldSubsystem.cpp new file mode 100644 index 00000000..aefff6cd --- /dev/null +++ b/Source/LuckyWorldV2/Private/Subsystems/LuckyWorldSubsystem.cpp @@ -0,0 +1,75 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "Subsystems/LuckyWorldSubsystem.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); + } +} \ No newline at end of file diff --git a/Source/LuckyWorldV2/Public/Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.h b/Source/LuckyWorldV2/Public/Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.h new file mode 100644 index 00000000..49807a17 --- /dev/null +++ b/Source/LuckyWorldV2/Public/Gameplay/Robot/Components/Movement/LuckyRobotMovementComponent.h @@ -0,0 +1,31 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.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(); + + void InitializeMovementComponent(ALuckyRobotPawnBase* InOwningRobot); + + ALuckyRobotPawnBase* GetOwningRobot() const { return OwningRobot.Get(); } + + AMujocoVolumeActor* GetMujocoVolumeActor() const { return MujocoVolumeActor.Get(); } + +protected: + virtual void BeginPlay() override; + + TWeakObjectPtr OwningRobot; + TWeakObjectPtr MujocoVolumeActor; +}; diff --git a/Source/LuckyWorldV2/Public/Gameplay/Robot/Data/RobotSettings.h b/Source/LuckyWorldV2/Public/Gameplay/Robot/Data/RobotSettings.h new file mode 100644 index 00000000..a4bf6192 --- /dev/null +++ b/Source/LuckyWorldV2/Public/Gameplay/Robot/Data/RobotSettings.h @@ -0,0 +1,42 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "RobotSettings.generated.h" + + +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") + TSubclassOf MujocoSettings; + + UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Settings") + TSubclassOf MovementComponentClass; + + UPROPERTY(BlueprintReadWrite, EditDefaultsOnly, Category = "Robot Settings") + ERobotInputHandlingMethod InputHandlingMethod = ERobotInputHandlingMethod::CombinedInput; +}; \ No newline at end of file diff --git a/Source/LuckyWorldV2/Public/Gameplay/Robot/LuckyRobotPawnBase.h b/Source/LuckyWorldV2/Public/Gameplay/Robot/LuckyRobotPawnBase.h new file mode 100644 index 00000000..bff0eb64 --- /dev/null +++ b/Source/LuckyWorldV2/Public/Gameplay/Robot/LuckyRobotPawnBase.h @@ -0,0 +1,168 @@ +// 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(BlueprintCallable, Category = "Lucky Robot Pawn") + bool SetActuators(const TArray& InActuators); + + // SPEED CONFIG + UFUNCTION(BlueprintCallable, Category = "Lucky Robot Pawn") + 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(BlueprintCallable, Category = "Lucky Robot Pawn") + 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(); + + // 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; + + 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); + +protected: + virtual void BeginPlay() override; + + void RemoveMujocoVolumeActor(); + + TWeakObjectPtr LuckyGameInstance; + TWeakObjectPtr MujocoVolumeSettings; + TWeakObjectPtr RobotMovementComponent; + TWeakObjectPtr LuckyWorldSubsystem; + TWeakObjectPtr EnhancedInputComponent; + + TArray Actuators; + + float AccelerationRate = 1.f; + FVector2D CachedMovementInputValues = FVector2D::ZeroVector; + + ERobotInputType PreviousInputType = ERobotInputType::None; + ERobotInputType CurrentInputType = ERobotInputType::None; + + float TargetSpeed = 0.f; + float TorqueLimiter = 0.85f; + int32 CurrentTaskIndex = -1; + float TaskTime = 0.f; +}; \ No newline at end of file diff --git a/Source/LuckyWorldV2/Public/Subsystems/LuckyWorldSubsystem.h b/Source/LuckyWorldV2/Public/Subsystems/LuckyWorldSubsystem.h new file mode 100644 index 00000000..939c4369 --- /dev/null +++ b/Source/LuckyWorldV2/Public/Subsystems/LuckyWorldSubsystem.h @@ -0,0 +1,78 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/WorldSubsystem.h" +#include "LuckyWorldSubsystem.generated.h" + + +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); + +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; + +protected: + TWeakObjectPtr LuckyRobotPawn; + TWeakObjectPtr MujocoVolumeActor; + + float Robot_TargetSpeed = 0.f; + float Robot_TorqueLimiter = 0.85f; + int32 Robot_CurrentTaskIndex = -1; + + bool bSimulationReady = false; +};