Merge pull request #1126 from aws-lumberyard-dev/SpawnableEntityIdMapping

Spawnable entity id mapping
This commit is contained in:
AMZN-koppersr
2021-06-04 15:15:46 -07:00
committed by GitHub
10 changed files with 559 additions and 230 deletions
@@ -15,6 +15,7 @@
#include <AzFramework/Application/Application.h>
#include <AzFramework/Spawnable/SpawnableAssetHandler.h>
#include <AzFramework/Spawnable/SpawnableEntitiesManager.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzTest/AzTest.h>
namespace UnitTest
@@ -86,6 +87,42 @@ namespace UnitTest
}
}
void CreateRecursiveHierarchy()
{
AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities();
size_t numElements = entities.size();
AZ::EntityId parent;
for (size_t i=0; i<numElements; ++i)
{
AZStd::unique_ptr<AZ::Entity>& entity = entities[i];
auto component = entity->CreateComponent<AzFramework::TransformComponent>();
if (i > 0)
{
component->SetParent(parent);
}
parent = entity->GetId();
}
}
void CreateSingleParent()
{
AzFramework::Spawnable::EntityList& entities = m_spawnable->GetEntities();
size_t numElements = entities.size();
if (numElements > 0)
{
AZ::EntityId parent = entities[0]->GetId();
for (size_t i = 0; i < numElements; ++i)
{
AZStd::unique_ptr<AZ::Entity>& entity = entities[i];
auto component = entity->CreateComponent<AzFramework::TransformComponent>();
if (i > 0)
{
component->SetParent(parent);
}
}
}
}
protected:
AZ::Data::Asset<AzFramework::Spawnable>* m_spawnableAsset { nullptr };
AzFramework::SpawnableEntitiesManager* m_manager { nullptr };
@@ -109,17 +146,50 @@ namespace UnitTest
{
spawnedEntitiesCount += entities.size();
};
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(callback));
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(callback);
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_EQ(NumEntities, spawnedEntitiesCount);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_SetParentOnSpawnedEntities_LineageIsPreserved)
{
static constexpr size_t NumEntities = 4;
FillSpawnable(NumEntities);
CreateRecursiveHierarchy();
auto callback = [](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
AZ::EntityId parentId;
bool isFirst = true;
for (const AZ::Entity* entity : entities)
{
if (!isFirst)
{
auto transform = entity->GetTransform();
ASSERT_NE(nullptr, transform);
EXPECT_EQ(parentId, transform->GetParentId());
}
else
{
isFirst = false;
}
parentId = entity->GetId();
}
};
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(callback);
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnAllEntities_DeleteTicketBeforeCall_NoCrash)
{
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->SpawnAllEntities(ticket, AzFramework::SpawnablePriority_Default);
m_manager->SpawnAllEntities(ticket);
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -129,11 +199,175 @@ namespace UnitTest
// SpawnEntities
//
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_Call_AllEntitiesSpawned)
{
static constexpr size_t NumEntities = 4;
FillSpawnable(NumEntities);
AZStd::vector<size_t> indices = { 0, 2, 3, 1 };
size_t spawnedEntitiesCount = 0;
auto callback = [&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
spawnedEntitiesCount += entities.size();
};
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(callback);
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_EQ(NumEntities, spawnedEntitiesCount);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_SpawnTheSameEntity_AllEntitiesSpawned)
{
static constexpr size_t NumEntities = 1;
FillSpawnable(NumEntities);
AZStd::vector<size_t> indices = { 0, 0 };
size_t spawnedEntitiesCount = 0;
auto callback =
[&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
spawnedEntitiesCount += entities.size();
};
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(callback);
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_MultipleSpawns_AllEntitiesSpawned)
{
static constexpr size_t NumEntities = 4;
FillSpawnable(NumEntities);
AZStd::vector<size_t> indices = { 0, 2, 3, 1 };
size_t spawnedEntitiesCount = 0;
auto callback =
[&spawnedEntitiesCount](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
spawnedEntitiesCount += entities.size();
};
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(callback);
m_manager->SpawnEntities(*m_ticket, indices, optionalArgs);
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_EQ(NumEntities * 2, spawnedEntitiesCount);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForNewBatch_AllPointToLatestParent)
{
static constexpr size_t NumEntities = 4;
FillSpawnable(NumEntities);
CreateSingleParent();
AZStd::vector<size_t> indices = { 0, 1, 2, 3 };
AZStd::vector<AZ::EntityId> parents;
auto callback = [&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
AZ::EntityId parent = (*entities.begin())->GetId();
parents.push_back(parent);
auto it = entities.begin();
++it; // Skip the first as that is the parent.
for (; it != entities.end(); ++it)
{
AZ::TransformInterface* transform = (*it)->GetTransform();
ASSERT_NE(nullptr, transform);
ASSERT_EQ(parent, transform->GetParentId());
}
};
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(callback);
optionalArgs.m_referencePreviouslySpawnedEntities = false;
m_manager->SpawnEntities(*m_ticket, indices, optionalArgs);
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_NE(parents[0], parents[1]);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedForContinuedBatch_AllPointToLatestParent)
{
static constexpr size_t NumEntities = 4;
FillSpawnable(NumEntities);
CreateSingleParent();
AZStd::vector<size_t> indices = { 0, 1, 2, 3 };
AZStd::vector<AZ::EntityId> parents;
auto callback =
[&parents](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
AZ::EntityId parent = (*entities.begin())->GetId();
parents.push_back(parent);
auto it = entities.begin();
++it; // Skip the first as that is the parent.
for (; it!=entities.end(); ++it)
{
AZ::TransformInterface* transform = (*it)->GetTransform();
ASSERT_NE(nullptr, transform);
ASSERT_EQ(parent, transform->GetParentId());
}
};
AzFramework::SpawnEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(callback);
optionalArgs.m_referencePreviouslySpawnedEntities = true;
m_manager->SpawnEntities(*m_ticket, indices, optionalArgs);
m_manager->SpawnEntities(*m_ticket, AZStd::move(indices), AZStd::move(optionalArgs));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_NE(parents[0], parents[1]);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_ReferencesAreRemappedAcrossBatches_AllPointToLatestParent)
{
FillSpawnable(4);
CreateSingleParent();
// Spawn a regular batch but with two parents and store the id of the last entity. This will the parent for the next batch.
AZ::EntityId parent;
auto getParent = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
ASSERT_NE(entities.begin(), entities.end());
parent = (*AZStd::prev(entities.end()))->GetId();
};
AzFramework::SpawnEntitiesOptionalArgs optionalArgsFirstBatch;
optionalArgsFirstBatch.m_completionCallback = AZStd::move(getParent);
optionalArgsFirstBatch.m_referencePreviouslySpawnedEntities = true;
m_manager->SpawnEntities(*m_ticket, {0, 1, 2, 3, 0}, AZStd::move(optionalArgsFirstBatch));
// Next, spawn all the entities that have a reference to the parent that was just stored.
auto parentCheck = [&parent](AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableConstEntityContainerView entities)
{
for (auto& it : entities)
{
AZ::TransformInterface* transform = it->GetTransform();
ASSERT_NE(nullptr, transform);
ASSERT_EQ(parent, transform->GetParentId());
}
};
AzFramework::SpawnEntitiesOptionalArgs optionalArgsSecondBatch;
optionalArgsSecondBatch.m_completionCallback = AZStd::move(parentCheck);
optionalArgsSecondBatch.m_referencePreviouslySpawnedEntities = true;
m_manager->SpawnEntities(*m_ticket, {1, 2, 3}, AZStd::move(optionalArgsSecondBatch));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
TEST_F(SpawnableEntitiesManagerTest, SpawnEntities_DeleteTicketBeforeCall_NoCrash)
{
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->SpawnEntities(ticket, AzFramework::SpawnablePriority_Default, {});
m_manager->SpawnEntities(ticket, {/* Deliberate empty list of indices. */});
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -147,7 +381,7 @@ namespace UnitTest
{
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->DespawnAllEntities(ticket, AzFramework::SpawnablePriority_Default);
m_manager->DespawnAllEntities(ticket);
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -161,7 +395,7 @@ namespace UnitTest
{
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->ReloadSpawnable(ticket, AzFramework::SpawnablePriority_Default, *m_spawnableAsset);
m_manager->ReloadSpawnable(ticket, *m_spawnableAsset);
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -188,8 +422,8 @@ namespace UnitTest
spawnedEntitiesCount += entities.size();
};
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default);
m_manager->ListEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
m_manager->SpawnAllEntities(*m_ticket);
m_manager->ListEntities(*m_ticket, AZStd::move(callback));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_TRUE(allValidEntityIds);
@@ -202,7 +436,7 @@ namespace UnitTest
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->ListEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
m_manager->ListEntities(ticket, AZStd::move(callback));
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -233,8 +467,8 @@ namespace UnitTest
}
};
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default);
m_manager->ListIndicesAndEntities(*m_ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
m_manager->SpawnAllEntities(*m_ticket);
m_manager->ListIndicesAndEntities(*m_ticket, AZStd::move(callback));
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
EXPECT_TRUE(allValidEntityIds);
@@ -247,7 +481,7 @@ namespace UnitTest
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->ListIndicesAndEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
m_manager->ListIndicesAndEntities(ticket, AZStd::move(callback));
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -263,7 +497,7 @@ namespace UnitTest
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->ClaimEntities(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
m_manager->ClaimEntities(ticket, AZStd::move(callback));
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -279,7 +513,7 @@ namespace UnitTest
{
AzFramework::EntitySpawnTicket ticket(*m_spawnableAsset);
m_manager->Barrier(ticket, AzFramework::SpawnablePriority_Default, AZStd::move(callback));
m_manager->Barrier(ticket, AZStd::move(callback));
}
m_manager->ProcessQueue(AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
}
@@ -310,8 +544,16 @@ namespace UnitTest
defaultPriorityCallId = callCounter++;
};
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback));
m_manager->SpawnAllEntities(highPriorityTicket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback));
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(defaultCallback);
optionalArgs.m_priority = AzFramework::SpawnablePriority_Default;
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs;
highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback);
highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High;
m_manager->SpawnAllEntities(highPriorityTicket, AZStd::move(highPriortyOptionalArgs));
m_manager->ProcessQueue(
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High |
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);
@@ -338,8 +580,16 @@ namespace UnitTest
defaultPriorityCallId = callCounter++;
};
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_Default, {}, AZStd::move(defaultCallback));
m_manager->SpawnAllEntities(*m_ticket, AzFramework::SpawnablePriority_High, {}, AZStd::move(highCallback));
AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
optionalArgs.m_completionCallback = AZStd::move(defaultCallback);
optionalArgs.m_priority = AzFramework::SpawnablePriority_Default;
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(optionalArgs));
AzFramework::SpawnAllEntitiesOptionalArgs highPriortyOptionalArgs;
highPriortyOptionalArgs.m_completionCallback = AZStd::move(highCallback);
highPriortyOptionalArgs.m_priority = AzFramework::SpawnablePriority_High;
m_manager->SpawnAllEntities(*m_ticket, AZStd::move(highPriortyOptionalArgs));
m_manager->ProcessQueue(
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::High |
AzFramework::SpawnableEntitiesManager::CommandQueuePriority::Regular);