汎用ビデオゲーム記述言語VGDLとその処理系PyVGDL

2Dビデオゲームのメカクニスを記述するための言語VGDL (Video Game Description Language)というものを研究している人達がいる。

ゲーム内AIなどを研究するために必要な言語仕様の提案として始まったようだ。それを実際にパースして実行する処理系PyVGDLも作られている。

VGDLはパックマンとかフロッガーとかバルダーダッシュとかの古典的アクションゲームが簡単にかけるという触れ込みである。例えばフロッガーをVGDLで書いたコードは以下だ。

まずレベルの定義がある。

wwwwwwwwwwwwwwwwwwwwwwwwwwww
w           wGw            w
w00==000000===0000=====000=2
w0000====0000000000====00012
w00===000===000====0000===02
www   ww   www    www  wwwww
w   ----   ---   -  ----   w
w-     xxx       xxx    xx w
w -   ---     -   ---- --  w
w       A                  w
wwwwwwwwwwwwwwwwwwwwwwwwwwww

なんとなく見覚えのある画面がテキストで書いてある。次はゲーム内キャラの定義。

    SpriteSet
        forest > SpawnPoint stype=log prob=0.4  cooldown=10
        structure > Immovable
            water > color=BLUE
            goal  > color=GREEN
        log    > Missile   orientation=LEFT  speed=0.1 color=BROWN
        safety > Resource  limit=2 color=BROWN
        truck  > Missile   orientation=RIGHT 
            fasttruck  > speed=0.2  color=ORANGE
            slowtruck  > speed=0.1  color=RED
        wall > Immovable color=BLACK 

林とか水とかゴールとか丸太とか、フロッガー内のキャラクタ(スプライト)が定義されている。後で説明するが、SpawnPointとか、Missileとか、この辺の記述がキャラクタの動作を決めている。

その次はキャラ同士の衝突時に何が起こるかの定義。

    InteractionSet
        goal avatar  > killSprite
        avatar log   > changeResource resource=safety value=2
        avatar log   > pullWithIt
        avatar wall  > stepBack
        avatar water > killIfHasLess  resource=safety limit=0
        avatar water > changeResource resource=safety value=-1
        avatar truck > killSprite
        log    EOS   > killSprite
        truck  EOS   > wrapAround

avatarってのは自機キャラのことですなわちカエルだ。ゴールにカエルが突っ込んだらゴールを消す(killSprite)とか、壁に突っ込んだら戻る(stepBack)などの動作が書いてある。

次はゲームの終了条件。

    TerminationSet
        SpriteCounter stype=goal   limit=0 win=True
        SpriteCounter stype=avatar limit=0 win=False

画面上からゴールが無くなったら勝ち、カエルが無くなったら負け、ということだ。

最後は最初に書いたレベルがどのキャラに相当するか。

    LevelMapping
        G > goal
        0 > water
        1 > forest water
        2 > forest wall log
        - > slowtruck
        x > fasttruck
        = > log water

以上。非常にシンプルに書けている。

ただ本当にこれで汎用かと言うとちょっと何なところもある感じがする。SpriteSetとInteractionSetの部分がメカニクスというかゲームルール記述の肝なんだけど、ここにあるMissileとかstepBackとかの動作の定義はあらかじめ決められたセットから選ばれているようだ。

このコード内にある動作がその全てで、これの作り込みによって定義できるゲームのバリエーションは規定されているように思える。で、これら動作も汎用的かというと、どうかなあ、killIfSlowとか、これルナランダー専用動作ではないのかという気がしないでも。まあここの動作が十分な数揃っていればいいのかもしれないが。

最初に思ったのは、これPuzzleScriptに似ているなということ。

PuzzleScriptはその名の通りパズルを作るための専用言語。例えば倉庫番は以下で書ける。

LEVELS、RULES、WINCONDITIONS、LEGENDの順に見ればVGDLのサンプルに非常に似ている感じがする。PuzzleScriptのルール定義は本当にシンプルで、状態のパターンマッチと、その後の状態を記述するだけ。プレイヤーが荷物に向かって歩いたら、荷物を押す、は

[ > Player | Crate ] -> [ > Player | > Crate ]

こうだ。VISCUITのメガネにも少し似ている。この1行書くだけで、上下左右どこから押されても同じように動作するみたいなことはPuzzleScriptの処理系がよろしくやってくれるので、うまく使えば少ない記述量で複雑な動作も書ける。

PuzzleScriptはキャラクタの形や効果音などもちゃんと記述することができて、これだけでちゃんと体裁が整ったゲームを作ることができる。VGDLはあくまでゲームルールを記述するだけなのでこれだけではゲームとして色々足りない部分がある。

この2つどっちが先にできたのかと言うと、PyVGDLのinitial commitが2012年6月でPuzzleScriptのinitial commitが2013年9月だからPuzzleScriptのほうが後発だ。それらの間に関係があるかというと、よく分からない。よく分からないが、比較記事があった。

PuzzleScriptはターンベースのパズルゲーム記述にかなり特化、対してVGDLはもっと広くアクションゲームを書こうとしている点で異なるけど、ゲームの形式化という点でアプローチは似ているよね、という内容。アクションやパズル系ゲームの簡易記述を考えると、この2つのようなフォーマットに落ちるのが一つの妥当なアプローチなんだろう。

ゲームのルールのエッセンスさえ記述できれば良いという割り切りのもと、どれだけ簡易なゲーム記述言語を作ることができるか、というのを考えてみるのも楽しそうだ。VGDLの発展形や、それとは全く別な記述方式、いろんなアプローチが思いつけるのが理想だが、簡易だけど汎用的というバランスの良い言語を考えるのはたぶんそう簡単では無いね。