From 9def902e1ceac7fc82b0a9bca91c75647c755d54 Mon Sep 17 00:00:00 2001 From: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> Date: Fri, 10 Dec 2021 17:52:03 -0800 Subject: [PATCH] Fixes up parents for entities that are moved to another Prefab. This fixes issues with entities that are moved to another Prefab and have a parent that was also moved to the same Prefab. Entities that have their parent moved to another Prefab continue to work as is because a placeholder entity is always left behind. Entities that are moved to another Prefab but have a parent that's still in the original Prefab will currently not work correctly. This will be a addressed in a future commit. Signed-off-by: AMZN-koppersr <82230785+AMZN-koppersr@users.noreply.github.com> --- .../Components/TransformComponent.cpp | 25 +-- .../Prefab/Spawnable/SpawnableUtils.cpp | 169 +++++++----------- .../Prefab/Spawnable/SpawnableUtils.h | 18 +- 3 files changed, 78 insertions(+), 134 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp index 809f664a10..2f3fc3cb8b 100644 --- a/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp +++ b/Code/Framework/AzFramework/AzFramework/Components/TransformComponent.cpp @@ -585,21 +585,24 @@ namespace AzFramework EBUS_EVENT_PTR(m_notificationBus, AZ::TransformNotificationBus, OnParentChanged, oldParent, parentId); m_parentChangedEvent.Signal(oldParent, parentId); - if (oldParent != parentId) // Don't send removal notification while activating. + if (GetEntity() != nullptr) { - EBUS_EVENT_ID(oldParent, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId()); - auto oldParentTransform = AZ::TransformBus::FindFirstHandler(oldParent); - if (oldParentTransform) + if (oldParent != parentId) // Don't send removal notification while activating. { - oldParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Removed, GetEntityId()); + EBUS_EVENT_ID(oldParent, AZ::TransformNotificationBus, OnChildRemoved, GetEntityId()); + auto oldParentTransform = AZ::TransformBus::FindFirstHandler(oldParent); + if (oldParentTransform) + { + oldParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Removed, GetEntityId()); + } } - } - EBUS_EVENT_ID(parentId, AZ::TransformNotificationBus, OnChildAdded, GetEntityId()); - auto newParentTransform = AZ::TransformBus::FindFirstHandler(parentId); - if (newParentTransform) - { - newParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Added, GetEntityId()); + EBUS_EVENT_ID(parentId, AZ::TransformNotificationBus, OnChildAdded, GetEntityId()); + auto newParentTransform = AZ::TransformBus::FindFirstHandler(parentId); + if (newParentTransform) + { + newParentTransform->NotifyChildChangedEvent(AZ::ChildChangeType::Added, GetEntityId()); + } } } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index feed44c740..e30417324c 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -52,14 +53,32 @@ namespace AzToolsFramework::Prefab::SpawnableUtils return result; } + const AZ::Entity* FindEntity(AZ::EntityId entityId, const AzToolsFramework::Prefab::Instance& source) + { + const AZ::Entity* result = nullptr; + source.GetConstEntities( + [&result, entityId](const AZ::Entity& entity) + { + if (entity.GetId() != entityId) + { + return true; + } + else + { + result = &entity; + return false; + } + }); + return result; + } + AZ::Entity* FindEntity(AZ::EntityId entityId, AzFramework::Spawnable& source) { uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entityId, source); return index != InvalidEntityIndex ? source.GetEntities()[index].get() : nullptr; } - template - AZStd::unique_ptr CloneEntity(AZ::EntityId entityId, T& source) + AZStd::unique_ptr CloneEntity(AZ::EntityId entityId, AzToolsFramework::Prefab::Instance& source) { AZ::Entity* target = Internal::FindEntity(entityId, source); AZ_Assert( @@ -74,43 +93,30 @@ namespace AzToolsFramework::Prefab::SpawnableUtils return clone; } - AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entityId, AzToolsFramework::Prefab::Instance& source) + AZStd::unique_ptr ReplaceEntityWithPlaceholder( + AZ::EntityId entityId, + [[maybe_unused]] AZStd::string_view sourcePrefabName, + AzToolsFramework::Prefab::Instance& source) { auto&& [instance, alias] = source.FindInstanceAndAlias(entityId); AZ_Assert( - instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%s' for replacing.", - aznumeric_cast(entityId), source.GetTemplateSourcePath().c_str()); + instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%.*s' for replacing.", + aznumeric_cast(entityId), AZ_STRING_ARG(sourcePrefabName)); EntityOptionalReference entityData = instance->GetEntity(alias); AZ_Assert( - entityData.has_value(), "SpawnbleUtils were unable to locate entity '%.*s' in Instance '%s' for replacing.", - AZ_STRING_ARG(alias), source.GetTemplateSourcePath().c_str()); + entityData.has_value(), "SpawnbleUtils were unable to locate entity '%.*s' in Instance '%.*s' for replacing.", + AZ_STRING_ARG(alias), AZ_STRING_ARG(sourcePrefabName)); // A new entity id can be used for the placeholder as `ReplaceEntity` will swap the entity ids. auto placeholder = AZStd::make_unique(AZ::Entity::MakeId(), entityData->get().GetName()); - AZStd::unique_ptr result = instance->ReplaceEntity(AZStd::move(placeholder), alias); - return result; + return instance->ReplaceEntity(AZStd::move(placeholder), alias); } - AZStd::unique_ptr ReplaceEntityWithPlaceholder(AZ::EntityId entityId, AzFramework::Spawnable& source) - { - uint32_t index = AzToolsFramework::Prefab::SpawnableUtils::FindEntityIndex(entityId, source); - AZ_Assert( - index != InvalidEntityIndex, "SpawnbleUtils were unable to locate entity alias with id %zu in Spawnable for replacing.", - aznumeric_cast(entityId)); - - AZStd::unique_ptr original = AZStd::move(source.GetEntities()[index]); - AZ_Assert( - original, "SpawnbleUtils were unable to locate entity with id %zu in Spawnable for replacing.", - aznumeric_cast(entityId)); - - source.GetEntities()[index] = AZStd::make_unique(original->GetId(), original->GetName()); - original->SetId(AZ::Entity::MakeId()); - return original; - } - - template AZStd::pair, AzFramework::Spawnable::EntityAliasType> ApplyAlias( - Source& source, AZ::EntityId entityId, AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType) + AZStd::string_view sourcePrefabName, + AzToolsFramework::Prefab::Instance& source, + AZ::EntityId entityId, + AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType) { namespace PCU = AzToolsFramework::Prefab::PrefabConversionUtils; using ResultPair = AZStd::pair, AzFramework::Spawnable::EntityAliasType>; @@ -123,11 +129,13 @@ namespace AzToolsFramework::Prefab::SpawnableUtils case PCU::EntityAliasType::OptionalReplace: return ResultPair(CloneEntity(entityId, source), AzFramework::Spawnable::EntityAliasType::Replace); case PCU::EntityAliasType::Replace: - return ResultPair(ReplaceEntityWithPlaceholder(entityId, source), AzFramework::Spawnable::EntityAliasType::Replace); + return ResultPair( + ReplaceEntityWithPlaceholder(entityId, sourcePrefabName, source), + AzFramework::Spawnable::EntityAliasType::Replace); case PCU::EntityAliasType::Additional: - ResultPair(AZStd::make_unique(), AzFramework::Spawnable::EntityAliasType::Additional); + return ResultPair(AZStd::make_unique(), AzFramework::Spawnable::EntityAliasType::Additional); case PCU::EntityAliasType::Merge: - ResultPair(AZStd::make_unique(), AzFramework::Spawnable::EntityAliasType::Merge); + return ResultPair(AZStd::make_unique(), AzFramework::Spawnable::EntityAliasType::Merge); default: AZ_Assert( false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast(aliasType)); @@ -183,7 +191,8 @@ namespace AzToolsFramework::Prefab::SpawnableUtils AliasPath alias = source.GetAliasPathRelativeToInstance(entityId); if (!alias.empty()) { - auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType); + auto&& [replacement, storedAliasType] = + Internal::ApplyAlias(sourcePrefabName, source, entityId, aliasType); if (replacement) { AZ::Entity* result = replacement.get(); @@ -213,82 +222,30 @@ namespace AzToolsFramework::Prefab::SpawnableUtils } } - AZ::Entity* CreateEntityAlias( - AZStd::string sourcePrefabName, - AzToolsFramework::Prefab::Instance& source, - AzFramework::Spawnable& target, - AZ::EntityId entityId, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, - uint32_t tag, - AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) + void PatchParents(const AzToolsFramework::Prefab::Instance& source, AzToolsFramework::Prefab::Instance& target) { - using namespace AzToolsFramework::Prefab::PrefabConversionUtils; - - AliasPath alias = source.GetAliasPathRelativeToInstance(entityId); - if (!alias.empty()) - { - auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType); - if (replacement) + target.GetEntities( + [&source, &target](AZStd::unique_ptr& entity) { - AZ::Entity* result = replacement.get(); - target.GetEntities().push_back(AZStd::move(replacement)); - - EntityAliasStore store; - store.m_aliasType = storedAliasType; - store.m_source.emplace(AZStd::move(sourcePrefabName), AZStd::move(alias)); - store.m_target.emplace(target, result->GetId()); - store.m_tag = tag; - store.m_loadBehavior = loadBehavior; - context.RegisterSpawnableEntityAlias(AZStd::move(store)); - - return result; - } - else - { - AZ_Assert(false, "A replacement for entity with id %zu could not be created.", static_cast(entityId)); - return nullptr; - } - } - else - { - AZ_Assert(false, "Entity with id %llu was not found in the source prefab.", static_cast(entityId)); - return nullptr; - } - } - - AZ::Entity* CreateEntityAlias( - AzFramework::Spawnable& source, - AzFramework::Spawnable& target, - AZ::EntityId entityId, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, - uint32_t tag, - AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context) - { - using namespace AzToolsFramework::Prefab::PrefabConversionUtils; - - auto&& [replacement, storedAliasType] = Internal::ApplyAlias(source, entityId, aliasType); - if (replacement) - { - AZ::Entity* result = replacement.get(); - target.GetEntities().push_back(AZStd::move(replacement)); - - EntityAliasStore store; - store.m_aliasType = storedAliasType; - store.m_source.emplace(source, entityId); - store.m_target.emplace(target, result->GetId()); - store.m_tag = tag; - store.m_loadBehavior = loadBehavior; - context.RegisterSpawnableEntityAlias(AZStd::move(store)); - - return result; - } - else - { - AZ_Assert(false, "A replacement for entity with id %zu could not be created.", static_cast(entityId)); - return nullptr; - } + AzFramework::TransformComponent* transform = entity->FindComponent(); + if (transform) + { + if (transform->GetParentId().IsValid()) + { + AliasPath originalParentAlias = source.GetAliasPathRelativeToInstance(transform->GetParentId()); + if (!originalParentAlias.empty()) + { + AZ::EntityId targetParentId = target.GetEntityIdFromAliasPath(originalParentAlias); + if (targetParentId.IsValid()) + { + // If this is valid then the parent was moved to the target spawnable so adjust the entity id. + transform->SetParent(targetParentId); + } + } + } + } + return true; + }); } uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable) diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h index ea8a49857e..ea33ca09cc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.h @@ -41,23 +41,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, uint32_t tag, AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); - AZ::Entity* CreateEntityAlias( - AZStd::string sourcePrefabName, - AzToolsFramework::Prefab::Instance& source, - AzFramework::Spawnable& target, - AZ::EntityId entityId, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, - uint32_t tag, - AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); - AZ::Entity* CreateEntityAlias( - AzFramework::Spawnable& source, - AzFramework::Spawnable& target, - AZ::EntityId entityId, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasType aliasType, - AzToolsFramework::Prefab::PrefabConversionUtils::EntityAliasSpawnableLoadBehavior loadBehavior, - uint32_t tag, - AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext& context); + void PatchParents(const AzToolsFramework::Prefab::Instance& source, AzToolsFramework::Prefab::Instance& target); uint32_t FindEntityIndex(AZ::EntityId entity, const AzFramework::Spawnable& spawnable);