Common Lispで3Dダンジョンゲームを作成
以前「xclhb」というCommon Lisp用のX Window Systemのライブラリを作ったので、せっかくなのでそれを利用して何か作ろうと思い作成しました。
ジャンルはウィザードリィ風の3Dダンジョンゲームです。特に名前はありません。
Common LispとX Window Systemを使える環境なら↑のページの手順で動かせるはずです。
LinuxでもXではなくWaylandを使っているのであれば、そのままでは動きません。
WindowsでもVcXsrvなどを入れれば動かすことができます。
方針
手っ取り早く作るために以下の方針としました。
- Xのコアプロトコルのみを使用
- アニメーションはなし
- 音はなし
- 戦闘は1対1
プログラムの構成
主なモード(ユーザーのキー入力をどう扱うかが変わるだけ)として以下の3つを作り、戦闘やダンジョン内でのアイテムの使用などはこれらを組み合わせて作っています。
- ダンジョンの移動
- テキストの表示
- 選択肢の表示と選択
ダンジョンの描画
ダンジョンはプレーヤー視点のワイヤーフレームで描画しています。
上の画像はマップの(x, y) = (2, 1)から南を向いているときのものです。
ダンジョンは5×5マスで、各マス(cell)は四方がそれぞれ通行可能か壁か扉かを持つようにしています。
(defstruct cell
(sides (vector nil nil nil nil)) ; nil :wall :door :locked-door
ceiling ; nil :stairs
floor
(visible-p t)
on-moved)
こうしているのは一方から見たら何もなく通行可能、反対方向から見たら壁という一方通行を可能にするためです。
プレイヤーの位置を基準にプレイヤーの向いている方向の奥Nマス先から手前に向かって壁を描画していけば上の画像のようになります。
プレーヤー視点で立体的にダンジョンを描画するには手前の壁は大きく、奥の壁は小さく描いて上げる必要があります。
透視投影変換
3Dグラフィックスを描画する方法を調べると座標系だとか変換行列だとかが出てくると思います。
色々と変換がある内で立体感を出すための変換が「透視投影変換」です。
やることは各頂点の「X座標、Y座標をZ座標で割る」ということだけです。ただ(X,Y)座標を求めたいだけであればこれで十分です。
透視投影変換の後にスクリーンのサイズや画角に合わせて各頂点を拡大し、スクリーンの中央にちょうど(0, 0)が来るように各頂点を平行移動させてあげれば、それらしく立体的な物体を描画するための座標が得られます。
雑感
Lispなのでとりあえずウィンドウを表示しておいて、色々と描画を試してみるというのがやりやすかったです。セーブデータの読み書きもS式をread, writeしてあげるだけでいいので楽ですね。
一番難しかったのは戦闘です。1対1でコマンドも攻撃と防御だけでしたがなかなか苦労しました。今度は多対多でコマンドもパラメーターも増やしたものを作ってみたいと思います。
Discussion