diff --git a/Content/M_PointOfInterest.uasset b/Content/M_PointOfInterest.uasset new file mode 100644 index 0000000000000000000000000000000000000000..a8139e4ff912b49947eea83e25b273d05b8715fc Binary files /dev/null and b/Content/M_PointOfInterest.uasset differ diff --git a/Content/location_marker.uasset b/Content/location_marker.uasset new file mode 100644 index 0000000000000000000000000000000000000000..9596735fa4f3699f4a0effa1340d9ad94508e6a9 Binary files /dev/null and b/Content/location_marker.uasset differ diff --git a/Source/RWTHVRQuickStart/Private/PointOfInterest.cpp b/Source/RWTHVRQuickStart/Private/PointOfInterest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..30b7635e76c6d4e982f747e5f1e3b8ec7922d690 --- /dev/null +++ b/Source/RWTHVRQuickStart/Private/PointOfInterest.cpp @@ -0,0 +1,29 @@ +#include "PointOfInterest.h" + +#include "Logging/StructuredLog.h" +#include "Components/BillboardComponent.h" + +DEFINE_LOG_CATEGORY(POILog) + +APointOfInterest::APointOfInterest() +{ + UE_LOGFMT(POILog, Verbose, "POI instantiated"); + + SetRootComponent(CreateDefaultSubobject<USceneComponent>("SceneComponent")); + + auto Billboard = CreateDefaultSubobject<UBillboardComponent>("BillboardComponent"); + + static ConstructorHelpers::FObjectFinder<UTexture2D> LocationMarker(TEXT("/Script/Engine.Texture2D'/RWTHVRQuickStart/location_marker.location_marker'")); + if(LocationMarker.Succeeded()) + { + Billboard->SetSprite(LocationMarker.Object); + } + else + { + UE_LOGFMT(POILog, Verbose, "POILog: Could not find location marker Texture. The Asset might have moved."); + } + Billboard->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetIncludingScale); + Billboard->SetHiddenInGame(true); + Billboard->SetWorldScale3D(FVector(0.1f, 0.1f, 0.1f)); +} + diff --git a/Source/RWTHVRQuickStart/Private/PointOfInterestManager.cpp b/Source/RWTHVRQuickStart/Private/PointOfInterestManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6a2ab18c2787aeb964647e75d406849d3533286 --- /dev/null +++ b/Source/RWTHVRQuickStart/Private/PointOfInterestManager.cpp @@ -0,0 +1,194 @@ +#include "PointOfInterestManager.h" + +#include "PointOfInterest.h" +#include "Kismet/GameplayStatics.h" +#include "Logging/StructuredLog.h" + +DEFINE_LOG_CATEGORY(POIManagerLog); + +APointOfInterestManager::APointOfInterestManager() +{ + PrimaryActorTick.bCanEverTick = true; + PrimaryActorTick.bStartWithTickEnabled = true; + PrimaryActorTick.SetTickFunctionEnable(true); + + POIs = TArray<APointOfInterest*>(); + + SplineComponent = CreateDefaultSubobject<USplineComponent>("SplineComponent"); + SplineComponent->SetupAttachment(RootComponent.Get()); +} + +void APointOfInterestManager::TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction) +{ + SplineComponent->Duration = SplineComponent->GetSplineLength() / CameraSpeed; + if (IsRidingCamera) + { + ProgressCameraRide(); + } +} + +void APointOfInterestManager::AddPointOfInterest() +{ + APointOfInterest* POI = static_cast<APointOfInterest*>(GetWorld()->SpawnActor(APointOfInterest::StaticClass())); + + TScriptDelegate Del = TScriptDelegate(); + Del.BindUFunction(this, "UpdateSpline"); + POI->GetRootComponent()->TransformUpdated.AddLambda([this](USceneComponent*, EUpdateTransformFlags, ETeleportType) + { + UpdateSpline(); + }); + + POIs.Add(POI); + + UpdateSpline(); +} + +void APointOfInterestManager::VisitNextPointOfInterest() +{ + if (CurrentPOIIndex >= GetPointOfInterestCount() - 1) + { + CurrentPOIIndex = 0; + } + else + { + ++CurrentPOIIndex; + } + VisitPointOfInterest(POIs[CurrentPOIIndex]); +} + +void APointOfInterestManager::VisitPreviousPointOfInterest() +{ + if (CurrentPOIIndex <= 0) + { + CurrentPOIIndex = GetPointOfInterestCount() - 1; + } + else + { + --CurrentPOIIndex; + } + VisitPointOfInterest(POIs[CurrentPOIIndex]); +} + +void APointOfInterestManager::VisitPointOfInterestByIndex(int index) +{ + VisitPointOfInterest(POIs[index]); +} + + +void APointOfInterestManager::VisitPointOfInterest(APointOfInterest* POI) const +{ + APawn* Pawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0); + if (!Pawn) + { + UE_LOGFMT(POIManagerLog, Warning, "Attempted to move player pawn but no pawn found to move."); + return; + } + FVector Location = POI->GetActorLocation(); + FRotator Rotation = POI->GetActorRotation(); + Rotation.Pitch = 0; + Rotation.Roll = 0; + + Pawn->Controller->SetControlRotation(Rotation); + Pawn->SetActorLocationAndRotation(Location, Rotation); +} + +int APointOfInterestManager::GetPointOfInterestCount() +{ + return POIs.Num(); +} + +void APointOfInterestManager::StartCameraRide() +{ + CameraRideStart = UGameplayStatics::GetTimeSeconds(this); + CameraRideEnd = CameraRideStart + SplineComponent->Duration; + + IsRidingCamera = true; +} + +void APointOfInterestManager::StopCameraRide() +{ + CameraRideEnded(); +} + + +void APointOfInterestManager::UpdateSpline() +{ + for (int i = 0; i < GetPointOfInterestCount(); ++i) + { + if (POIs[i] == nullptr) + { + POIs.RemoveAt(i); + SplineComponent->RemoveSplinePoint(i); + --i; + } + else if (!POIs[i]->GetRootComponent()->TransformUpdated.IsBound()) + { + POIs[i]->GetRootComponent()->TransformUpdated.AddLambda( + [this](USceneComponent*, EUpdateTransformFlags, ETeleportType) + { + UpdateSpline(); + }); + } + } + + int POIsNum = GetPointOfInterestCount(); + int SplinePointsNum = SplineComponent->GetNumberOfSplinePoints(); + + if (POIsNum > SplinePointsNum) + { + for (int i = SplinePointsNum; i < POIsNum; ++i) + { + SplineComponent->AddSplinePointAtIndex(FVector::Zero(), i, ESplineCoordinateSpace::World); + } + } + else if (POIsNum < SplinePointsNum) + { + for (int i = SplinePointsNum - 1; i >= POIsNum; --i) + { + SplineComponent->RemoveSplinePoint(i); + } + } + + for (int i = 0; i < GetPointOfInterestCount(); ++i) + { + APointOfInterest* POI = POIs[i]; + FVector POIPosition = POI->GetActorLocation(); + FRotator POIRotation = POI->GetActorRotation(); + SplineComponent->SetLocationAtSplinePoint(i, POIPosition, ESplineCoordinateSpace::World); + SplineComponent->SetRotationAtSplinePoint(i, POIRotation, ESplineCoordinateSpace::World); + } +} + +void APointOfInterestManager::ProgressCameraRide() +{ + double Current = UGameplayStatics::GetTimeSeconds(this); + float SecondsSinceStart = Current - CameraRideStart; + UE_LOGFMT(POIManagerLog, VeryVerbose, ""); + + APawn* Pawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0); + if (!Pawn) + { + UE_LOGFMT(POIManagerLog, Warning, "Attempted to move player pawn but no pawn found to move."); + return; + } + if (Current < CameraRideEnd) + { + FVector Location = SplineComponent->GetLocationAtTime(SecondsSinceStart, ESplineCoordinateSpace::World); + FRotator Rotation = SplineComponent->GetRotationAtTime(SecondsSinceStart, ESplineCoordinateSpace::World); + Rotation.Pitch = 0; + Rotation.Roll = 0; + + Pawn->Controller->SetControlRotation(Rotation); + Pawn->SetActorLocationAndRotation(Location, Rotation); + } + else + { + CameraRideEnded(); + } +} + +void APointOfInterestManager::CameraRideEnded() +{ + IsRidingCamera = false; + VisitPointOfInterestByIndex(GetPointOfInterestCount() - 1); +} diff --git a/Source/RWTHVRQuickStart/Public/PointOfInterest.h b/Source/RWTHVRQuickStart/Public/PointOfInterest.h new file mode 100644 index 0000000000000000000000000000000000000000..71f2139f90008f671978a255d81bece406ad6513 --- /dev/null +++ b/Source/RWTHVRQuickStart/Public/PointOfInterest.h @@ -0,0 +1,16 @@ +#pragma once + +#include "CoreMinimal.h" + +#include "PointOfInterest.generated.h" + +DECLARE_LOG_CATEGORY_EXTERN(POILog, All, All) + +UCLASS() +class APointOfInterest : public AActor +{ + GENERATED_BODY() + +public: + APointOfInterest(); +}; diff --git a/Source/RWTHVRQuickStart/Public/PointOfInterestManager.h b/Source/RWTHVRQuickStart/Public/PointOfInterestManager.h new file mode 100644 index 0000000000000000000000000000000000000000..982f2374b7455e2b2641b62cbd3ebbf3f9b73934 --- /dev/null +++ b/Source/RWTHVRQuickStart/Public/PointOfInterestManager.h @@ -0,0 +1,61 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Components/SplineComponent.h" + +#include "PointOfInterestManager.generated.h" + +DECLARE_LOG_CATEGORY_EXTERN(POIManagerLog, All, All); + +class APointOfInterest; +class FCameraRide; + +UCLASS(Blueprintable) +class APointOfInterestManager : public AActor +{ + GENERATED_BODY() + +public: + APointOfInterestManager(); + + virtual void TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction) override; + + UFUNCTION(CallInEditor, Category="Point Of Interest Manager") + void AddPointOfInterest(); + + UFUNCTION(CallInEditor, Category="Point Of Interest Manager") + void VisitNextPointOfInterest(); + + UFUNCTION(CallInEditor, Category="Point Of Interest Manager") + void VisitPreviousPointOfInterest(); + + void VisitPointOfInterestByIndex(int index); + void VisitPointOfInterest(APointOfInterest* POI) const; + + UFUNCTION(CallInEditor, Category="Point Of Interest Manager") + void StartCameraRide(); + UFUNCTION(CallInEditor, Category="Point Of Interest Manager") + void StopCameraRide(); + + int GetPointOfInterestCount(); + +protected: + UPROPERTY(EditAnywhere) + TArray<APointOfInterest*> POIs; + + int CurrentPOIIndex = 0; + +private: + UFUNCTION(CallInEditor, Category="Point Of Interest Manager") + void UpdateSpline(); + + void ProgressCameraRide(); + void CameraRideEnded(); + + USplineComponent* SplineComponent; + + UPROPERTY(EditAnywhere, meta=(UIMin = 0.0, UIMax = 500)) + float CameraSpeed = 100; + bool IsRidingCamera; + float CameraRideStart, CameraRideEnd; +}; diff --git a/Source/RWTHVRQuickStart/RWTHVRQuickStart.Build.cs b/Source/RWTHVRQuickStart/RWTHVRQuickStart.Build.cs index 894b0c6ab811c64470fa1dad70e6fda8b7a0553f..21f957f00a2fc6d0887f4f42b50215e6cae15878 100644 --- a/Source/RWTHVRQuickStart/RWTHVRQuickStart.Build.cs +++ b/Source/RWTHVRQuickStart/RWTHVRQuickStart.Build.cs @@ -25,7 +25,7 @@ public class RWTHVRQuickStart : ModuleRules PublicDependencyModuleNames.AddRange( new string[] { - "Core", + "Core", "Engine", "Engine", // ... add other public dependencies that you statically link with here ... } ); @@ -37,7 +37,7 @@ public class RWTHVRQuickStart : ModuleRules "CoreUObject", "Engine", "Slate", - "SlateCore", + "SlateCore", "RWTHVRToolkit", "RWTHVRToolkit", // ... add private dependencies that you statically link with here ... } );