// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "Engine/Content/Content.h" #include "Engine/Content/AssetReference.h" #include "Engine/Core/Log.h" #include "Engine/Level/Actor.h" #include "Engine/Level/Actors/EmptyActor.h" #include "Engine/Level/Actors/DirectionalLight.h" #include "Engine/Level/Actors/ExponentialHeightFog.h" #include "Engine/Level/Prefabs/Prefab.h" #include "Engine/Level/Prefabs/PrefabManager.h" #include "Engine/Scripting/ScriptingObjectReference.h" #include TEST_CASE("Prefabs") { SECTION("Virtual Prefab") { auto* prefab = Content::CreateVirtualAsset(); REQUIRE(prefab); Content::DeleteAsset(prefab); } SECTION("Test Repareting in Nested Prefab") { // https://github.com/FlaxEngine/FlaxEngine/issues/718 // Create Prefab B with two children attached to the root AssetReference prefabB = Content::CreateVirtualAsset(); REQUIRE(prefabB); Guid id; Guid::Parse("665bb01c49a3370f14a023b5395de261", id); prefabB->ChangeID(id); auto prefabBInit = prefabB->Init(Prefab::TypeName, "[" "{" "\"ID\": \"eec6b9644492fbca1a6ab0a7904a557e\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"Name\": \"Prefab B.Root\"" "}," "{" "\"ID\": \"124540354d2d0a1decbb3ebfc279cfe6\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"ParentID\": \"eec6b9644492fbca1a6ab0a7904a557e\"," "\"Name\": \"Prefab B.Parent\"" "}," "{" "\"ID\": \"f701472747356b7c26186db1c4252b53\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"ParentID\": \"eec6b9644492fbca1a6ab0a7904a557e\"," "\"Name\": \"Prefab B.Child\"" "}" "]"); REQUIRE(!prefabBInit); // Create Prefab A with nested Prefab B attached to the root AssetReference prefabA = Content::CreateVirtualAsset(); REQUIRE(prefabA); Guid::Parse("02524a044184af56b6c664a0f98bd761", id); prefabA->ChangeID(id); auto prefabAInit = prefabA->Init(Prefab::TypeName, "[" "{" "\"ID\": \"5aa124754dcd1cdefed80e828831d45b\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"Name\": \"Prefab A.Root\"" "}," "{" "\"ID\": \"8e51f1094f430733333f8280e78dfcc3\"," "\"PrefabID\": \"665bb01c49a3370f14a023b5395de261\"," "\"PrefabObjectID\": \"eec6b9644492fbca1a6ab0a7904a557e\"," "\"ParentID\": \"5aa124754dcd1cdefed80e828831d45b\"" "}," "{" "\"ID\": \"8e1f2bae4aaedeab8725908ce1aec325\"," "\"PrefabID\": \"665bb01c49a3370f14a023b5395de261\"," "\"PrefabObjectID\": \"124540354d2d0a1decbb3ebfc279cfe6\"," "\"ParentID\": \"8e51f1094f430733333f8280e78dfcc3\"" "}," "{" "\"ID\": \"4e4f3a1847cf96fe2e8919848b7eca79\"," "\"PrefabID\": \"665bb01c49a3370f14a023b5395de261\"," "\"PrefabObjectID\": \"f701472747356b7c26186db1c4252b53\"," "\"ParentID\": \"8e51f1094f430733333f8280e78dfcc3\"" "}" "]"); REQUIRE(!prefabAInit); // Spawn test instances of both prefabs ScriptingObjectReference instanceB = PrefabManager::SpawnPrefab(prefabB); ScriptingObjectReference instanceA = PrefabManager::SpawnPrefab(prefabA); // Verify initial scenario REQUIRE(instanceA); REQUIRE(instanceB); REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root")); REQUIRE(instanceA->GetChildrenCount() == 1); REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root")); REQUIRE(instanceA->Children[0]->GetChildrenCount() == 2); REQUIRE(instanceA->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Parent")); REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 0); REQUIRE(instanceA->Children[0]->Children[1]->GetName() == TEXT("Prefab B.Child")); REQUIRE(instanceA->Children[0]->Children[1]->GetChildrenCount() == 0); // Modify Prefab B instance to reparent the actor instanceB->FindActor(TEXT("Prefab B.Child"))->SetParent(instanceB->FindActor(TEXT("Prefab B.Parent"))); // Apply nested prefab changes auto applyResult = PrefabManager::ApplyAll(instanceB); REQUIRE(!applyResult); // Verify if instance of Prefab B nested in instance of Prefab A was properly updated REQUIRE(instanceA); REQUIRE(instanceB); REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root")); REQUIRE(instanceA->GetChildrenCount() == 1); REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root")); REQUIRE(instanceA->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Parent")); REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[0]->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Child")); REQUIRE(instanceA->Children[0]->Children[0]->Children[0]->GetChildrenCount() == 0); // Cleanup instanceA->DeleteObject(); instanceB->DeleteObject(); Content::DeleteAsset(prefabA); Content::DeleteAsset(prefabB); } SECTION("Test Adding Object in Nested Prefab") { // https://github.com/FlaxEngine/FlaxEngine/issues/690 // Create Prefab B with just root object AssetReference prefabB = Content::CreateVirtualAsset(); REQUIRE(prefabB); Guid id; Guid::Parse("25dbe4b0416be0777a6ce59e8788b10f", id); prefabB->ChangeID(id); auto prefabBInit = prefabB->Init(Prefab::TypeName, "[" "{" "\"ID\": \"aac6b9644492fbca1a6ab0a7904a557e\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"Name\": \"Prefab B.Root\"" "}" "]"); REQUIRE(!prefabBInit); // Create Prefab A with two nested Prefab B attached to the root AssetReference prefabA = Content::CreateVirtualAsset(); REQUIRE(prefabA); Guid::Parse("4cb744714f746e31855f41815612d14b", id); prefabA->ChangeID(id); auto prefabAInit = prefabA->Init(Prefab::TypeName, "[" "{" "\"ID\": \"244274a04cc60d56a2f024bfeef5772d\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"Name\": \"Prefab A.Root\"" "}," "{" "\"ID\": \"1e51f1094f430733333f8280e78dfcc3\"," "\"PrefabID\": \"25dbe4b0416be0777a6ce59e8788b10f\"," "\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\"," "\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\"" "}," "{" "\"ID\": \"2e1f2bae4aaedeab8725908ce1aec325\"," "\"PrefabID\": \"25dbe4b0416be0777a6ce59e8788b10f\"," "\"PrefabObjectID\": \"aac6b9644492fbca1a6ab0a7904a557e\"," "\"ParentID\": \"244274a04cc60d56a2f024bfeef5772d\"" "}" "]"); REQUIRE(!prefabAInit); // Spawn test instances of both prefabs ScriptingObjectReference instanceB = PrefabManager::SpawnPrefab(prefabB); ScriptingObjectReference instanceA = PrefabManager::SpawnPrefab(prefabA); // Verify initial scenario REQUIRE(instanceA); REQUIRE(instanceB); REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root")); REQUIRE(instanceA->GetChildrenCount() == 2); REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root")); REQUIRE(instanceA->Children[0]->GetChildrenCount() == 0); REQUIRE(instanceA->Children[1]->GetName() == TEXT("Prefab B.Root")); REQUIRE(instanceA->Children[1]->GetChildrenCount() == 0); // Modify Prefab B instance to add a new actor as a child so at appears in both nested instances in Prefab A instance Guid newChildId; Guid::Parse(TEXT("123456a04cc60d56a2f024bfeef57723"), newChildId); auto newChild = EmptyActor::Spawn(ScriptingObject::SpawnParams(newChildId, EmptyActor::TypeInitializer)); newChild->SetName(String(TEXT("Prefab B.Child"))); newChild->SetParent(instanceB); // Apply nested prefab changes auto applyResult = PrefabManager::ApplyAll(instanceB); REQUIRE(!applyResult); // Verify if instance of Prefab B nested instances in instance of Prefab A was properly updated REQUIRE(instanceA); REQUIRE(instanceB); REQUIRE(instanceA->GetName() == TEXT("Prefab A.Root")); REQUIRE(instanceA->GetChildrenCount() == 2); REQUIRE(instanceA->Children[0]->GetName() == TEXT("Prefab B.Root")); REQUIRE(instanceA->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[0]->Children[0]->GetName() == TEXT("Prefab B.Child")); REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 0); REQUIRE(instanceA->Children[1]->GetName() == TEXT("Prefab B.Root")); REQUIRE(instanceA->Children[1]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[1]->Children[0]->GetName() == TEXT("Prefab B.Child")); REQUIRE(instanceA->Children[1]->Children[0]->GetChildrenCount() == 0); // Add another child Guid::Parse(TEXT("678906a04cc60d56a2f024bfeef57723"), newChildId); newChild = EmptyActor::Spawn(ScriptingObject::SpawnParams(newChildId, EmptyActor::TypeInitializer)); newChild->SetName(String(TEXT("Prefab B.Child 2"))); newChild->SetParent(instanceB); // Apply nested prefab changes applyResult = PrefabManager::ApplyAll(instanceB); REQUIRE(!applyResult); // Reparent another child into the first child newChild->SetParent(instanceB->Children[0]); // Apply nested prefab changes applyResult = PrefabManager::ApplyAll(instanceB); REQUIRE(!applyResult); // Verify if instance of Prefab B nested instances in instance of Prefab A was properly updated REQUIRE(instanceA); REQUIRE(instanceB); REQUIRE(instanceA->GetChildrenCount() == 2); REQUIRE(instanceA->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[0]->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[0]->Children[0]->Children[0]->GetChildrenCount() == 0); REQUIRE(instanceA->Children[1]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[1]->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceA->Children[1]->Children[0]->Children[0]->GetChildrenCount() == 0); // Cleanup instanceA->DeleteObject(); instanceB->DeleteObject(); Content::DeleteAsset(prefabA); Content::DeleteAsset(prefabB); } SECTION("Test Syncing Changes In Nested Prefab Instance") { // https://github.com/FlaxEngine/FlaxEngine/issues/1015 // Create TestActor prefab with just root object AssetReference testActorPrefab = Content::CreateVirtualAsset(); REQUIRE(testActorPrefab); Guid id; Guid::Parse("7691e981482f2a486e10cfae149e07d3", id); testActorPrefab->ChangeID(id); auto testActorPrefabInit = testActorPrefab->Init(Prefab::TypeName, "[" "{" "\"ID\": \"5d73990240497afc0c6d36814cc6ebbe\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"Name\": \"TestActor\"" "}" "]"); REQUIRE(!testActorPrefabInit); // Create NestedActor prefab that inherits from TestActor prefab AssetReference nestedActorPrefab = Content::CreateVirtualAsset(); REQUIRE(nestedActorPrefab); Guid::Parse("1d521df4465ad849e274748c6d14b703", id); nestedActorPrefab->ChangeID(id); auto nestedActorPrefabInit = nestedActorPrefab->Init(Prefab::TypeName, "[" "{" "\"ID\": \"75c1587b4caeea27241ba7af00dafd45\"," "\"PrefabID\": \"7691e981482f2a486e10cfae149e07d3\"," "\"PrefabObjectID\": \"5d73990240497afc0c6d36814cc6ebbe\"," "\"Name\": \"NestedActor\"" "}" "]"); REQUIRE(!nestedActorPrefabInit); // Spawn test instances of both prefabs ScriptingObjectReference testActor = PrefabManager::SpawnPrefab(testActorPrefab); ScriptingObjectReference nestedActor = PrefabManager::SpawnPrefab(nestedActorPrefab); // Verify initial scenario REQUIRE(testActor); CHECK(testActor->GetName() == TEXT("TestActor")); CHECK(testActor->GetStaticFlags() == StaticFlags::FullyStatic); REQUIRE(nestedActor); CHECK(nestedActor->GetName() == TEXT("NestedActor")); CHECK(nestedActor->GetStaticFlags() == StaticFlags::FullyStatic); // Modify TestActor instance to have Static Flags property changed testActor->SetStaticFlags(StaticFlags::None); // Apply prefab changes auto applyResult = PrefabManager::ApplyAll(testActor); REQUIRE(!applyResult); // Verify if instances were properly updated REQUIRE(testActor); CHECK(testActor->GetName() == TEXT("TestActor")); CHECK(testActor->GetStaticFlags() == StaticFlags::None); REQUIRE(nestedActor); CHECK(nestedActor->GetName() == TEXT("NestedActor")); CHECK(nestedActor->GetStaticFlags() == StaticFlags::None); // Cleanup nestedActor->DeleteObject(); testActor->DeleteObject(); // Spawn another test instances instances of both prefabs testActor = PrefabManager::SpawnPrefab(testActorPrefab); nestedActor = PrefabManager::SpawnPrefab(nestedActorPrefab); // Verify if instances were properly updated REQUIRE(testActor); CHECK(testActor->GetName() == TEXT("TestActor")); CHECK(testActor->GetStaticFlags() == StaticFlags::None); REQUIRE(nestedActor); CHECK(nestedActor->GetName() == TEXT("NestedActor")); CHECK(nestedActor->GetStaticFlags() == StaticFlags::None); // Cleanup nestedActor->DeleteObject(); testActor->DeleteObject(); Content::DeleteAsset(nestedActorPrefab); Content::DeleteAsset(testActorPrefab); } SECTION("Test Loading Nested Prefab After Changing Root") { // https://github.com/FlaxEngine/FlaxEngine/issues/1138 // Create base prefab with 3 objects AssetReference prefabBase = Content::CreateVirtualAsset(); REQUIRE(prefabBase); Guid id; Guid::Parse("2b3334524c696dcfa93cabacd2a4f404", id); prefabBase->ChangeID(id); auto prefabBaseInit = prefabBase->Init(Prefab::TypeName, "[" "{" "\"ID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\"," "\"TypeName\": \"FlaxEngine.DirectionalLight\"," "\"Name\": \"1\"" "}," "{" "\"ID\": \"589bcfaa4bd1a53435129480e5bbdb3b\"," "\"TypeName\": \"FlaxEngine.Camera\"," "\"ParentID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\"," "\"Name\": \"2\"" "}," "{" "\"ID\": \"9e81c24342e61af456411ea34593841d\"," "\"TypeName\": \"FlaxEngine.UICanvas\"," "\"ParentID\": \"589bcfaa4bd1a53435129480e5bbdb3b\"," "\"Name\": \"3\"" "}" "]"); REQUIRE(!prefabBaseInit); // Create nested prefab but with 'old' state where root object is different AssetReference prefabNested = Content::CreateVirtualAsset(); REQUIRE(prefabNested); Guid::Parse("a71447e947cbd2deea018a8377636ce6", id); prefabNested->ChangeID(id); auto prefabNestedInit = prefabNested->Init(Prefab::TypeName, "[" "{" "\"ID\": \"597ab8ea43a5c58b8d06f58f9364d261\"," "\"PrefabID\": \"2b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"589bcfaa4bd1a53435129480e5bbdb3b\"" "}," "{" "\"ID\": \"1a6228d84897ff3b2f444ea263c3657e\"," "\"PrefabID\": \"2b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\"," "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\"" "}," "{" "\"ID\": \"f8fbee1349f749396ab6c2ad34f3afec\"," "\"PrefabID\": \"2b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"9e81c24342e61af456411ea34593841d\"," "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\"" "}" "]"); REQUIRE(!prefabNestedInit); // Spawn test instances of both prefabs ScriptingObjectReference instanceBase = PrefabManager::SpawnPrefab(prefabBase); ScriptingObjectReference instanceNested = PrefabManager::SpawnPrefab(prefabNested); // Verify scenario REQUIRE(instanceBase); REQUIRE(instanceBase->GetName() == TEXT("1")); REQUIRE(instanceBase->GetChildrenCount() == 1); REQUIRE(instanceBase->Children[0]->GetName() == TEXT("2")); REQUIRE(instanceBase->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceBase->Children[0]->Children[0]->GetName() == TEXT("3")); REQUIRE(instanceNested); REQUIRE(instanceNested->GetName() == TEXT("1")); REQUIRE(instanceNested->GetChildrenCount() == 1); REQUIRE(instanceNested->Children[0]->GetName() == TEXT("2")); REQUIRE(instanceNested->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceNested->Children[0]->Children[0]->GetName() == TEXT("3")); // Cleanup instanceNested->DeleteObject(); instanceBase->DeleteObject(); Content::DeleteAsset(prefabNested); Content::DeleteAsset(prefabBase); } SECTION("Test Loading Nested Prefab After Changing and Deleting Root") { // https://github.com/FlaxEngine/FlaxEngine/issues/2050 // Create base prefab with 1 object AssetReference prefabBase = Content::CreateVirtualAsset(); REQUIRE(prefabBase); Guid id; Guid::Parse("3b3334524c696dcfa93cabacd2a4f404", id); prefabBase->ChangeID(id); auto prefabBaseInit = prefabBase->Init(Prefab::TypeName, "[" "{" "\"ID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\"," "\"TypeName\": \"FlaxEngine.DirectionalLight\"," "\"Name\": \"New Root\"" "}," "{" "\"ID\": \"f8fbee1349f749396ab6c2ad34f3afec\"," "\"TypeName\": \"FlaxEngine.Camera\"," "\"Name\": \"Child 1\"," "\"ParentID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\"" "}," "{" "\"ID\": \"5632561847cf96fe2e8919848b7eca79\"," "\"TypeName\": \"FlaxEngine.EmptyActor\"," "\"Name\": \"Child 1.Child\"," "\"ParentID\": \"f8fbee1349f749396ab6c2ad34f3afec\"" "}," "{" "\"ID\": \"4e4f3a1847cf96fe2e8919848b7eca79\"," "\"TypeName\": \"FlaxEngine.UICanvas\"," "\"Name\": \"Child 2\"," "\"ParentID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\"" "}" "]"); REQUIRE(!prefabBaseInit); // Create nested prefab but with 'old' state where root object is different AssetReference prefabNested1 = Content::CreateVirtualAsset(); REQUIRE(prefabNested1); Guid::Parse("671447e947cbd2deea018a8377636ce6", id); prefabNested1->ChangeID(id); auto prefabNestedInit1 = prefabNested1->Init(Prefab::TypeName, "[" "{" "\"ID\": \"597ab8ea43a5c58b8d06f58f9364d261\"," "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"589bcfaa4bd1a53435129480e5bbdb3b\"," "\"Name\": \"Old Root\"" "}," "{" "\"ID\": \"1a6228d84897ff3b2f444ea263c3657e\"," "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"f8fbee1349f749396ab6c2ad34f3afec\"," "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\"" "}," "{" "\"ID\": \"1212124f4d913e58eb35ab8b0b7e2eef\"," "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"82ce814f4d913e58eb35ab8b0b7e2eef\"," "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\"," "\"Name\": \"New Root\"" "}," "{" "\"ID\": \"468028d84897ff3b2f444ea263c3657e\"," "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"2468902349f749396ab6c2ad34f3afec\"," "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\"," "\"Name\": \"Old Child\"" "}" "]"); REQUIRE(!prefabNestedInit1); // Create nested prefab but with 'old' state where root object is different and doesn't exist anymore AssetReference prefabNested2 = Content::CreateVirtualAsset(); REQUIRE(prefabNested2); Guid::Parse("b71447e947cbd2deea018a8377636ce6", id); prefabNested2->ChangeID(id); auto prefabNestedInit2 = prefabNested2->Init(Prefab::TypeName, "[" "{" "\"ID\": \"597ab8ea43a5c58b8d06f58f9364d261\"," "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"589bcfaa4bd1a53435129480e5bbdb3b\"," "\"Name\": \"Old Root\"" "}," "{" "\"ID\": \"1a6228d84897ff3b2f444ea263c3657e\"," "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"f8fbee1349f749396ab6c2ad34f3afec\"," "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\"" "}," "{" "\"ID\": \"468028d84897ff3b2f444ea263c3657e\"," "\"PrefabID\": \"3b3334524c696dcfa93cabacd2a4f404\"," "\"PrefabObjectID\": \"2468902349f749396ab6c2ad34f3afec\"," "\"ParentID\": \"597ab8ea43a5c58b8d06f58f9364d261\"," "\"Name\": \"Old Child\"" "}" "]"); REQUIRE(!prefabNestedInit2); // Spawn test instances of both prefabs ScriptingObjectReference instanceBase = PrefabManager::SpawnPrefab(prefabBase); ScriptingObjectReference instanceNested1 = PrefabManager::SpawnPrefab(prefabNested1); ScriptingObjectReference instanceNested2 = PrefabManager::SpawnPrefab(prefabNested2); // Verify scenario REQUIRE(instanceBase); REQUIRE(instanceBase->GetName() == TEXT("New Root")); REQUIRE(instanceBase->GetChildrenCount() == 2); REQUIRE(instanceBase->Children[0]->GetName() == TEXT("Child 1")); REQUIRE(instanceBase->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceBase->Children[1]->GetName() == TEXT("Child 2")); REQUIRE(instanceBase->Children[1]->GetChildrenCount() == 0); REQUIRE(instanceBase->Children[0]->Children[0]->GetName() == TEXT("Child 1.Child")); REQUIRE(instanceBase->Children[0]->Children[0]->GetChildrenCount() == 0); REQUIRE(instanceNested1); REQUIRE(instanceNested1->GetName() == TEXT("New Root")); REQUIRE(instanceNested1->GetChildrenCount() == 2); REQUIRE(instanceNested1->Children[0]->GetName() == TEXT("Child 1")); REQUIRE(instanceNested1->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceNested1->Children[1]->GetName() == TEXT("Child 2")); REQUIRE(instanceNested1->Children[1]->GetChildrenCount() == 0); REQUIRE(instanceNested1->Children[0]->Children[0]->GetName() == TEXT("Child 1.Child")); REQUIRE(instanceNested1->Children[0]->Children[0]->GetChildrenCount() == 0); REQUIRE(instanceNested2); REQUIRE(instanceNested2->GetName() == TEXT("New Root")); REQUIRE(instanceNested2->GetChildrenCount() == 2); REQUIRE(instanceNested2->Children[0]->GetName() == TEXT("Child 1")); REQUIRE(instanceNested2->Children[0]->GetChildrenCount() == 1); REQUIRE(instanceNested2->Children[1]->GetName() == TEXT("Child 2")); REQUIRE(instanceNested2->Children[1]->GetChildrenCount() == 0); REQUIRE(instanceNested2->Children[0]->Children[0]->GetName() == TEXT("Child 1.Child")); REQUIRE(instanceNested2->Children[0]->Children[0]->GetChildrenCount() == 0); // Cleanup instanceNested2->DeleteObject(); instanceNested1->DeleteObject(); instanceBase->DeleteObject(); Content::DeleteAsset(prefabNested2); Content::DeleteAsset(prefabNested1); Content::DeleteAsset(prefabBase); } SECTION("Test Applying Prefab ChangeTo Object References") { // https://github.com/FlaxEngine/FlaxEngine/issues/3136 // Create Prefab AssetReference prefab = Content::CreateVirtualAsset(); REQUIRE(prefab); Guid id; Guid::Parse("690e55514cd6fdc2a269429a2bf84133", id); prefab->ChangeID(id); auto prefabInit = prefab->Init(Prefab::TypeName, "[" "{" "\"ID\": \"fc3f88cf413c2e668039a0bb7429900d\"," "\"TypeName\": \"FlaxEngine.ExponentialHeightFog\"," "\"Name\": \"Fog\"," "\"DirectionalInscatteringLight\": \"44873cc44e950c754f0c7bb59dd432d6\"" "}," "{" "\"ID\": \"44873cc44e950c754f0c7bb59dd432d6\"," "\"TypeName\": \"FlaxEngine.DirectionalLight\"," "\"ParentID\": \"fc3f88cf413c2e668039a0bb7429900d\"," "\"Name\": \"Sun 1\"" "}," "{" "\"ID\": \"583f91604b622e3b7aa698b51c9966d6\"," "\"TypeName\": \"FlaxEngine.DirectionalLight\"," "\"ParentID\": \"fc3f88cf413c2e668039a0bb7429900d\"," "\"Name\": \"Sun 2\"" "}" "]"); REQUIRE(!prefabInit); // Spawn test instances ScriptingObjectReference instanceA = PrefabManager::SpawnPrefab(prefab); ScriptingObjectReference instanceB = PrefabManager::SpawnPrefab(prefab); // Swap reference from Sun 1 to Sun 2 on a Fog REQUIRE(instanceA); REQUIRE(instanceA->Children.Count() == 2); CHECK(instanceA.As()->DirectionalInscatteringLight == instanceA->Children[0]); instanceA.As()->DirectionalInscatteringLight = (DirectionalLight*)instanceA->Children[1]; // Apply change on instance A and verify it's applied on instance B bool applyResult = PrefabManager::ApplyAll(instanceA); REQUIRE(!applyResult); REQUIRE(instanceB); REQUIRE(instanceB->Children.Count() == 2); CHECK(instanceB.As()->DirectionalInscatteringLight == instanceB->Children[1]); // Cleanup instanceA->DeleteObject(); instanceB->DeleteObject(); Content::DeleteAsset(prefab); } }