Pyxen Script

Pyxenのゲームは、ゲーム向けに作られたPythonの方言であるPyxen Scriptで書きます。インデント、def、リスト、辞書、f文字列、内包表記と、Pythonの文法と書き心地をそのまま備えています。違うのはその下の部分です。コードはプロジェクト全体としてチェックされ、ゲームが始まる前にコンパイルされます。そして、ゲームをどのデバイスでも同じように動かすという、ひとつの仕事のために作られたランタイムで実行されます。

Pythonをすでに知っているなら、書いたコードのほとんどは期待どおりに動きます。このページでは、Pyxen Scriptがなぜあるのか、Pythonと何が共通なのか、そして意図的に何を省いているのかを説明します。

なぜ方言なのか

ゲームが言語に求めるものは、スクリプトが求めるものとは違います。

以前のPyxenはMicroPythonインタプリタを使っていました。Pyxen Scriptはそれに代わるものです。

Pythonと同じところ

Pyxen Scriptは、何百もの1行のPythonイディオムでテストされ、それぞれ標準のPythonの出力と比較されています。以下はすべてPythonと同じように動きます。

違うところ

クラスはない

Pyxen Scriptにはclassがありません。これは設計上の選択です。

Pyxenのゲームでは、状態はエンティティとそのコンポーネントに置かれます。これらはエンジンが所有し、コンパクトに格納し、フレームインスペクタで見せてくれます。クラスは同じ状態を置く二つ目の場所になり、エンジンからもインスペクタからも見えません。そのため、Pythonならクラスを定義するところで、Pyxenのコードはエンティティを生成し、コンポーネントを付け、それを操作する関数を書きます。

よくあるPythonのクラス:

class Enemy:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.hp = 3

    def hit(self):
        self.hp -= 1

同じことをPyxen Scriptで:

def spawn_enemy(x, y):
    e = world.spawn(name="enemy", x=x, y=y, sprite=Sprite("slime"))
    e.enemy = {"hp": 3, "speed": 1.5}
    return e

def hit(e):
    e.enemy.hp -= 1
    if e.enemy.hp <= 0:
        world.destroy(e)

def update():
    for e in world.all("enemy"):
        e.x -= e.enemy.speed

エンティティはすでに位置、スプライト、シーン内の居場所を持っています。enemyコンポーネントには敵に固有のものを入れ、world.all("enemy")ですべての敵が見つかるので、同期を保つべきインスタンスのリストはありません。ハイスコア表やレベルの記述のように、ゲームオブジェクトではない単純なデータには、辞書とリストを使います。モデル全体についてはエンティティ&コンポーネントを参照してください。

例外はない

Pyxen Scriptにはtry、except、finally、raiseがありません。これも設計上の選択です。

Pyxen Scriptのエラーはゲームを止め、そこに至った呼び出しとともにその行で報告されます。途中で捕まえるものがないので、エラーがexcept: passに飲み込まれて、3つ先のレベルで黙っておかしな動きをするゲームになることはありません。

ゲームのコードにあるtryブロックのほとんどは、直接答えられる問いを投げています。その問いを直接書きましょう。

# このエンティティはコンポーネントを持っているか?
hp = e.health.value if e.has("health") else 0

# このキーは辞書にあるか?
best = scores.get(name, 0)

# このテキストは数字か?
if text.isdigit():
    level = int(text)

# このインデックスはリストの範囲内か?
if i < len(items):
    item = items[i]

assertは使えます。条件が偽のとき、その行でゲームを止めます。

ほかにPyxen Scriptにないもの

これらはすべて、ゲームが始まる前に、名前とその行で報告されます。

モジュールとimport

プロジェクトはひとつのプログラムとしてコンパイルされます。その中のスクリプトは、ファイル名ですでにお互いから見えています。monsters.pyにある関数spawn()は、ほかのどのスクリプトからもimportなしでmonsters.spawn()として呼べます。エンジンのAPI(world、input、Spriteなど)も常に使えます。

from pyxen import *やimport monstersのような、すでに書いてあるimportはそのまま動きます。Pyxen Scriptはそれらを不要なものとして扱うので、削除してもかまいません。