エンティティ&コンポーネント

PyxenはEntity-Component System(ECS)を採用しています。PlayerがCharacterを継承し、CharacterがGameObjectを継承するようなクラス階層を持つゲームエンジンを使ったことがある方には、これは異なるアプローチです。Pyxenには継承がありません。すべてはコンポジション(構成)です。

エンティティ

エンティティはゲームオブジェクトです。プレイヤー、壁、弾丸、パーティクル — 何でもなれます。内部的には、エンティティは単なるIDです。エンティティ自体はデータを持ちません。代わりに、コンポーネントをアタッチします。

player = world.spawn(name="player", x=100, y=50)

これは位置(100, 50)に名前付きのエンティティを作成します。エンティティはワールドに存在し、スプライトを持っていれば描画されます。

コンポーネント

コンポーネントはエンティティにアタッチされるデータの断片です。Pyxenには2種類あります:

組み込みコンポーネント

エンジンが提供するコンポーネント:

コンポーネント用途
Transform位置(xy)、回転、スケール
Sprite見た目 — 画像、タイル、ピボット、カラー
Cameraズーム付きビューポート
GridBodyグリッド上の衝突ボディ
GridMap行、列、タイルデータを持つタイルマップ
Sound効果音の再生
Music音楽ストリームの再生
Name検索用の文字列識別子
Layer描画レイヤー(0〜255)
Parent / Childrenシーン階層

組み込みコンポーネントはエンティティのプロパティまたは生成時に設定します:

e = world.spawn(
    x=50, y=80,
    sprite=Sprite("hero"),
    name="player"
)

# later
e.x = 100
e.sprite = Sprite("hero", tile=(16, 0, 16, 16))

カスタムコンポーネント

任意のエンティティに独自のコンポーネントをアタッチできます。カスタムコンポーネントは型付きフィールドを持つ辞書です:

player.health = {"value": 100, "max": 100}
player.speed = {"value": 2.5}

アタッチ後は、フィールドに直接アクセスできます:

player.health.value -= 10

マーカーコンポーネント — データを持たずタグとして機能するコンポーネントも使えます:

player.alive = True
enemy.boss = True

これはクエリに便利です:特定のマーカーを持つすべてのエンティティを検索できます。

フィールドの型

コンポーネントのフィールドに使える型:

備考
int4232ビット整数
float3.1432ビット浮動小数点数
boolTrueブーリアン
str"hello"最大31文字
Entityplayer別のエンティティへの参照

これらの制約は、コンポーネントがC++エンジン内でコンパクトでキャッシュに優しいレイアウトで格納されるために存在します。これがクエリを高速にしている仕組みです。

エンティティのクエリ

ワールドを通じて、コンポーネントでエンティティを検索できます:

# "health"コンポーネントを持つすべてのエンティティ
for e in world.all("health"):
    if e.health.value <= 0:
        world.destroy(e)

# 組み合わせ:"enemy"を持ち、"dead"を持たない
for e in world.all("enemy", without=("dead",)):
    e.x += e.speed.value

"sprite""body""camera""sound""music""map"などの組み込みコンポーネントでもクエリできます:

# スプライトを持つすべてのエンティティ
for e in world.all("sprite"):
    e.color = (1, 1, 1, 1)

# スプライトと"enemy"カスタムコンポーネントの両方を持つエンティティ
for e in world.all("sprite", "enemy"):
    e.color = (1, 0, 0, 1)

クエリはPyxenでゲームロジックを書く主な方法です。各オブジェクトが自身を更新する代わりに、特定のコンポーネントを持つエンティティを反復処理するシステムを書きます。

完全なAPIはこちら:world.all()Entity

なぜECSなのか?

オブジェクト指向のゲームコードに慣れている場合、ECSは最初は違和感があるかもしれません。メリットは以下の通り: