# Ecsjs cheat sheet

- [Define components](#define-components)
- [Register components](#register-components)
- [Add or overwrite entity component data](#add-or-overwrite-entity-component-data)
- [Get entity data](#get-entity-data)
- [Check if an entity has a component](#check-if-an-entity-has-a-component)
- [Remove components from an entity](#remove-components-from-an-entity)
- [Destroy entities](#destroy-entities)
- [Clear all entities and their component data](#clear-all-entities-and-their-component-data)
- [Get the first entity value by component](#get-the-first-entity-value-by-component)
- [Get the first entity id by component](#get-the-first-entity-id-by-component)
- [Query entities by component](#query-entities-by-component)
- [Iterate entities by component](#iterate-entities-by-component)
---

> **IMPORTANT** 
>
> - You must register your components before passing them to other ecsjs functions<br> otherwise a [ComponentNotRegistered](https://ecsjs.gitlab.io/ecs/classes/ComponentNotRegistered.html) error will be thrown.
> - [ecs.register](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#register) will throw [ComponentTypeKeyMissing](https://ecsjs.gitlab.io/ecs/classes/ComponentTypeKeyMissing.html) if a class name is missing (e.g. anonymous classes).
> - [ecs.register](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#register) will throw [ComponentAlreadyRegistered](https://ecsjs.gitlab.io/ecs/classes/ComponentAlreadyRegistered.html) if a component is already registered.
> - Component data is always returned by reference for all ecsjs functions.<br>
> This allows instant mutation of the component data

### Define components
```js
// components are defined as classes
class Player {
  constructor(speed) {
    this.speed = speed
  }
}
class Position {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
}
class Velocity {
  constructor(x, y) {
    this.x = x
    this.y = y
  }
}
// etc...
```
---

### Register components

```js
// register a component
ecs.register(Player)

// optionally register multiple components
ecs.register(Player, Position)
```
- [ecs.register](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#register)

---

### Add or overwrite entity component data
```js
// get the next entity id
const newEntityId = ecs.getNextId()

// add a Player instance to the new entity
const player = ecs.set(newEntityId, new Player())

// you can also add multiple component instances to a new entity
const [player, position, velocity] = ecs.set(
  newEntityId, 
  new Player(), 
  new Position(10, 40),
  new Velocity(0, 0)
  // ...
)

// overwrite component(s)
const player = ecs.set(entityId, new Velocity(10, 0))
```
- [ecs.set](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#set)
- [ecs.getNextId](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#getnextid)

---

### Get entity data
```js
// get component values by id
const player = ecs.get(entityId, Player)

// optionally return related components matching the same id
const [player, position] = ecs.get(entityId, Player, Position) ?? []
```
- [ecs.get](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#get)

---

### Check if an entity has a component
```js
const hasPlayer = ecs.has(entityId, Player)
// or
const hasAll = ecs.hasAll(entityId, Position, Velocity)
// or
const hasAny = ecs.hasAny(entityId, Player, Enemy)
```
- [ecs.has](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#has)
- [ecs.hasAny](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#hasany)
- [ecs.hasAll](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#hasall)

---

### Remove components from an entity
```js
ecs.remove(entityId, Player)

// optionally remove related component data matching the same id
ecs.remove(entityId, Player, Position)
```
- [ecs.remove](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#remove)

---

### Destroy entities
```js
// removes all data associated with the specified id
ecs.destroyEntity(entityId)
```

```js
// removes all data associated with the specified id using a ComponentQuery
const playerQuery = ecs.query(Player)
playerQuery.destroyEntities()
```

- [ecs.destroyEntity](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#destroyentity)
- [ComponentQuery::destroyEntities](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#destroyentities)

---

### Clear all entities and their component data
```js
// clears all component data (preserves component registration)
ecs.clearComponents()
```
- [ecs.clearComponents](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#clearcomponents)

---

### Get the first entity value by component
```js
// get the first entity value containing a Player component
const player = ecs.firstValue(Player)

// optionally return related component values associated with the Player
const [player, position] = ecs.firstValue(Player, Position) ?? []
```

```js
// get the first entity value using a ComponentQuery
const playerQuery = ecs.query(Player, Position)
const [player, position] = playerQuery.firstValue() ?? []
```

---
- [ecs.firstValue](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#firstvalue)
- [ComponentQuery::firstValue](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#firstvalue)

### Get the first entity id by component
```js
// get the first entity id containing a Player component
const playerId = ecs.firstKey(Player)
---
// optionally return related component "values" associated with the Player
const [playerId, position] = ecs.firstKey(Player, Position) ?? []
```

```js
// get the first entity id with related component "values" using a ComponentQuery
const playerQuery = ecs.query(Player, Position)
const [playerId, position] = playerQuery.firstKey() ?? []
```

- [ecs.firstKey](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#firstkey)
- [ComponentQuery::firstKey](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#firstkey)

---

### Query entities by component

```js
// define a query
const enemyQuery = ecs.query(Enemy, Position, Velocity)

// iterate all the enemies and it's related components in the query
for (const [entityId, enemy, position, velocity] of enemyQuery) {
  position.x += velocity.x * enemy.speed
  position.y += velocity.y * enemy.speed
}

// iterate all the enemy entity ids in the query
for (const [entityId] of enemyQuery.keys()) { }

// iterate only related enemy components in the query
for (const [enemy, position, velocity] of enemyQuery.values()) {
  position.x += velocity.x * enemy.speed
  position.y += velocity.y * enemy.speed
}
```

- [ComponentQuery](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html)
- [query.entityCount](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#entitycount)
- [query.firstKey](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#firstkey)
- [query.firstValue](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#firstvalue)
- [query.firstEntry](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#firstentry)
- [query.keys](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#keys)
- [query.values](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#values)
- [query.entries](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#entries)

---

### Iterate entities by component

```js
// iterate all the enemy entities and update their related position data
const iterator = ecs.iterator(Enemy, Position, Velocity)
for (const [entityId, enemy, position, velocity] of iterator) {
  position.x += velocity.x * enemy.speed
  position.y += velocity.y * enemy.speed
}
// NOTE: subsequent calls to a cached iterator will require calling iterator.reset() to restart iteration from the first entry
iterator.reset()
```

- [ComponentQuery::[iterator]](https://ecsjs.gitlab.io/ecs/classes/ComponentQuery.html#iterator)
- [ecs.iterator](https://ecsjs.gitlab.io/ecs/classes/EntityMap.html#iterator)

---