Fix issues with components and remove components when entities are destroyed

This commit is contained in:
Daniel Chappuis 2019-01-09 12:19:58 +01:00
parent 4e438d3ccc
commit d8d490bff9
7 changed files with 68 additions and 92 deletions

View File

@ -200,7 +200,6 @@ void ProxyShapesComponents::addComponent(Entity entity, bool isSleeping, const P
linkProxyShapeWithEntity(entity, index);
assert(mSleepingStartIndex <= mNbComponents);
assert(mPreviousBodyProxyShapes[mNextBodyProxyShapes[index]] == index || !hasNextProxyShape(index));
assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index]] == index || !hasPreviousProxyShape(index));
}
@ -305,43 +304,6 @@ void ProxyShapesComponents::swapComponents(uint32 index1, uint32 index2) {
assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index2]] == index2 || !hasPreviousProxyShape(index2));
}
// Perform garbage collection to remove unused components
void ProxyShapesComponents::garbageCollection(const EntityManager& entityManager) {
// TODO : Make sure we call this method each frame
// We use lazy garbage collection. The idea is to pick random components and destroy
// them if their corresponding entities have been destroyed. We do this until we hit
// GARBAGE_COLLECTION_MAX_VALID_ENTITIES in a row. Therefore, it cost almost nothing
// if there are no destroyed entities and it very quickly destroys components where there
// are a lot of destroyed entities.
uint32 nbHitValidEntitiesInARow = 0;
// For random number generation
std::random_device rd;
std::mt19937 eng(rd());
while (mNbComponents > 0 && nbHitValidEntitiesInARow < GARBAGE_COLLECTION_MAX_VALID_ENTITIES) {
// Select a random index in the components array
std::uniform_int_distribution<uint32> distr(0, mNbComponents - 1);
uint32 i = distr(eng);
// If the corresponding entity is valid
if (entityManager.isValid(mEntities[i])) {
nbHitValidEntitiesInARow++;
continue;
}
nbHitValidEntitiesInARow = 0;
// Destroy the component
removeComponent(i);
}
}
// Remove a component at a given index
void ProxyShapesComponents::removeComponent(uint32 index) {
@ -430,12 +392,13 @@ void ProxyShapesComponents::removeComponent(uint32 index) {
if (mSleepingStartIndex != mNbComponents) {
// We replace the last awake component by the last sleeping component
moveComponentToIndex(mNbComponents - 1, index);
mSleepingStartIndex--;
moveComponentToIndex(mNbComponents - 1, mSleepingStartIndex - 1);
}
mSleepingStartIndex--;
}
assert(mSleepingStartIndex <= mNbComponents);
mNbComponents--;
}
@ -454,6 +417,7 @@ bool ProxyShapesComponents::hasNextProxyShape(uint32 index) const {
// Destroy a component at a given index
void ProxyShapesComponents::destroyComponent(uint32 index) {
assert(index < mNbComponents);
mEntities[index].~Entity();
mLocalBounds[index].~AABB();
@ -500,3 +464,19 @@ void ProxyShapesComponents::setIsEntitySleeping(Entity entity, bool isSleeping)
assert(mNextBodyProxyShapes[mPreviousBodyProxyShapes[index]] == index || !hasPreviousProxyShape(index));
}
// Remove all the components of a given entity
void ProxyShapesComponents::removeComponents(Entity entity) {
auto it = mMapEntityToComponentIndex.find(entity);
// While there are components for this entity
while (it != mMapEntityToComponentIndex.end()) {
// Remove the component
removeComponent(it->second);
it = mMapEntityToComponentIndex.find(entity);
}
assert(!mMapEntityToComponentIndex.containsKey(entity));
}

View File

@ -169,11 +169,11 @@ class ProxyShapesComponents {
/// Add a component
void addComponent(Entity entity, bool isSleeping, const ProxyShapeComponent& component);
/// Remove all the components of a given entity
void removeComponents(Entity entity);
/// Notify if a given entity is sleeping or not
void setIsEntitySleeping(Entity entity, bool isSleeping);
/// Perform garbage collection to remove unused components
void garbageCollection(const EntityManager& entityManager);
};
}

View File

@ -140,45 +140,6 @@ void TransformComponents::addComponent(Entity entity, bool isSleeping, const Tra
assert(mNbComponents == static_cast<uint32>(mMapEntityToComponentIndex.size()));
}
// Perform garbage collection to remove unused components
void TransformComponents::garbageCollection(const EntityManager& entityManager) {
// TODO : Make sure we call this method each frame
// We use lazy garbage collection. The idea is to pick random components and destroy
// them if their corresponding entities have been destroyed. We do this until we hit
// GARBAGE_COLLECTION_MAX_VALID_ENTITIES in a row. Therefore, it cost almost nothing
// if there are no destroyed entities and it very quickly destroys components where there
// are a lot of destroyed entities.
uint32 nbHitValidEntitiesInARow = 0;
// For random number generation
std::random_device rd;
std::mt19937 eng(rd());
while (mNbComponents > 0 && nbHitValidEntitiesInARow < GARBAGE_COLLECTION_MAX_VALID_ENTITIES) {
// Select a random index in the components array
std::uniform_int_distribution<uint32> distr(0, mNbComponents - 1);
uint32 i = distr(eng);
// If the corresponding entity is valid
if (entityManager.isValid(mEntities[i])) {
nbHitValidEntitiesInARow++;
continue;
}
nbHitValidEntitiesInARow = 0;
// Destroy the component
removeComponent(i);
}
assert(mNbComponents == static_cast<uint32>(mMapEntityToComponentIndex.size()));
}
// Remove a component at a given index
void TransformComponents::removeComponent(uint32 index) {
@ -214,14 +175,15 @@ void TransformComponents::removeComponent(uint32 index) {
if (mSleepingStartIndex != mNbComponents) {
// We replace the last awake component by the last sleeping component
moveComponentToIndex(mNbComponents - 1, index);
mSleepingStartIndex--;
moveComponentToIndex(mNbComponents - 1, mSleepingStartIndex - 1);
}
mSleepingStartIndex--;
}
mNbComponents--;
assert(mSleepingStartIndex <= mNbComponents);
assert(mNbComponents == static_cast<uint32>(mMapEntityToComponentIndex.size()));
}
@ -309,8 +271,26 @@ void TransformComponents::swapComponents(uint32 index1, uint32 index2) {
// Destroy a component at a given index
void TransformComponents::destroyComponent(uint32 index) {
assert(index < mNbComponents);
assert(mMapEntityToComponentIndex[mEntities[index]] == index);
mMapEntityToComponentIndex.remove(mEntities[index]);
mEntities[index].~Entity();
mTransforms[index].~Transform();
}
// Remove all the components of a given entity
void TransformComponents::removeComponents(Entity entity) {
auto it = mMapEntityToComponentIndex.find(entity);
// If there is a component for this entity
if (it != mMapEntityToComponentIndex.end()) {
// Remove the component
removeComponent(it->second);
}
assert(!mMapEntityToComponentIndex.containsKey(entity));
}

View File

@ -105,8 +105,8 @@ class TransformComponents : public Components {
/// Add a component
void addComponent(Entity entity, bool isSleeping, const TransformComponent& component);
/// Perform garbage collection to remove unused components
void garbageCollection(const EntityManager& entityManager);
/// Remove all the components of a given entity
void removeComponents(Entity entity);
/// Return the transform of an entity
Transform& getTransform(Entity entity) const;

View File

@ -197,8 +197,8 @@ void CollisionWorld::destroyCollisionBody(CollisionBody* collisionBody) {
// Reset the contact manifold list of the body
collisionBody->resetContactManifoldsList();
// Destroy the corresponding entity
mEntityManager.destroyEntity(collisionBody->getEntity());
// Destroy the entity and its components
destroyEntity(collisionBody->getEntity());
// Call the destructor of the collision body
collisionBody->~CollisionBody();
@ -248,6 +248,19 @@ void CollisionWorld::notifyBodySleeping(Entity entity, bool isSleeping) {
mProxyShapesComponents.setIsEntitySleeping(entity, isSleeping);
}
// Destroy an entity and all the associated components
void CollisionWorld::destroyEntity(Entity entity) {
// Destroy the corresponding entity
mEntityManager.destroyEntity(entity);
// TODO : Make sure we notify all the components here ...
// Notify all the components
mTransformComponents.removeComponents(entity);
mProxyShapesComponents.removeComponents(entity);
}
// Test if the AABBs of two bodies overlap
/**
* @param body1 Pointer to the first body to test

View File

@ -128,6 +128,9 @@ class CollisionWorld {
/// Notify the world if a body is sleeping or not
void notifyBodySleeping(Entity entity, bool isSleeping);
/// Destroy an entity and all the associated components
void destroyEntity(Entity entity);
public :
// -------------------- Methods -------------------- //

View File

@ -479,8 +479,8 @@ void DynamicsWorld::destroyRigidBody(RigidBody* rigidBody) {
// Reset the contact manifold list of the body
rigidBody->resetContactManifoldsList();
// Destroy the corresponding entity
mEntityManager.destroyEntity(rigidBody->getEntity());
// Destroy the corresponding entity and its components
destroyEntity(rigidBody->getEntity());
// Call the destructor of the rigid body
rigidBody->~RigidBody();