Lua と相互運用可能な Lisp の方言のプログラミング言語でFennelというのがある。最近これを使って、ちょっとしたゲームを作ってみた。
個人でゲームを作ることを考えたときに、開発環境に何を求めるか…というのを考えてきた。 その中で、自分は以下のようなことを重要視したいと思った。
- イテレーションの速度
- コードを書いて次の瞬間にはもう反映されている、ゲーム状態をリセットせず継続できる、というのが理想
- asset as code
- ゲームにはプログラム以外にも画像、音声、3D モデル、アニメーション、などなど様々なアセットが必要となる
- アセットを特定のバイナリフォーマットやツールに依存することなく、プログラムと同等に扱えるようにしたい
- ゲームコードからアセットを生成しそのまま利用するようなことがしたい
- ようは JSON みたいな扱い方をしたいということ
- 完全理解
- すべての処理を完全に理解できる範囲で書きたい
- 気になった部分はすべてコードを読んで、手を入れられるようにしたい
- glmライクな簡潔な vector/matrix 計算がしたい
以上のことを(たぶん全部完璧にやるのは難しいので)いいバランスで実現しようと思ったとき、Lisp が向いているのではと思った。
- REPL があり、プログラムを動かしながら、書いたコードを反映できる
- S 式という柔軟なフォーマットがあり、マクロで DSL を作れる
- 言語仕様も処理系もシンプルで、実装を完全に把握できる
そこで Lisp でゲームを作る時に今どんな感じかというのを調べていたら、前述の Fennel という言語を見つけた。 これは Lisp で書いたコードを Lua にコンパイルして実行できるもので、Lua 関数を Fennel から呼んだり、Fennel 関数を Lua から呼んだりということがごく自然にできる。 Lua で 2D ゲームを作る時に使えるLÖVEというフレームワークがあり、Fennel を用いて LÖVE を操作することも問題なく行える。 さらに、このへんのことを調べていたときにちょうどAutumn Lisp Game Jam 2025というゲームジャムが開かれており、これ幸いと参加することにした…というのが冒頭のゲームを作った経緯です。ちょうどFennel+LÖVE でゲームを作るチュートリアル記事も公開されており、参考になった。
久々に Lisp を書いてみて、思ったよりめんどくないなと思った。 というのも、Fennel においてはいわゆる car/cdr みたいなリスト操作は不要で、Lua の table を操作する関数がそのまま利用できる。 またエディタ拡張としてParinferという仕組みがあり、これを使うとカッコを手入力する必要がだいぶ減り、インデントに応じてエディタ側が勝手にカッコを付け外ししてくれる。自分は Neovim のプラグインを利用した。
イテレーション速度においては、今回あまりエディタや環境の便利機能を追求する時間がとれず、特定のコマンドでソースを一括でリロードするだけであった。それでも操作した次の瞬間には最新のコードが反映されており、かなり快適だった。
というわけで、Lisp で個人ゲーム開発をするということに割と好感触を持った。 今回試せなかった DSL によるアセット生成や、計算ライブラリの利用、3D ゲーム開発への応用、エディタ機能の活用など、今後さらに深ぼっていきたい。