// Copyright Epic Games, Inc. All Rights Reserved. #include "SmartObjectTestingActor.h" #include "SmartObjectSubsystem.h" #include "Engine/World.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(SmartObjectTestingActor) //----------------------------------------------------------------------// // USmartObjectTest //----------------------------------------------------------------------// void USmartObjectTest::PostInitProperties() { UObject::PostInitProperties(); if (!HasAnyFlags(RF_ClassDefaultObject)) { SmartObjectTestingActor = GetTypedOuter(); checkf(SmartObjectTestingActor, TEXT("SmartObjectTest are expected to be used only in ASmartObjectTestingActor")); } } bool USmartObjectTest::RunTest() { if (SmartObjectTestingActor != nullptr && SmartObjectTestingActor->GetSubsystem() != nullptr) { return Run(*SmartObjectTestingActor); } return false; } bool USmartObjectTest::ResetTest() { if (SmartObjectTestingActor != nullptr && SmartObjectTestingActor->GetSubsystem() != nullptr) { return Reset(*SmartObjectTestingActor); } return false; } FBox USmartObjectTest::CalcTestBounds() const { if (SmartObjectTestingActor != nullptr && SmartObjectTestingActor->GetSubsystem() != nullptr) { return CalcBounds(*SmartObjectTestingActor); } return FBox(ForceInit); } #if UE_ENABLE_DEBUG_DRAWING void USmartObjectTest::DebugDraw(FDebugRenderSceneProxy* DebugProxy) const { if (SmartObjectTestingActor != nullptr && SmartObjectTestingActor->GetSubsystem() != nullptr) { DebugDraw(*SmartObjectTestingActor, DebugProxy); } } void USmartObjectTest::DebugDrawCanvas(UCanvas* Canvas, APlayerController* PlayerController) const { if (SmartObjectTestingActor != nullptr && SmartObjectTestingActor->GetSubsystem() != nullptr) { DebugDrawCanvas(*SmartObjectTestingActor, Canvas, PlayerController); } } #endif // UE_ENABLE_DEBUG_DRAWING //----------------------------------------------------------------------// // USmartObjectSimpleQueryTest //----------------------------------------------------------------------// bool USmartObjectSimpleQueryTest::Run(ASmartObjectTestingActor& TestingActor) { FSmartObjectRequest RequestWithTransformedBox = Request; RequestWithTransformedBox.QueryBox = Request.QueryBox.ShiftBy(TestingActor.GetActorLocation()); TArray NewResults; TestingActor.GetSubsystemRef().FindSmartObjects(RequestWithTransformedBox, NewResults, FConstStructView::Make(FSmartObjectActorUserData(&TestingActor))); // Request redraw only when results differ from previous run bool bResultsChanged = false; if (NewResults.Num() != Results.Num()) { bResultsChanged = true; } else { for (int32 i = 0; i < NewResults.Num(); ++i) { if (NewResults[i] != Results[i]) { bResultsChanged = true; break; } } } Results = MoveTemp(NewResults); return bResultsChanged; } bool USmartObjectSimpleQueryTest::Reset(ASmartObjectTestingActor& TestingActor) { const bool bResultsChanged = Results.Num() > 0; Results.Reset(); return bResultsChanged; } FBox USmartObjectSimpleQueryTest::CalcBounds(ASmartObjectTestingActor& TestingActor) const { FBox BoundingBox(EForceInit::ForceInitToZero); BoundingBox += Request.QueryBox.ShiftBy(TestingActor.GetActorLocation()); for (const FSmartObjectRequestResult& Result : Results) { if (Result.IsValid()) { TOptional Location = TestingActor.GetSubsystemRef().GetSlotLocation(Result); BoundingBox += Location.GetValue(); } } return BoundingBox; } #if UE_ENABLE_DEBUG_DRAWING void USmartObjectSimpleQueryTest::DebugDraw(ASmartObjectTestingActor& TestingActor, FDebugRenderSceneProxy* DebugProxy) const { FVector TestLocation = TestingActor.GetActorLocation(); DebugProxy->Boxes.Emplace(Request.QueryBox.ShiftBy(TestLocation), FColor::Yellow); const FVector Extent(10.f); for (const FSmartObjectRequestResult& Result : Results) { FVector Location = TestingActor.GetSubsystemRef().GetSlotLocation(Result).GetValue(); DebugProxy->Boxes.Emplace(FBox(Location - Extent, Location + Extent), FColor::Yellow); DebugProxy->Lines.Emplace(Location, TestLocation, FColor::Orange); } } #endif // UE_ENABLE_DEBUG_DRAWING //----------------------------------------------------------------------// // USmartObjectTestRenderingComponent //----------------------------------------------------------------------// FBoxSphereBounds USmartObjectTestRenderingComponent::CalcBounds(const FTransform& LocalToWorld) const { if (const ASmartObjectTestingActor* TestingActor = Cast(GetOwner())) { return TestingActor->CalcTestsBounds(); } return FBox(ForceInit); } void USmartObjectTestRenderingComponent::PostInitProperties() { Super::PostInitProperties(); if (!HasAnyFlags(RF_ClassDefaultObject)) { ensureMsgf(Cast(GetOwner()), TEXT("SmartObjectTestRenderingComponent is expected to be used only by SmartObjectTestingActor")); } } #if UE_ENABLE_DEBUG_DRAWING void USmartObjectTestRenderingComponent::DebugDraw(FDebugRenderSceneProxy* DebugProxy) { if (ASmartObjectTestingActor* TestingActor = Cast(GetOwner())) { TestingActor->ExecuteOnEachTest([DebugProxy](const USmartObjectTest& Test) { Test.DebugDraw(DebugProxy); }); } } void USmartObjectTestRenderingComponent::DebugDrawCanvas(UCanvas* Canvas, APlayerController* PlayerController) { if (ASmartObjectTestingActor* TestingActor = Cast(GetOwner())) { TestingActor->ExecuteOnEachTest([Canvas, PlayerController](const USmartObjectTest& Test) { Test.DebugDrawCanvas(Canvas, PlayerController); }); } } #endif // UE_ENABLE_DEBUG_DRAWING //----------------------------------------------------------------------// // ASmartObjectTestingActor //----------------------------------------------------------------------// ASmartObjectTestingActor::ASmartObjectTestingActor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { RenderingComponent = CreateDefaultSubobject(TEXT("RenderingComp")); RootComponent = RenderingComponent; PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bStartWithTickEnabled = true; SetCanBeDamaged(false); } void ASmartObjectTestingActor::PostRegisterAllComponents() { Super::PostRegisterAllComponents(); SmartObjectSubsystem = UWorld::GetSubsystem(GetWorld()); } void ASmartObjectTestingActor::ExecuteOnEachTest(const TFunctionRef ExecFunc) { for (USmartObjectTest* Test : Tests) { if (Test != nullptr) { ExecFunc(*Test); } } } void ASmartObjectTestingActor::ExecuteOnEachTest(const TFunctionRef ExecFunc) const { for (const USmartObjectTest* Test : Tests) { if (Test != nullptr) { ExecFunc(*Test); } } } void ASmartObjectTestingActor::RunTests() { bool bRedrawRequired = false; ExecuteOnEachTest([&bRedrawRequired](USmartObjectTest& Test) { bRedrawRequired = Test.RunTest() || bRedrawRequired; }); if (bRedrawRequired) { RenderingComponent->MarkRenderStateDirty(); } } void ASmartObjectTestingActor::ResetTests() { bool bRedrawRequired = false; ExecuteOnEachTest([&bRedrawRequired](USmartObjectTest& Test) { bRedrawRequired = Test.ResetTest() || bRedrawRequired; }); if (bRedrawRequired) { RenderingComponent->MarkRenderStateDirty(); } } bool ASmartObjectTestingActor::ShouldTickIfViewportsOnly() const { // Allow the actor to be ticked in the Editor world without a running simulation return true; } void ASmartObjectTestingActor::Tick(const float DeltaTime) { Super::Tick(DeltaTime); if (bRunTestsEachFrame) { RunTests(); } } FBox ASmartObjectTestingActor::CalcTestsBounds() const { FBox CombinedBounds(ForceInit); ExecuteOnEachTest([&CombinedBounds](const USmartObjectTest& Test) { CombinedBounds += Test.CalcTestBounds(); }); return CombinedBounds; } #if WITH_EDITOR void ASmartObjectTestingActor::PostEditMove(const bool bFinished) { Super::PostEditMove(bFinished); RunTests(); // Force refresh since test results might be the same but reference position has changed RenderingComponent->MarkRenderStateDirty(); } void ASmartObjectTestingActor::PostEditChangeChainProperty(FPropertyChangedChainEvent& PropertyChangedEvent) { Super::PostEditChangeChainProperty(PropertyChangedEvent); // Force refresh for any change since it might affect test debug draw RenderingComponent->MarkRenderStateDirty(); } void ASmartObjectTestingActor::DebugInitializeSubsystemRuntime() { #if WITH_SMARTOBJECT_DEBUG if (SmartObjectSubsystem != nullptr) { SmartObjectSubsystem->DebugInitializeRuntime(); } ResetTests(); #endif // WITH_SMARTOBJECT_DEBUG } void ASmartObjectTestingActor::DebugCleanupSubsystemRuntime() { #if WITH_SMARTOBJECT_DEBUG if (SmartObjectSubsystem != nullptr) { SmartObjectSubsystem->DebugCleanupRuntime(); } ResetTests(); #endif // WITH_SMARTOBJECT_DEBUG } #endif // WITH_EDITOR