Skip to content

Commit

Permalink
Merge pull request #45 from chriscoderdr/feat/44-remove-entities
Browse files Browse the repository at this point in the history
feat(#44): ECS.removeEntities(world, query)
  • Loading branch information
mreinstein authored Nov 10, 2024
2 parents de9aae6 + 9594286 commit 41383df
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 1 deletion.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,26 @@ ECS.getEntities(world, [ 'test_component' ]).length // because we are not defer
```



### removeEntities Example

You can use `ECS.removeEntities` to remove all entities with a specified component. For example, if you have entities with an `ephemeral` component and want to delete them from the world, you can do this:

```javascript
// remove all entities that have an 'ephemeral' component
ECS.removeEntities(world, ['ephemeral']);
```

The `removeEntities` function also supports the **not filter**, which allows you to specify components that should **not** be present on the entities you want to remove. For example, to remove entities that have a `transform` component but lack a `health` component, you can use the following code:

```javascript
// remove all entities that have a 'transform' component and lack a 'health' component
ECS.removeEntities(world, ['transform', '!health']);
```

This functionality can be useful for cleaning up entities that meet specific component criteria in your world.


### get entity by unique id

ECS will generate a unique integer id for every entity created in a world:
Expand Down
37 changes: 37 additions & 0 deletions ecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,42 @@ export function addComponentToEntity (world, entity, componentName, componentDat
}
}

/**
* Remove entities from the world that match the given component criteria.
* @param {World} world - The world containing the entities.
* @param {string[]} componentNames - Array of component names to filter by.
* Supports 'not' filters by prefixing component names with '!'.
*/
export function removeEntities(world, componentNames) {
const entitiesToRemove = world.entities.filter((entity) => {
for (const componentName of componentNames) {
const isNotFilter = componentName.startsWith('!');
const actualComponentName = isNotFilter
? componentName.slice(1)
: componentName;

if (isNotFilter) {
// If it's a 'not' filter (prefixed with '!'), the entity should NOT have the component
if (entity[actualComponentName]) {
return false;
}
} else {
// If it's a regular filter, the entity MUST have the component
if (!entity[actualComponentName]) {
return false;
}
}
}
return true;
});

// Remove each matched entity
for (const entity of entitiesToRemove) {
removeEntity(world, entity, false); // remove immediately
}
}



/**
* Get entities from the world with all provided components. Optionally,
Expand Down Expand Up @@ -687,6 +723,7 @@ export default {
getEntities,
getEntity,
removeEntity,
removeEntities,
addSystem,
preFixedUpdate,
fixedUpdate,
Expand Down
39 changes: 39 additions & 0 deletions test/removeEntity.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,3 +220,42 @@ import tap from 'tap'

ECS.cleanup(w)
}

{
// Test removeEntities function with 'not' filter support
const w = ECS.addWorld();

const e1 = ECS.addEntity(w);
ECS.addComponent(w, e1, 'ephemeral', {});

const e2 = ECS.addEntity(w);
ECS.addComponent(w, e2, 'ephemeral', {});
ECS.addComponent(w, e2, 'health', { hp: 100 });

const e3 = ECS.addEntity(w);
ECS.addComponent(w, e3, 'transform', {});
ECS.addComponent(w, e3, 'health', { hp: 100 });

tap.equal(w.entities.length, 3, '3 entities should be present initially');

// Remove all entities with 'ephemeral' component
ECS.removeEntities(w, ['ephemeral']);
ECS.cleanup(w); // process deferred removals
tap.equal(w.entities.length, 1, 'only 1 entity should remain after removing all entities with "ephemeral"');

// Add entities again for next test
const e4 = ECS.addEntity(w);
ECS.addComponent(w, e4, 'transform', {});
ECS.addComponent(w, e4, 'ephemeral', {});

const e5 = ECS.addEntity(w);
ECS.addComponent(w, e5, 'transform', {});
ECS.addComponent(w, e5, 'health', { hp: 100 });

tap.equal(w.entities.length, 3, '3 entities should be present after adding new entities');

// Remove entities with 'transform' but not 'health'
ECS.removeEntities(w, ['transform', '!health']);
ECS.cleanup(w); // process deferred removals
tap.equal(w.entities.length, 2, '2 entities should remain after removing entities with "transform" but not "health"');
}
7 changes: 7 additions & 0 deletions types/ecs.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ export function removeComponentFromEntity(world: World, entity: Entity, componen
* @returns {void} returns early if entity does not exist in world
*/
export function removeEntity(world: World, entity: Entity, deferredRemoval?: boolean): void;
/**
* Remove entities from the world that match the given component criteria.
* @param {World} world - The world containing the entities.
* @param {string[]} componentNames - Array of component names to filter by.
* Supports 'not' filters by prefixing component names with '!'.
*/
export function removeEntities(world: World, componentNames: string[]): void;
/**
* Get entities from the world with all provided components. Optionally,
* @param {World} world
Expand Down
2 changes: 1 addition & 1 deletion types/ecs.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 41383df

Please sign in to comment.