NimでECS(Entity Component System)

# まずECSって何？

###Wikiを読んだりググったりしてね
参考: [エンティティ・コンポーネント・システム(Wikipedia)](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%83%B3%E3%83%86%E3%82%A3%E3%83%86%E3%82%A3%E3%83%BB%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%83%BB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0)

### 3行で
オブジェクトをクラスで表すんじゃなくて、
Entityって呼ばれるIDにComponentって呼んでる任意のデータを紐付けて
Systemって呼ばれる関数でConponent毎に処理する。

### 何が嬉しいの？
- IDにコンポーネントを紐付けてオブジェクトを表すので簡単にいろんなオブジェクトを作れる。
- コンポーネント毎に処理するので、CPUキャッシュが有効に使えて処理が早くなる（実装による）
- ゲームみたいな多様なデータをもつオブジェクトを表現する場合、静的なクラスを設計しなくていいので楽

# 実際どんな感じ？
まずはエンティティやコンポートネントの置き場所となるWorldオブジェクトを作成する。

```nim:
let world = World()
```

次にEntityを作成する。

```nim
let entity = world.newEntity
```

Entityにコンポーネントを紐づける

```nim
# 位置情報(Position)と名前(Name)を紐づける
entity.with(Position(x: 1, y: 2)).with(Name("Bob"))
```

Entityからコンポーネントを取得する場合、get関数に型を指定する。

```nim
entity.get(Position) == Position(x: 1, y: 2) # True
```

Entityから一度に複数のコンポーネントを取得できるようにする。

```nim
let (pos, name) = entity.getAll(Position, Name)
```

Entityが指定コンポーネントを持っているかチェックする。

```nim
entity.hasAll(Position, Name) # True
```

Systemを実装するために指定の全てのコンポーネントを処理する。

```nim
# 値のみ取得
for pos in world.compornentsOf(Position):
  # pになんかする

# Entityも取得する
for entity, pos in world.compornentsOf(Position):
  # p, eになんかする
```

使い終わったEntityを削除する。

```nim
entity.delete
```

# データ構造
こんな感じになっております。

```nim
type
  EntityId = uint64

  Entity* = ref object
    id: EntityId
    world: World

  AbstructComponent = ref object of RootObj
    index: Table[Entity, int]
    freeIndex: seq[int]

  Component*[T] = ref object of AbstructComponent
    components: seq[T]

  World* = ref object
    lastEntityId: EntityId
    components: Table[string, AbstructComponent]
```

## Entity
EntityId自体はただのuint64。
newEntityされるたびにインクリメントしていく。
余程のことがないとオーバーフローはしないと思うので、
使い回しはしない感じでいく。
ただ、Entityを起点として色々操作したいので、
自分が属するWorldの参照を持たせとく。

## Component
これはちょっとヒネってる。
管理情報だけをもつAbstructComponentを継承して、
実際のデータリストをもつCompornentをジェネリックで作る。
AbstructComponentのindexはEntityと
紐づいているデータのComponent配列のインデックス情報のテーブル。
freeIndexはComponent配列の空いたところを使い回すようの空きインデックス管理。
単純にEntityとComponentのテーブルにすると、
System関数で回すときにCPUキャッシュの恩恵を受けられそうにないので、
Componentを配列で保持するためにこんな感じになっております。

## World
EntityIDの管理とコンポーネントのテーブルを持ってる。
コンポーネントは文字列とAbstructComponentのテーブル。
キーの文字列は型名。使う時はAbstructComponentをキャストして使う。

# どうやってんの？

## Entity作成
これは単純

```nim
proc newEntity*(self: World): Entity =
  self.lastEntityId += 1
  Entity(id: self.lastEntityId, world: self)
```

## Component紐付け
これは面倒
with関数自体はジェネリック関数

```nim
proc with*[T](self: Entity, component: T): Entity {.inline, discardable.} =
  self.world.assign(self, component)
  self
```
Worldのassign関数に丸投げ。
assignは対象のコンポーネントをすでに持ってれば作成。
そのあと、キャストされたComponentを取得して引数のentityとcomponentを追加する。

```nim
proc assign*[T](self: World, entity: Entity, component: T) =
  if self.has(T) == false:
    self.newComponent(T)
  self.componentsOf(T).assign(entity, component)
```

そのコンポーネントを持っているかどうかは以下の関数でチェック


```nim
proc has*(self: World, T: typedesc): bool {.inline.} =
  self.components.hasKey(T.type.name)
```
型自体をtypedescで受け取り、type.nameで型名を文字列に変換し、
componentsテーブルにあるかhasKeyでチェックする。
型名から文字列を取得するname関数はtypetraitsをimportする必要がある。

newComponentは単に指定の型のComponentをテーブルに追加するだけ。

```nim
proc newComponent*(self: World, T: typedesc) {.inline.} =
  self.components[T.type.name] = Component[T]()
```

componentsOfはcomponentsテーブルから指定された型の文字列をキーに
AbstructComponentを持ってきてそれを指定された型にcastして返す。

```nim
proc componentsOf*(self: World, T: typedesc): Component[T] {.inline.} =
  cast[Component[T]](self.components[T.type.name])
```
Componentのassignはこんな感じ。

```nim
proc assign*[T](self: Component[T], entity: Entity, component: T) =
  if self.index.hasKey(entity):
    self.components[self.index[entity]] = component
    return
  if self.freeIndex.len > 0:
    let index = self.freeIndex.pop
    self.index[entity] = index
    self.components[index] = component
    return
  self.index[entity] = self.components.len
  self.components.add(component)
```
既に割り当てられてたら置換え。
空きindexがあればそこに格納。
そうでなければ配列を拡張して追加。

## Componentの取得
Entity経由での取得はWorldに丸投げ。
型はtypedescで指定する。

```nim
proc get*(self: Entity, T: typedesc): T {.inline.} =
  self.world.get(self, T)
```

WorldからComponentリストを取得して、
Componentから指定したentityのコンポーネントを取得する。

```nim
proc get*(self: World, entity: Entity, T: typedesc): T {.inline.} =
  self.componentsOf(T).get(entity)
```

Componentから指定されたEntityのデータ取得
単にentityをキーにindexテーブルからインデックスを取得して、
components配列からデータを取り出してくるだけ。

```nim
proc get*[T](self: Component[T], entity: Entity): T {.inline.} =
  return self.components[self.index[entity]]
```

ちなみにEntityはオブジェクトなので、ハッシュのキーにする場合は
hash関数の定義が必要。

```nim
import hashes
proc hash*(self: Entity): Hash {.inline.} =
  self.id.hash
```

複数取得はマクロを使って実装。
やってることはgetを並べてるだけ。

```nim
macro getAll*(self: Entity, types: varargs[typed]): untyped =
  var body = ""
  for t in types:
    if len(body) > 0:
      body.add(", ")
    body.add(fmt"{repr(self)}.get({repr(t)})")
  parseStmt(fmt"({body})")
```

##Componentを持っているかチェック

worldが持っててかつEntityが持ってれば真を返す感じ。

```nim
proc has*(self: Entity, T: typedesc): bool {.inline.} =
  self.world.has(T) and self.world.componentsOf(T).has(self)
```

Componentのチェックは単純にhasKeyで。

```nim
proc has(self: AbstructComponent, entity: Entity): bool {.inline.} =
  self.index.hasKey(entity)
```

複数の型を一気にチェックできた方が便利なので
これもマクロで実装。hasを並べてるだけ。

```nim
macro hasAll*(self: Entity, types: varargs[typed]): untyped =
  var body = ""
  for t in types:
    if len(body) > 0:
      body.add(" and ")
    body.add(fmt"{repr(self)}.has({repr(t)})")
  parseStmt(body)
```

##Component全処理

Componentにitmesとpairsイテレーターを実装すれば
Component、Entity、Componentのペアを
forループでぶん回せる。

```nim
iterator items*[T](self: Component[T]): T =
  for i in self.index.values:
    yield self.components[i]

iterator pairs*[T](self: Component[T]): tuple[key: Entity, val: T] =
  for e, i in self.index.pairs:
    yield (e, self.components[i])
```

## Entityの削除

worldから対象のEntityを削除して
念のためidに無効なidを設定する。

```nim
let InvalidEntityId: EntityId = 0

proc delete*(self: Entity) =
  self.world.deleteEntity(self)
  self.id = InvalidEntityId
```

Entityが有効かチェックする関数も定義しとくと良いかもしんない。

```nim
proc isValid*(self: Entity): bool {.inline.} =
  self.id != InvalidEntityId
```

worldからの削除は全Componentから対象Entityのデータを削除する。

```nim
proc deleteEntity(self: World, entity: Entity) =
  for c in self.components.values:
    c.remove(entity)
```

Componentからの削除はEntityが持っていれば、
データ自体の削除処理はしないで対象EntityのインデックスをfreeIndexに移動するだけ。

```nim
proc remove(self: AbstructComponent, entity: Entity) =
  if self.has(entity):
    self.freeIndex.add(self.index[entity])
    self.index.del(entity)
```

#最後に

ECSを知って興味が出てNimで実装してみただけなので、
実際使ったら色々問題あるかもだし、ベンチマークとかも取ってないので遅いかもだけど、
あくまでご参考までに。

以上〜。