void AmbitSpawnerCollisionHelpersSpec::Define()

in Ambit/Source/Ambit/Utils/AmbitSpawnerCollisionHelpers.spec.cpp [37:351]


void AmbitSpawnerCollisionHelpersSpec::Define()
{
    BeforeEach([this]()
    {
        // Create an empty test map;
        World = FAutomationEditorCommonUtils::CreateNewMap();
        TestNotNull("Check if World is properly created", World);

        const FString Path = "StaticMesh'/Engine/BasicShapes/Cube.Cube'";
        FActorSpawnParameters Params;
        ExpectedActorName = "Cube";
        Params.Name = FName(*ExpectedActorName);

        UStaticMesh* StaticMesh = LoadObject<UStaticMesh>(nullptr, *Path, nullptr, LOAD_None, nullptr);
        TestNotNull("Check if static mesh for surface actor is properly loaded", StaticMesh);

        FVector Location(0, 0, 0);
        FRotator Rotation(0, 0, 0);
        FTransform Transform(Rotation, Location);

        TestSurfaceActor = World->SpawnActor<AStaticMeshActor>(AStaticMeshActor::StaticClass(), Transform, Params);

        TestNotNull("Check if surface actor is properly created", TestSurfaceActor);

        TestSurfaceActor->SetActorLabel(ExpectedActorName);
        TestSurfaceActor->SetMobility(EComponentMobility::Movable);
        TestSurfaceActor->GetStaticMeshComponent()->SetStaticMesh(StaticMesh);

        FActorSpawnParameters NewParams;
        FString NewParamName = "Obstacle";
        NewParams.Name = FName(*NewParamName);
        FVector NewLocation(0, 0, 100);
        FRotator NewRotation(0, 0, 0);
        const FString SpawnedActorPath = "/Ambit/Test/Props/BP_Box01.BP_Box01_C";
        const FSoftClassPath ClassPath(SpawnedActorPath);
        const TSubclassOf<AActor> ActorToSpawn = ClassPath.TryLoadClass<UObject>();
        TestSpawnedActor = World->SpawnActor(ActorToSpawn.Get(), &NewLocation, &NewRotation);
        TestNotNull("Check if spawned actor is properly created", TestSpawnedActor);
    });
    Describe("SetGenerateOverlapEventsForActor()", [this]()
    {
        It("does nothing if there is no static mesh component", [this]()
        {
            AActor* NoStaticMeshActor = World->SpawnActor(AActor::StaticClass());
            TArray<bool> Original;
            AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(NoStaticMeshActor, Original);
            TestEqual("nothing added to array", Original.Num(), 0);
            NoStaticMeshActor->Destroy();
        });

        It("produces an error message and returns early if the number of static mesh components"
           " does not equal the number of elements in the original array if bReset is true", [this]()
           {
               AddExpectedError("The amount of StaticMeshComponents found for Actor");
               UStaticMeshComponent* StaticMesh = TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>();
               StaticMesh->SetGenerateOverlapEvents(false);
               TArray<bool> Original;
               Original.Add(true);
               Original.Add(true);
               AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original, true);
               TestFalse("static mesh setting was not changed", StaticMesh->GetGenerateOverlapEvents());
           });

        It("sets generate overlap events to true if only one static mesh component exists", [this]()
        {
            UStaticMeshComponent* StaticMesh = TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>();
            StaticMesh->SetGenerateOverlapEvents(false);
            TArray<bool> Original;
            AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original);
            TestTrue("set to true",
                     TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>()->GetGenerateOverlapEvents());
        });

        It("does not accumulate stored original settings if called twice", [this]()
        {
            UStaticMeshComponent* StaticMesh = TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>();
            StaticMesh->SetGenerateOverlapEvents(false);
            TArray<bool> Original;
            AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original);
            // intentional second call
            AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original);
            TestEqual("size is not accumulated", Original.Num(), 1);
        });

        It("stores original setting if only one static mesh component exists", [this]()
        {
            UStaticMeshComponent* StaticMesh = TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>();
            StaticMesh->SetGenerateOverlapEvents(false);
            TArray<bool> Original;
            AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original);
            TestFalse("stored original setting", Original[0]);
        });

        It("resets generate overlap events if only one static mesh component exists", [this]()
        {
            UStaticMeshComponent* StaticMesh = TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>();
            StaticMesh->SetGenerateOverlapEvents(true);
            TArray<bool> Original;
            Original.Add(false); // "Default"
            AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original, true);

            TestFalse("reset to false",
                      TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>()->GetGenerateOverlapEvents());
        });

        Describe("given multiple static mesh components", [this]()
        {
            BeforeEach([this]()
            {
                const FString Path = "StaticMesh'/Engine/BasicShapes/Plane.Plane'";

                UStaticMesh* StaticMesh = LoadObject<UStaticMesh>(nullptr, *Path, nullptr, LOAD_None, nullptr);
                TestNotNull("Check if static mesh is properly loaded", StaticMesh);
                UStaticMeshComponent* Mesh2 = NewObject<UStaticMeshComponent>(
                    TestSurfaceActor, UStaticMeshComponent::StaticClass());
                Mesh2->SetStaticMesh(StaticMesh);
            });

            It("sets generate overlap events to true if multiple static mesh components exist", [this]()
            {
                TArray<UStaticMeshComponent*> ArrayOfMeshes;
                TestSurfaceActor->GetComponents<UStaticMeshComponent>(ArrayOfMeshes);

                TestEqual("size of array of static meshes", ArrayOfMeshes.Num(), 2);

                ArrayOfMeshes[0]->SetGenerateOverlapEvents(false);
                ArrayOfMeshes[1]->SetGenerateOverlapEvents(false);
                TArray<bool> Original;
                AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original);
                TestTrue("set to true", ArrayOfMeshes[0]->GetGenerateOverlapEvents());
                TestTrue("set to true", ArrayOfMeshes[1]->GetGenerateOverlapEvents());
            });

            It("resets generate overlap events if multiple static mesh components exist", [this]()
            {
                TArray<UStaticMeshComponent*> ArrayOfMeshes;
                TestSurfaceActor->GetComponents<UStaticMeshComponent>(ArrayOfMeshes);

                TestEqual("size of array of static meshes", ArrayOfMeshes.Num(), 2);

                ArrayOfMeshes[0]->SetGenerateOverlapEvents(true);
                ArrayOfMeshes[1]->SetGenerateOverlapEvents(true);
                TArray<bool> Original;
                Original.Add(false);
                Original.Add(false);
                AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original, true);
                TestFalse("reset", ArrayOfMeshes[0]->GetGenerateOverlapEvents());
                TestFalse("reset", ArrayOfMeshes[1]->GetGenerateOverlapEvents());
            });

            It("stores original settings if multiple static mesh components exist", [this]()
            {
                TArray<UStaticMeshComponent*> ArrayOfMeshes;
                TestSurfaceActor->GetComponents<UStaticMeshComponent>(ArrayOfMeshes);

                TestEqual("size of array of static meshes", ArrayOfMeshes.Num(), 2);

                ArrayOfMeshes[0]->SetGenerateOverlapEvents(false);
                ArrayOfMeshes[1]->SetGenerateOverlapEvents(false);
                TArray<bool> Original;
                AmbitSpawnerCollisionHelpers::SetGenerateOverlapEventsForActor(TestSurfaceActor, Original);
                TestTrue("stored original settings", Original.Num() == 2);
                TestFalse("stored original settings", Original[0]);
                TestFalse("stored original settings", Original[1]);
            });
        });
    });

    Describe("FindDefaultStaticMeshComponents()", [this]()
    {
        It("will not add anything to the array if there are no static mesh components.", [this]()
        {
            TArray<UStaticMeshComponent*> Actual;
            AmbitSpawnerCollisionHelpers::FindDefaultStaticMeshComponents(AActor::StaticClass(), Actual);
            TestEqual("Static mesh component array is empty", Actual.Num(), 0);
        });

        It("will get the right StaticMeshComponent when there is one", [this]()
        {
            TArray<UStaticMeshComponent*> Actual;
            AmbitSpawnerCollisionHelpers::FindDefaultStaticMeshComponents(TestSurfaceActor->GetClass(), Actual);
            TestEqual("Static mesh component array has one element", Actual.Num(), 1);

            TestEqual("static mesh component array is correct", TestSurfaceActor->GetStaticMeshComponent()->GetClass(),
                      Actual[0]->GetClass());
        });
    });

    Describe("SetCollisionForAllStaticMeshComponents()", [this]()
    {
        It("sets component to object type for Ambit Spawner Obstacles", [this]()
        {
            UStaticMeshComponent* StaticMesh = TestSurfaceActor->GetStaticMeshComponent();

            TArray<UStaticMeshComponent*> Meshes;
            StaticMesh->SetCollisionObjectType(ECC_WorldDynamic);
            Meshes.Add(StaticMesh);
            AmbitSpawnerCollisionHelpers::SetCollisionForAllStaticMeshComponents(Meshes);

            TestEqual("channel", StaticMesh->GetCollisionObjectType(), ECC_GameTraceChannel1);
        });

        It("sets channels correctly", [this]()
        {
            UStaticMeshComponent* StaticMesh = TestSurfaceActor->GetStaticMeshComponent();

            TArray<UStaticMeshComponent*> Meshes;
            Meshes.Add(StaticMesh);
            AmbitSpawnerCollisionHelpers::SetCollisionForAllStaticMeshComponents(Meshes);
            TestEqual("collision enabled", StaticMesh->GetCollisionEnabled(), ECollisionEnabled::QueryAndPhysics);

            const FCollisionResponseContainer& Container = StaticMesh->GetCollisionResponseToChannels();
            TestEqual("ambit spawned obstacles", Container.GameTraceChannel1, ECR_Block);
            TestEqual("camera", Container.Camera, ECR_Overlap);
            TestEqual("visibility", Container.Visibility, ECR_Overlap);
            TestEqual("world static", Container.WorldStatic, ECR_Overlap);
            TestEqual("world dynamic", Container.WorldDynamic, ECR_Overlap);
            TestEqual("physics body", Container.PhysicsBody, ECR_Overlap);
            TestEqual("destructible", Container.Destructible, ECR_Overlap);
            TestEqual("pawn", Container.Pawn, ECR_Overlap);
            TestEqual("vehicle", Container.Vehicle, ECR_Overlap);
        });
    });

    Describe("IsPenetratingOverlap()", [this]()
    {
        It("returns true if actor is a penetrating overlap", [this]()
        {
            TestSurfaceActor->SetActorLocation(FVector(0));
            TestSurfaceActor->SetActorScale3D(FVector(5));
            TestSpawnedActor->SetActorLocation(FVector(0));

            UStaticMeshComponent* Surface = TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>();

            TestTrue("is penetrating overlap",
                     AmbitSpawnerCollisionHelpers::IsPenetratingOverlap(Surface, TestSpawnedActor));
        });

        It("returns false if actor is on top of surface", [this]()
        {
            TestSpawnedActor->SetActorLocation(FVector(0));
            TestSurfaceActor->SetActorLocation(FVector(0, 0, 100));
            TestSurfaceActor->SetActorScale3D(FVector(2));
            const FHitResult& Hit = AmbitWorldHelpers::LineTraceBelowWorldPoint(FVector(0, 0, 1000));
            TestSpawnedActor->SetActorLocation(Hit.ImpactPoint);
            TestSurfaceActor->SetActorEnableCollision(true);
            TestSpawnedActor->SetActorEnableCollision(true);

            UStaticMeshComponent* Surface = TestSurfaceActor->FindComponentByClass<UStaticMeshComponent>();

            TestFalse("is not penetrating overlap",
                      AmbitSpawnerCollisionHelpers::IsPenetratingOverlap(Surface, TestSpawnedActor));
        });
    });

    Describe("StoreCollisionProfiles()", [this]()
    {
        It("does nothing when there are no components given", [this]()
        {
            const TArray<UStaticMeshComponent*> StaticMeshComponents;
            TMap<FString, TArray<FCollisionResponseTemplate>> OutMap;
            AmbitSpawnerCollisionHelpers::StoreCollisionProfiles("Empty", StaticMeshComponents, OutMap);

            TestEqual("empty key added to map", OutMap.Num(), 1);
            TestEqual("empty array associated with empty key", OutMap.FindChecked("Empty").Num(), 0);
        });

        It("correctly adds to the map when there is only one component", [this]()
        {
            UStaticMeshComponent* Mesh = NewObject<UStaticMeshComponent>();
            Mesh->SetCollisionResponseToAllChannels(ECR_Ignore);
            TArray<UStaticMeshComponent*> StaticMeshComponents;
            StaticMeshComponents.Add(Mesh);
            TMap<FString, TArray<FCollisionResponseTemplate>> OutMap;

            AmbitSpawnerCollisionHelpers::StoreCollisionProfiles("One", StaticMeshComponents, OutMap);

            TestEqual("number of map elements", OutMap.Num(), 1);
            TestEqual("number of static meshes for element", OutMap.FindChecked("One").Num(), 1);
            TestTrue("correct collisions",
                     OutMap.FindChecked("One")[0].ResponseToChannels == Mesh->GetCollisionResponseToChannels());
        });

        It("correctly adds to the map when there are multiple components", [this]()
        {
            UStaticMeshComponent* MeshOne = NewObject<UStaticMeshComponent>();
            MeshOne->SetCollisionResponseToAllChannels(ECR_Ignore);

            UStaticMeshComponent* MeshTwo = NewObject<UStaticMeshComponent>();
            MeshTwo->SetCollisionResponseToAllChannels(ECR_Overlap);

            TArray<UStaticMeshComponent*> StaticMeshComponents;
            StaticMeshComponents.Add(MeshOne);
            StaticMeshComponents.Add(MeshTwo);

            TMap<FString, TArray<FCollisionResponseTemplate>> OutMap;
            AmbitSpawnerCollisionHelpers::StoreCollisionProfiles("One", StaticMeshComponents, OutMap);

            TestEqual("elements in map", OutMap.Num(), 1);
            TestEqual("number of static meshes for element", OutMap.FindChecked("One").Num(), 2);
            TestTrue("correct collisions",
                     OutMap.FindChecked("One")[0].ResponseToChannels == MeshOne->GetCollisionResponseToChannels());
            TestTrue("correct collisions",
                     OutMap.FindChecked("One")[1].ResponseToChannels == MeshTwo->GetCollisionResponseToChannels());
        });
    });

    AfterEach([this]()
    {
        TestSurfaceActor->Destroy();
        TestSurfaceActor = nullptr;
        TestSpawnedActor->Destroy();
        TestSpawnedActor = nullptr;
    });
}