wvogel日記

自分用の技術備忘録が多めです.

wxHaskellでゲームを作る

ここ最近ずっと、wxHaskellでゲームを作っています。
ノベルゲームの作成で、マウスのクリック位置や画像描画の大体がわかったので、今回は直接描画などを目標として、
簡単なシューティングゲームを作ることにしました。

まだ製作中なので完全ではありませんが、あとは関数で処理を記述していくだけなので、思考整理の意味でもまとめておきます。

実行中の画面は次のような感じ

まじめに作ろうと思って、オープニング画面、ゲームオーバー画面も作成しました。
また、Parsecを使ってファイルからの読み出しにも対応しています。

描画は、Graphics.UI.WX.Drawにある関数群で記述することができます。
直線描画を例にとると、

line :: DC a -> Point -> Point -> [Prop (DC a)] -> IO ()

となります。この、[Prop (DC a)]が以前からよくわからず、わからないままに放置していたのですが、
調べてみると、このリストに線種や色などの項目を入れることでより細かい設定が可能となります。

また、前回ノベルゲームエンジンを作成した際は、テキスト描画の方法が分からず、
label関数を使ってテキストを描画していましたが、ちゃんと関数が用意されていました。

drawText :: DC a -> String -> Point -> [Prop (DC a)] -> IO ()

これも、図形描画とどうように、[Prop (DC a)]にフォントサイズなどを設定可能です。

最後にマウスイベントは、
イベントを設定する際に

mouse :: Reactive w => Event w (EventMouse -> IO ())

を使用します。
EventMouseにはMouseLeftDragなどがあり、

MouseLeftDrag :: Point -> Modifiers -> EventMouse

となっています。
これをcase文やパターンマッチで取り出すことで処理を行えます。

そして、座標はmousePos関数を使えば取り出すことができます。


現状、このゲームの完成に必要なのはDragイベントの処理だけです。
が、ちょっと面倒....


あと、しばらく走らせていると、段々処理が重くなってきているのはなぜでしょう....
確か前ノベルゲームを作った時にも同じ現象が起きた気がします。

で、ここまでノベルゲームとこのゲームと、二種類のゲームを通して感じたこと。

コールバック故にStateモナドなどでの継続が出来ないため、IORefを使って状態の更新を行いますが、更新するのはタイマーコールバック関数の中のみなので管理は容易です。
あと、IOモナドが描画・更新時に必ず必要になるので副作用まみれになるのはしょうがないですが、
描画時には描画IOとしてではなく、描画対象自体を表していると考えることも出来、その場合、描画対象の構造を書き下し、
(例えば人であれば頭・腕・胴・足など)
その書き下したものをIOに写すような構造を作れば、
見通しはかなりよくなると思います。

Freeモナドとか使えるんじゃないかなあと、今この記事を書きながら思いつきました。