diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp index 93dfca3f13..d189ba6d3e 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabSystemScriptingHandler.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -73,12 +74,24 @@ namespace AzToolsFramework::Prefab AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(result, &AzToolsFramework::ToolsApplicationRequestBus::Events::FindCommonRootInactive, entities, commonRoot, &topLevelEntities); + auto containerEntity = AZStd::make_unique(); - containerEntity->CreateComponent(); + containerEntity->CreateComponent(); containerEntity->CreateComponent(); containerEntity->CreateComponent(); + { + auto transformComponent = containerEntity->CreateComponent(); + // Because procedural prefabs need to be deterministic we need to set the component ID to something unique and non-random + // The prefab that references the proc prefab will store a patch that references the transform component by it's component ID + // If this ID is not stable, the proc prefab will lose its position, parenting, etc data next time it is regenerated + auto hash = TypeHash64(reinterpret_cast(filePath.data()), filePath.length(), AZ::HashValue64{0}); + + transformComponent->SetId(static_cast(hash)); + + } + for (AZ::Entity* entity : topLevelEntities) { AzToolsFramework::Components::TransformComponent* transformComponent = @@ -92,7 +105,7 @@ namespace AzToolsFramework::Prefab auto prefab = m_prefabSystemComponentInterface->CreatePrefab( entities, {}, AZ::IO::PathView(AZStd::string_view(filePath)), AZStd::move(containerEntity)); - + if (!prefab) { AZ_Error("PrefabSystemComponenent", false, "Failed to create prefab %s", filePath.c_str()); diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp index 7e61c50629..999f3e8c26 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabScriptingTests.cpp @@ -48,11 +48,45 @@ namespace UnitTest } }; + TEST_F(PrefabScriptingTest, CreatePrefabTemplate_GeneratesContainerWithStableTransformComponentId) + { + AZ::EntityId entityId; + AzToolsFramework::EntityUtilityBus::BroadcastResult(entityId, &AzToolsFramework::EntityUtilityBus::Events::CreateEditorReadyEntity, "test"); + TemplateId templateId1; + PrefabSystemScriptingBus::BroadcastResult(templateId1, &PrefabSystemScriptingBus::Events::CreatePrefabTemplate, AZStd::vector{ entityId }, "test.prefab"); + + auto prefabSystemComponentInterface = AZ::Interface::Get(); + + auto instance1 = prefabSystemComponentInterface->InstantiatePrefab(templateId1); + + // Clear all templates to reset the system + prefabSystemComponentInterface->RemoveAllTemplates(); + + TemplateId templateId2; + PrefabSystemScriptingBus::BroadcastResult(templateId2, &PrefabSystemScriptingBus::Events::CreatePrefabTemplate, AZStd::vector{ entityId }, "test.prefab"); + + auto instance2 = prefabSystemComponentInterface->InstantiatePrefab(templateId2); + + auto referenceWrapper1 = instance1->GetContainerEntity(); + auto referenceWrapper2 = instance2->GetContainerEntity(); + + ASSERT_TRUE(referenceWrapper1); + ASSERT_TRUE(referenceWrapper2); + + auto transformComponent1 = referenceWrapper1->get().FindComponent(); + auto transformComponent2 = referenceWrapper2->get().FindComponent(); + + ASSERT_NE(transformComponent1, nullptr); + ASSERT_NE(transformComponent2, nullptr); + + ASSERT_EQ(transformComponent1->GetId(), transformComponent2->GetId()); + } + TEST_F(PrefabScriptingTest, PrefabScripting_CreatePrefab) { AZ::ScriptContext sc; auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); - + sc.BindTo(behaviorContext); sc.Execute(R"LUA( my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") @@ -76,7 +110,7 @@ namespace UnitTest { AZ::ScriptContext sc; auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); - + sc.BindTo(behaviorContext); sc.Execute(R"LUA( my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") @@ -99,7 +133,7 @@ namespace UnitTest { AZ::ScriptContext sc; auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); - + sc.BindTo(behaviorContext); AZ_TEST_START_TRACE_SUPPRESSION; sc.Execute(R"LUA( @@ -119,7 +153,7 @@ namespace UnitTest { AZ::ScriptContext sc; auto behaviorContext = AZ::Interface::Get()->GetBehaviorContext(); - + sc.BindTo(behaviorContext); sc.Execute(R"LUA( my_id = EntityUtilityBus.Broadcast.CreateEditorReadyEntity("test") @@ -132,7 +166,7 @@ namespace UnitTest g_globalPrefabString = my_result:GetValue() end )LUA"); - + auto prefabSystemComponentInterface = AZ::Interface::Get(); prefabSystemComponentInterface->RemoveAllTemplates(); @@ -169,5 +203,5 @@ namespace UnitTest g_globalPrefabString.set_capacity(0); // Free all memory } - + }