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>
This commit is contained in:
AMZN-koppersr
2021-12-10 17:52:03 -08:00
parent 76a913882c
commit 9def902e1c
3 changed files with 78 additions and 134 deletions
@@ -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());
}
}
}
@@ -16,6 +16,7 @@
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
@@ -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<typename T>
AZStd::unique_ptr<AZ::Entity> CloneEntity(AZ::EntityId entityId, T& source)
AZStd::unique_ptr<AZ::Entity> 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<AZ::Entity> ReplaceEntityWithPlaceholder(AZ::EntityId entityId, AzToolsFramework::Prefab::Instance& source)
AZStd::unique_ptr<AZ::Entity> 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<AZ::u64>(entityId), source.GetTemplateSourcePath().c_str());
instance, "SpawnbleUtils were unable to locate entity alias with id %zu in Instance '%.*s' for replacing.",
aznumeric_cast<AZ::u64>(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>(AZ::Entity::MakeId(), entityData->get().GetName());
AZStd::unique_ptr<AZ::Entity> result = instance->ReplaceEntity(AZStd::move(placeholder), alias);
return result;
return instance->ReplaceEntity(AZStd::move(placeholder), alias);
}
AZStd::unique_ptr<AZ::Entity> 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<AZ::u64>(entityId));
AZStd::unique_ptr<AZ::Entity> original = AZStd::move(source.GetEntities()[index]);
AZ_Assert(
original, "SpawnbleUtils were unable to locate entity with id %zu in Spawnable for replacing.",
aznumeric_cast<AZ::u64>(entityId));
source.GetEntities()[index] = AZStd::make_unique<AZ::Entity>(original->GetId(), original->GetName());
original->SetId(AZ::Entity::MakeId());
return original;
}
template<typename Source>
AZStd::pair<AZStd::unique_ptr<AZ::Entity>, 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<AZStd::unique_ptr<AZ::Entity>, 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<AZ::Entity>(), AzFramework::Spawnable::EntityAliasType::Additional);
return ResultPair(AZStd::make_unique<AZ::Entity>(), AzFramework::Spawnable::EntityAliasType::Additional);
case PCU::EntityAliasType::Merge:
ResultPair(AZStd::make_unique<AZ::Entity>(), AzFramework::Spawnable::EntityAliasType::Merge);
return ResultPair(AZStd::make_unique<AZ::Entity>(), AzFramework::Spawnable::EntityAliasType::Merge);
default:
AZ_Assert(
false, "Invalid PrefabProcessorContext::EntityAliasType type (%i) provided.", aznumeric_cast<uint64_t>(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<AZ::Entity>& entity)
{
AZ::Entity* result = replacement.get();
target.GetEntities().push_back(AZStd::move(replacement));
EntityAliasStore store;
store.m_aliasType = storedAliasType;
store.m_source.emplace<EntityAliasPrefabLink>(AZStd::move(sourcePrefabName), AZStd::move(alias));
store.m_target.emplace<EntityAliasSpawnableLink>(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<AZ::u64>(entityId));
return nullptr;
}
}
else
{
AZ_Assert(false, "Entity with id %llu was not found in the source prefab.", static_cast<AZ::u64>(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<EntityAliasSpawnableLink>(source, entityId);
store.m_target.emplace<EntityAliasSpawnableLink>(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<AZ::u64>(entityId));
return nullptr;
}
AzFramework::TransformComponent* transform = entity->FindComponent<AzFramework::TransformComponent>();
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)
@@ -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);