Skip to content

Commit

Permalink
add getEntityId, getEntityById functions
Browse files Browse the repository at this point in the history
  • Loading branch information
mreinstein committed Jan 15, 2024
1 parent 0376362 commit b654a7d
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 0 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,32 @@ ECS.getEntities(world, [ 'test_component' ]).length // because we are not defer
```


### get entity by unique id

ECS will generate a unique integer id for every entity created in a world:

```javascript
const world = ECS.createWorld()
const e = ECS.createEntity(world)
const e2 = ECS.createEntity(world)

const id1 = ECS.getEntityId(world, e)
const id2 = ECS.getEntityId(world, e2)

console.log(id1) // 1
console.log(id2) // 2
```

You can then lookup this entity directly:
```javascript
const e = ECS.getEntityById(world, id1) // e is the entity
```

This can be useful in cases where you need to reference an entity from another context. for example,
if you're running a networked simulation and need to refer to a specific entity across peers.



### devtools chrome extension

If you'd like to see a real time view of the data in your ECS powered program, there is a dev tools extension!
Expand Down
29 changes: 29 additions & 0 deletions ecs.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ export function createWorld (worldId=Math.ceil(Math.random() * 999999999) ) {
* @type {World}
*/
const world = {

entityIds: new Map(), // maps both entityId -> entity, and entity -> entityId
nextId: 1, // id of the next entity to be created

entities: [ ],
filters: { },
systems: [ ],
Expand Down Expand Up @@ -177,11 +181,26 @@ export function createEntity (world) {
world.entities.push(entity)
world.stats.entityCount++

world.entityIds.set(world.nextId, entity)
world.entityIds.set(entity, world.nextId)

world.nextId++

world.listeners._added.add(entity)
return entity
}


export function getEntityId (world, entity) {
return world.entityIds.get(entity)
}


export function getEntityById (world, entityId) {
return world.entityIds.get(entityId)
}


/**
* Adds a component to the entity
* @param {World} world world where listener will be invoked
Expand Down Expand Up @@ -558,6 +577,12 @@ function _removeEntity (world, entity, shiftUpEntities=false) {

const entityToRemoveIdx = world.entities.indexOf(entity)

const entityId = world.entityIds.get(entity)
if (entityId !== undefined) {
world.entityIds.delete(entity)
world.entityIds.delete(entityId)
}

removeItems(world.entities, entityToRemoveIdx, 1)

if (shiftUpEntities) {
Expand Down Expand Up @@ -679,6 +704,10 @@ export function cleanup (world) {
export default {
createWorld,
createEntity,

getEntityId,
getEntityById,

addComponentToEntity,
removeComponentFromEntity,
getEntities,
Expand Down
2 changes: 2 additions & 0 deletions test/createWorld.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import tap from 'tap'
const w = ECS.createWorld()

tap.same(w, {
entityIds: new Map(),
nextId: 1,
entities: [ ],
filters: { },
systems: [ ],
Expand Down
87 changes: 87 additions & 0 deletions test/id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import ECS from '../ecs.js'
import tap from 'tap'


{
const w = ECS.createWorld()

const e1 = ECS.createEntity(w)
const e2 = ECS.createEntity(w)
const e3 = ECS.createEntity(w)

tap.equal(ECS.getEntityId(w, e1), 1, 'generates an integer id')
tap.equal(ECS.getEntityId(w, e2), 2, 'generates an integer id')
tap.equal(ECS.getEntityId(w, e3), 3, 'generates an integer id')

tap.equal(e1, ECS.getEntityById(w, 1), 'retrieves entity by id')
tap.equal(e2, ECS.getEntityById(w, 2), 'retrieves entity by id')
tap.equal(e3, ECS.getEntityById(w, 3), 'retrieves entity by id')
}


// immediate entity removal works as expected
{
const w = ECS.createWorld()

const e1 = ECS.createEntity(w)

tap.equal(ECS.getEntityId(w, e1), 1, 'generates an integer id')
tap.equal(e1, ECS.getEntityById(w, 1), 'retrieves entity by id')

// removing an entity removes it's id

const deferredRemoval = false
ECS.removeEntity(w, e1, deferredRemoval)

tap.equal(ECS.getEntityId(w, e1), undefined, 'entity id is removed')
tap.equal(undefined, ECS.getEntityById(w, 1), 'entity is removed')
}


// deferred entity removal works as expected
{
const w = ECS.createWorld()

const e1 = ECS.createEntity(w)

tap.equal(ECS.getEntityId(w, e1), 1, 'generates an integer id')
tap.equal(e1, ECS.getEntityById(w, 1), 'retrieves entity by id')

// removing an entity removes it's id

const deferredRemoval = true
ECS.removeEntity(w, e1, deferredRemoval)

// entity is still present because we haven't cleaned up yet
tap.equal(ECS.getEntityId(w, e1), 1, 'entity id is removed')
tap.equal(e1, ECS.getEntityById(w, 1), 'retrieves entity by id')

ECS.cleanup(w)

// now they should be gone
tap.equal(ECS.getEntityId(w, e1), undefined, 'entity id is removed')
tap.equal(undefined, ECS.getEntityById(w, 1), 'entity is removed')
}


{
const w = ECS.createWorld()

const e1 = ECS.createEntity(w)
const e2 = ECS.createEntity(w)
const e3 = ECS.createEntity(w)

const deferredRemoval = true

ECS.removeEntity(w, e1, deferredRemoval)
ECS.removeEntity(w, e2, deferredRemoval)
ECS.removeEntity(w, e3, deferredRemoval)

ECS.cleanup(w)

const e4 = ECS.createEntity(w)
tap.equal(ECS.getEntityId(w, e4), 4, 'entity id still increments despite entity removal')
}



0 comments on commit b654a7d

Please sign in to comment.