1.概略
ゲームを構成するプログラムの説明がほぼ終了したので、それらをまとめてゲームのプログラムとして機能させるメインプログラムを見ていく。Yampaではreactimateという関数を用意している。これを働かせるためには、初期化ルーチン、入力ルーチン、出力ルーチン、処理ルーチンを指定すればよい。初期化ルーチンでは画面の初期化を行う。入力ルーチンでは入力デバイスからの入力と一サイクルの時間を指定する。出力ルーチンは画面への出力である。処理ルーチンは一つのサイクルの中での処理方法を示すものである。
2.プログラムの作成
今までに説明したプログラムを利用して、メインプログラムを作成すると次のようになる。
main::IO() main = reactimate init' input output (process objs) where stone1Obj = stoneObject (-8, 0) (1, 15) stone2Obj = stoneObject (-8, 0) (4, 17) birdObj = birdObject (-8, 10) (4, 0) objs = listToIL [stone1Obj, stone2Obj, birdObj] init' :: IO () init' = initGL input :: Bool -> IO (DTime, Maybe Input) input _ = do threadDelay 100000 return (0.1, Nothing) output :: Bool -> IL ObjOutput -> IO Bool output _ oos = do draw oos return False
上記のプログラムで、processの引数として、登場者の識別リストobjsを渡している。石二つと鳥一羽をstone1Obj,stone2Obj,birdObjとして定義した後で、そのリストをlistToILによって識別リストに変換している。
また、inputのところでは、一サイクルの時間をreturnのところにある0.1で与えている。従って、これは0.1秒間隔で一つのサイクルが進行する。即ち、0.1秒間隔でサンプリングされていることと同じである。例えば、落下するものを観察しているとすると、0.1秒ごとの間隔でその位置をとらえていることになる。
一方で、threadDelay 100000は、スレッドを待機させるときに使う。ここでは、100000ナノ秒、即ち0.1秒、待たせている。スレッドを待機させることにより、ビデオの早送りやゆっくり送ることが実現できる。このプログラムでは観測している時間と待機時間が同じなので、等速でみることになるが、10倍速い速度で見たければ、 100000を 10000に変えてあげればよい。
2.プログラムを修正
上記のプログラムで描画してくれれば、すっきりしていて分かりやすいのだが、描画間でのコントロールがうまく働かないようで、うまく描画してくれない。そこで、「物理的なモデルに基づいてゲームを開発する:将来の発展のために」で紹介したようにサイクル終了時の処理を詳しく記述することにする。これが以下のプログラムである。なお、このプログラムは、module Mainとなっている。いつもは最後に全体のプログラムを示していたが、今回はこのプログラムが全体のプログラムである。
module Main where import FRP.Yampa import FRP.Yampa.Utilities import Control.Concurrent import Data.IORef import Graphics.UI.GLUT hiding (Level,Vector3(..),normalize) import Graphics import Process import IdentityList import Objects import Types mainSF = (process objs) >>^ (\ oos -> draw oos) where stone1Obj = stoneObject (-8, 0) (1, 15) stone2Obj = stoneObject (-8, 0) (4, 17) birdObj = birdObject (-8, 10) (4, 0) objs = listToIL [stone1Obj, stone2Obj, birdObj] main :: IO () main = do oldTime <- newIORef (0 :: Int) rh <- reactInit (initGL) (\_ _ b -> b >> return False) mainSF displayCallback $= return () idleCallback $= Just (idle oldTime rh) oldTime' <- get elapsedTime writeIORef oldTime oldTime' mainLoop idle :: IORef Int -> ReactHandle () (IO ()) -> IO () idle oldTime rh = do newTime' <- get elapsedTime oldTime' <- get oldTime let dt = (fromIntegral $ newTime' - oldTime')/1000 react rh (dt, Nothing) writeIORef oldTime newTime' return ()
上記のプログラムで、先ほどのプログラムでのoutputとprocessの部分はmainSFとなっている。また、init'はmainの中のreactInit (initGL)である。またスレッドの待機時間とサイクルの時間は下から4行目から3行目の部分である。