From 8792cac88a863d9b1daacf6d6964c5d58af80062 Mon Sep 17 00:00:00 2001 From: sconel Date: Mon, 17 May 2021 18:50:21 -0700 Subject: [PATCH 1/6] Updating SpawnableEntitiesManager to handle entity references during spawn --- .../Spawnable/SpawnableEntitiesManager.cpp | 28 ++++++++++++++----- .../Spawnable/SpawnableEntitiesManager.h | 7 ++++- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 3ab004b516..ac71b9fed8 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -200,11 +201,13 @@ namespace AzFramework } } - AZ::Entity* SpawnableEntitiesManager::SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext) + AZ::Entity* SpawnableEntitiesManager::SpawnSingleEntity(const AZ::Entity& entityTemplate, EntityIdMap& spawnableToInstanceEntityIdMap, + AZ::SerializeContext& serializeContext) { - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); + AZ::Entity* clone = AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entityTemplate, spawnableToInstanceEntityIdMap, &serializeContext); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); GameEntityContextRequestBus::Broadcast(&GameEntityContextRequestBus::Events::AddGameEntity, clone); return clone; } @@ -217,13 +220,16 @@ namespace AzFramework size_t spawnedEntitiesCount = ticket.m_spawnedEntities.size(); const Spawnable::EntityList& entities = ticket.m_spawnable->GetEntities(); + EntityIdMap& spawnableToInstanceEntityIdMap = ticket.m_spawnableToInstanceEntityIdMap; + size_t entitiesSize = entities.size(); ticket.m_spawnedEntities.reserve(ticket.m_spawnedEntities.size() + entitiesSize); ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesSize); + spawnableToInstanceEntityIdMap.reserve(entitiesSize); for(size_t i=0; iGetEntities(); + EntityIdMap& spawnableToInstanceEntityIdMap = ticket.m_spawnableToInstanceEntityIdMap; + size_t entitiesSize = entities.size(); ticket.m_spawnedEntities.reserve(ticket.m_spawnedEntities.size() + entitiesSize); ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesSize); + spawnableToInstanceEntityIdMap.reserve(entitiesSize); for (size_t index : request.m_entityIndices) { - ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[index], serializeContext)); + ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[index], spawnableToInstanceEntityIdMap, serializeContext)); ticket.m_spawnedEntityIndices.push_back(index); } ticket.m_loadAll = false; @@ -337,6 +346,8 @@ namespace AzFramework // Rebuild the list of entities. ticket.m_spawnedEntities.clear(); const Spawnable::EntityList& entities = request.m_spawnable->GetEntities(); + EntityIdMap& spawnableToInstanceEntityIdMap = ticket.m_spawnableToInstanceEntityIdMap; + if (ticket.m_loadAll) { // The new spawnable may have a different number of entities and since the intent of the user was @@ -346,7 +357,9 @@ namespace AzFramework size_t entitiesSize = entities.size(); for (size_t i = 0; i < entitiesSize; ++i) { - ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[i], serializeContext)); + ticket.m_spawnedEntities.push_back( + SpawnSingleEntity(*entities[i], spawnableToInstanceEntityIdMap, serializeContext)); + ticket.m_spawnedEntityIndices.push_back(i); } } @@ -356,7 +369,8 @@ namespace AzFramework for (size_t index : ticket.m_spawnedEntityIndices) { ticket.m_spawnedEntities.push_back( - index < entitiesSize ? SpawnSingleEntity(*entities[index], serializeContext) : nullptr); + index < entitiesSize ? + SpawnSingleEntity(*entities[index], spawnableToInstanceEntityIdMap, serializeContext) : nullptr); } } ticket.m_spawnable = AZStd::move(request.m_spawnable); diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index c70b9ccaa6..b101268c5f 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -29,6 +29,8 @@ namespace AZ namespace AzFramework { + using EntityIdMap = AZStd::unordered_map; + class SpawnableEntitiesManager : public SpawnableEntitiesInterface::Registrar { @@ -81,6 +83,8 @@ namespace AzFramework AZStd::vector m_spawnedEntities; AZStd::vector m_spawnedEntityIndices; + EntityIdMap m_spawnableToInstanceEntityIdMap; + AZ::Data::Asset m_spawnable; uint32_t m_nextTicketId{ 0 }; //!< Next id for this ticket. uint32_t m_currentTicketId{ 0 }; //!< The id for the command that should be executed. @@ -140,7 +144,8 @@ namespace AzFramework using Requests = AZStd::variant; - AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext); + AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, EntityIdMap& spawnableToInstanceEntityIdMap, + AZ::SerializeContext& serializeContext); bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext); From 66a7db44f7521bdabee11aeb69a6fa3cab620b78 Mon Sep 17 00:00:00 2001 From: sconel Date: Tue, 18 May 2021 17:13:24 -0700 Subject: [PATCH 2/6] Reduced scope of change to focus on SpawnAllEntities --- .../Spawnable/SpawnableEntitiesManager.cpp | 60 ++++++++----------- .../Spawnable/SpawnableEntitiesManager.h | 6 +- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index ad2e1551d5..39501ba0cd 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -216,6 +216,18 @@ namespace AzFramework return clone; } + Spawnable::EntityList* SpawnableEntitiesManager::CloneAllEntities(const Spawnable::EntityList& entitiesTemplate, + AZ::SerializeContext& serializeContext) + { + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneIdMap; + templateToCloneIdMap.reserve(entitiesTemplate.size()); + + return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( + &entitiesTemplate, templateToCloneIdMap, &serializeContext); + } + bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext) { Ticket& ticket = GetTicketPayload(*request.m_ticket); @@ -235,44 +247,16 @@ namespace AzFramework spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesToSpawnSize); - // TEMP: To be replaced by IdUtils::Remapper - using EntityIdMap = AZStd::unordered_map; - EntityIdMap templateToCloneIdMap; - // \TEMP - // Clone the entities from Spawnable + Spawnable::EntityList* clonedEntities = CloneAllEntities(entitiesToSpawn, serializeContext); + AZ_Assert(clonedEntities != nullptr, "Failed to clone entities while processing a SpawnAllEntitiesCommand"); + + spawnedEntities.insert(spawnedEntities.end(), clonedEntities->begin(), clonedEntities->end()); + + // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { - const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; - - AZ::Entity* clone = serializeContext.CloneObject(&entityTemplate); - AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); - clone->SetId(AZ::Entity::MakeId()); - - spawnedEntities.push_back(clone); spawnedEntityIndices.push_back(i); - - // TEMP: To be replaced by IdUtils::Remapper - templateToCloneIdMap[entityTemplate.GetId()] = clone->GetId(); - - // Update TransformComponent parent Id. It is guaranteed for the entities array to be sorted from parent->child here. - auto* transformComponent = clone->FindComponent(); - AZ::EntityId parentId = transformComponent->GetParentId(); - if (parentId.IsValid()) - { - auto it = templateToCloneIdMap.find(parentId); - if (it != templateToCloneIdMap.end()) - { - transformComponent->SetParentRelative(it->second); - } - else - { - AZ_Warning( - "SpawnableEntitiesManager", false, "Entity %s doesn't have the parent entity %s present in the spawnable", - clone->GetName().c_str(), parentId.ToString().data()); - } - } - // \TEMP } // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. @@ -438,10 +422,16 @@ namespace AzFramework // to load every, simply start over. ticket.m_spawnedEntityIndices.clear(); + // Clone the entities from Spawnable + Spawnable::EntityList* clonedEntities = CloneAllEntities(entities, serializeContext); + AZ_Assert(clonedEntities != nullptr, "Failed to clone entities while processing a SpawnAllEntitiesCommand"); + + ticket.m_spawnedEntities.insert(ticket.m_spawnedEntities.end(), clonedEntities->begin(), clonedEntities->end()); + + // Mark all indices as spawned size_t entitiesSize = entities.size(); for (size_t i = 0; i < entitiesSize; ++i) { - ticket.m_spawnedEntities.push_back(SpawnSingleEntity(*entities[i], serializeContext)); ticket.m_spawnedEntityIndices.push_back(i); } } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index bcd6a7b6ea..c9e1fbd715 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -83,7 +83,6 @@ namespace AzFramework AZStd::vector m_spawnedEntities; AZStd::vector m_spawnedEntityIndices; - EntityIdMap m_spawnableToInstanceEntityIdMap; AZ::Data::Asset m_spawnable; uint32_t m_nextTicketId{ 0 }; //!< Next id for this ticket. @@ -146,7 +145,10 @@ namespace AzFramework using Requests = AZStd::variant; - AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, EntityIdMap& spawnableToInstanceEntityIdMap, + AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, + AZ::SerializeContext& serializeContext); + + Spawnable::EntityList* CloneAllEntities(const Spawnable::EntityList& entitiesTemplate, AZ::SerializeContext& serializeContext); bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); From 8733f0e4928eb8481dfcf153cc12ada493daad3a Mon Sep 17 00:00:00 2001 From: sconel Date: Tue, 18 May 2021 17:16:44 -0700 Subject: [PATCH 3/6] Remove extra newline --- .../AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index c9e1fbd715..3def85170f 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -83,7 +83,6 @@ namespace AzFramework AZStd::vector m_spawnedEntities; AZStd::vector m_spawnedEntityIndices; - AZ::Data::Asset m_spawnable; uint32_t m_nextTicketId{ 0 }; //!< Next id for this ticket. uint32_t m_currentTicketId{ 0 }; //!< The id for the command that should be executed. From d8126d59c7082bceff7b31622e504fb3cc1e10c0 Mon Sep 17 00:00:00 2001 From: sconel Date: Wed, 19 May 2021 08:39:00 -0700 Subject: [PATCH 4/6] Moved to iterative clone instead of bulk, addressed PR feedback --- .../Spawnable/SpawnableEntitiesManager.cpp | 50 +++++++++++-------- .../Spawnable/SpawnableEntitiesManager.h | 4 +- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 39501ba0cd..fd838d5cb5 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -216,16 +216,11 @@ namespace AzFramework return clone; } - Spawnable::EntityList* SpawnableEntitiesManager::CloneAllEntities(const Spawnable::EntityList& entitiesTemplate, - AZ::SerializeContext& serializeContext) + AZ::Entity* SpawnableEntitiesManager::CloneSingleEntity(const AZ::Entity& entityTemplate, + EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext) { - // Map keeps track of ids from template (spawnable) to clone (instance) - // Allowing patch ups of fields referring to entityIds outside of a given entity - EntityIdMap templateToCloneIdMap; - templateToCloneIdMap.reserve(entitiesTemplate.size()); - return AZ::IdUtils::Remapper::CloneObjectAndGenerateNewIdsAndFixRefs( - &entitiesTemplate, templateToCloneIdMap, &serializeContext); + &entityTemplate, templateToCloneEntityIdMap, &serializeContext); } bool SpawnableEntitiesManager::ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext) @@ -243,19 +238,25 @@ namespace AzFramework const Spawnable::EntityList& entitiesToSpawn = ticket.m_spawnable->GetEntities(); size_t entitiesToSpawnSize = entitiesToSpawn.size(); + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneEntityIdMap; + // Reserve buffers spawnedEntities.reserve(spawnedEntities.size() + entitiesToSpawnSize); - ticket.m_spawnedEntityIndices.reserve(ticket.m_spawnedEntityIndices.size() + entitiesToSpawnSize); - - // Clone the entities from Spawnable - Spawnable::EntityList* clonedEntities = CloneAllEntities(entitiesToSpawn, serializeContext); - AZ_Assert(clonedEntities != nullptr, "Failed to clone entities while processing a SpawnAllEntitiesCommand"); - - spawnedEntities.insert(spawnedEntities.end(), clonedEntities->begin(), clonedEntities->end()); + spawnedEntityIndices.reserve(spawnedEntityIndices.size() + entitiesToSpawnSize); + templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); // Mark all indices as spawned for (size_t i = 0; i < entitiesToSpawnSize; ++i) { + const AZ::Entity& entityTemplate = *entitiesToSpawn[i]; + + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + + spawnedEntities.emplace_back(clone); spawnedEntityIndices.push_back(i); } @@ -422,16 +423,23 @@ namespace AzFramework // to load every, simply start over. ticket.m_spawnedEntityIndices.clear(); - // Clone the entities from Spawnable - Spawnable::EntityList* clonedEntities = CloneAllEntities(entities, serializeContext); - AZ_Assert(clonedEntities != nullptr, "Failed to clone entities while processing a SpawnAllEntitiesCommand"); + size_t entitiesToSpawnSize = entities.size(); - ticket.m_spawnedEntities.insert(ticket.m_spawnedEntities.end(), clonedEntities->begin(), clonedEntities->end()); + // Map keeps track of ids from template (spawnable) to clone (instance) + // Allowing patch ups of fields referring to entityIds outside of a given entity + EntityIdMap templateToCloneEntityIdMap; + templateToCloneEntityIdMap.reserve(entitiesToSpawnSize); // Mark all indices as spawned - size_t entitiesSize = entities.size(); - for (size_t i = 0; i < entitiesSize; ++i) + for (size_t i = 0; i < entitiesToSpawnSize; ++i) { + const AZ::Entity& entityTemplate = *entities[i]; + + AZ::Entity* clone = CloneSingleEntity(entityTemplate, templateToCloneEntityIdMap, serializeContext); + + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + + ticket.m_spawnedEntities.emplace_back(clone); ticket.m_spawnedEntityIndices.push_back(i); } } diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h index 3def85170f..e20f58ac76 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.h @@ -147,8 +147,8 @@ namespace AzFramework AZ::Entity* SpawnSingleEntity(const AZ::Entity& entityTemplate, AZ::SerializeContext& serializeContext); - Spawnable::EntityList* CloneAllEntities(const Spawnable::EntityList& entitiesTemplate, - AZ::SerializeContext& serializeContext); + AZ::Entity* CloneSingleEntity(const AZ::Entity& entityTemplate, + EntityIdMap& templateToCloneEntityIdMap, AZ::SerializeContext& serializeContext); bool ProcessRequest(SpawnAllEntitiesCommand& request, AZ::SerializeContext& serializeContext); bool ProcessRequest(SpawnEntitiesCommand& request, AZ::SerializeContext& serializeContext); From 4769664e9e660b40696168cedb65645b0ea12f20 Mon Sep 17 00:00:00 2001 From: sconel Date: Wed, 19 May 2021 08:50:14 -0700 Subject: [PATCH 5/6] Updating the loadAll flag after a SpawnAllCommand --- .../AzFramework/Spawnable/SpawnableEntitiesManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index fd838d5cb5..0418bce3a2 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -260,6 +260,8 @@ namespace AzFramework spawnedEntityIndices.push_back(i); } + ticket.m_loadAll = true; + // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. if (request.m_preInsertionCallback) { From 31e5a312b4b6499d4047ba941ebfff9a4a146a22 Mon Sep 17 00:00:00 2001 From: sconel Date: Wed, 19 May 2021 08:58:12 -0700 Subject: [PATCH 6/6] Updated loadAll check to set to false if previous entities already spawned on ticket --- .../Spawnable/SpawnableEntitiesManager.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp index 0418bce3a2..8045766686 100644 --- a/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp +++ b/Code/Framework/AzFramework/AzFramework/Spawnable/SpawnableEntitiesManager.cpp @@ -260,7 +260,16 @@ namespace AzFramework spawnedEntityIndices.push_back(i); } - ticket.m_loadAll = true; + // loadAll is true if every entity has been spawned only once + if (spawnedEntities.size() == entitiesToSpawnSize) + { + ticket.m_loadAll = true; + } + else + { + // Case where there were already spawns from a previous request + ticket.m_loadAll = false; + } // Let other systems know about newly spawned entities for any pre-processing before adding to the scene/game context. if (request.m_preInsertionCallback)