読者です 読者をやめる 読者になる 読者になる

Haskellでリアルタイムゲーム再戦しようとしてみたが

関数型言語でリアルタイムゲームを作るぜ!と意気込んで(id:ABA:20050604#p1)楽々敗北した私だが、そんじょそこらのOO言語でくすぶっているような俺達じゃない。ゲームさえ作れればどんな言語でもやってのける命知らず、と思ったけどもやっぱりHaskellだけはかんべんな。

今回はふつける(http://i.loveruby.net/ja/stdhaskell/)という強力なウェポンがあるのでなんとかやってやるぜ、とかいう楽観的な見通しをもってまず読み始め、結局やっぱりIOモナドはよく分からん、とかいう前回とあんまり変わらないステータスのままSDL+OpenGLのサンプルを書くというフェイズに入って、いい加減なサンプルをHSDL(http://fxp.hp.infoseek.co.jp/haskell/HSDL/)を使って書いた。

つうかまずMinGW上でHSDLを使ったバイナリを作る方法がよく分からん。HSDLのビルドが'multiple definition'とかで止まってしまったので、HSDLのコードを全部持ってきた上で

ghc -optl -mwindows --make -Lc:/usr/SDL-1.2.5/i386-mingw32msvc/lib -lSDLmain -lSDL main.hs

とかいう具合にビルドするという間違った解法に逃げた。wxHaskell (その3)(id:tanakh:20040711#p1)を丸写し。'-optl'って何だ?

あとはMonadius(http://www.geocities.jp/takascience/haskell/monadius_ja.html)のアーティクルを参考に適当にトークン管理などを構築。

class AbstractFrame f where
  updatefunc :: f -> f
  renderfunc :: f -> IO ()
instance AbstractFrame Frame where
  updatefunc = update
  renderfunc = render
data Frame = Frame (State, [Token])
data State = State {cnt :: Int} deriving Eq
data Token = RGBTriangle {x :: GLdouble, deg:: GLdouble}
update :: Frame -> Frame
update (Frame (state, tokens)) =
  Frame (nextState, nextTokens) where
    nextState = state { cnt = (cnt state) + 1 }
    nextTokens = map updateToken tokens
updateToken :: Token -> Token
updateToken triangle@RGBTriangle{} =
  triangle { deg = (deg triangle) + 1.0 }
render :: Frame -> IO ()
render (Frame (state, tokens)) = do
  clear [ColorBuffer]
  mapM_ renderToken tokens
  glSwapBuffers
renderToken :: Token -> IO ()
renderToken triangle@RGBTriangle{} =
  preservingMatrix $ do
    translate $ Vector3 (x triangle) 0.0 0.0
    rotate (deg triangle) $ Vector3 0.0 1.0 0.0
    renderPrimitive Triangles colorTriangle
    renderPrimitive LineLoop colorTriangle
     where
       colorTriangle = do
         currentColor $= Color4 1 0 0 0.5
         vertex$Vertex3 (-0.5) (-0.5) (0::GLfloat)
         currentColor $= Color4 0 1 0 0.5
         vertex$Vertex3 (0.5) (-0.5) (0::GLfloat)
         currentColor $= Color4 0 0 1 0.5
         vertex$Vertex3 (-0.5) (0.5) (0::GLfloat)

三角形2つが回るだけ。これ以外にもタイマ管理とかOpenGLの初期化とか初期フレームの作成とかいろいろあるんだけど、あんまりべたべた張ってもしょうがないのでやめとく。

まだトークンの追加、削除やトークン間の干渉、外部からのキー入力(ESCで抜けるのだけは実装したけど)などのややこしい部分をぜんぜんやってないのにこのめんどくささですか……気力が尽きそうですよ。Haskellのうまみを全然使えてない感じがするし。敵の移動を関数合成で書けるようにするとかいうフェイズまでいけば、Haskellの良さも分かるようになりますかね……

現状のHaskellの印象は、Prologライクな宣言的表記とリスト処理と単一化もどき、やけに豪華なUnixパイプ、それらに構文糖をどさどさとかけたもの、っていう感じ。あと代数的データ型周りの構文の非直感的っぷりが激しすぎる。Maybe以外のモナドは良く分からん。OpenGLバインディングのglEnable周りがCapabilityを使ったりMaybeモナドを使ったりでばらばらなので分かりにくいし、リファレンスが異常に引きにくい。エラーメッセージが分かりにくい。

まあ触っていて面白いことは面白いので、もうちょっとがんばってみますか。