4.イベント
イベントも振舞いと同じように型で表す。即ち、tという時間にeというイベントが発生したとき、
newtype Event t a = Event { getEvent :: (t,a) }
というデータ型で表す。
これは、振舞いを定義したBehaviorの型と比較するとよく似ていることが分かる。
newtype Behavior t a = Behavior { stepBehavior :: t -> a }
なお、getEventの型シグネチャは次のようになっている。
*Main> :t getEvent getEvent :: Event t a -> (t, a)
例えば、6時にアラームが鳴るようなイベントは次のように表すことができる。
alarmEvent = Event {getEvent = (6,"Alarm")}
また、12時に新幹線が通るようなイベントは次のように表すことができる。
trainEvent = Event {getEvent = (12,"Rapid train")}
4.1 キーボードからの入力
パソコンを使っている我々にとって、最も慣れ親しんでいるイベントの一つはキーボードからの入力である。パソコンでゲームを作成するときもキーボードは重要な役割を担っている。画面上のあるものを動かしたり、弾丸を発射させる時に用いる。ここでは、キーボードからの入力について説明する。今、四角い箱があったとし、キーボードからの入力でこの箱を左右に動かすこととする。
キーのAを押し下げると左に、また、Dを押し下げると右に動くこととする。そこで、キーに対して次のようにデータ型を定義する。
data Input = A | D | Quit deriving (Eq,Read,Show)
キーボードの入力をイベントとしてポーリングするために、次の関数を用意する。
pollEvents :: IO [Input] pollEvents = fmap treatLine getLine where treatLine = concatMap (map fst . reads) . words
ここで、concatMapは、リストの各要素に対して関数fを適応したものをconcatする。concatは複数のリストのリストを一つのリストにする。例えば次のようになる。
Prelude> concat [[1,2,3],[4,5]] [1,2,3,4,5] Prelude> concat ["I ", "would ", "like ", "to ", "invite ", " him ", "to ", "the ", "party", "."] "I would like to invite him to the party."
また、concatMapの例を示す。
Prelude> putStr $ concatMap (++"\n") ["I", "like", "Haskell"] I like Haskell
上記のプログラムは、最初に、各要素に(++"\n")を施すと["I\n", "like\n", "Haskell\n"]になる。これにconcatを適応すると、"I\nlike\nHaskell\n"となり、上記のような出力を得る。
それでは、pollEventsを実行する。適当にキーを入力すると次のような結果が得られる。
*Main> pollEvents A [A] *Main> pollEvents S [] *Main> pollEvents D [D] *Main> pollEvents AB [] *Main> pollEvents A D E R T [A,D] *Main> pollEvents A R D T A U [A,D,A] *Main> pollEvents A T H Quit ADE DD [A,Quit]
4.2 四角い箱を定義する
次の四角い箱を用意する。これも新しい型を導入して行う。
newtype Box = Box { _boxPosition :: V3 Double } deriving (Eq,Read,Show)
また、フィールド名_boxPositionを関数boxPositionにするために次の処理を行う。
makeLenses ''AppBox
なお、これらを使用するためには、TemplateHaskellを使えるようにするばかりでなく、以下のモジュールをインポートしておく必要がある。
{-# LANGUAGE TemplateHaskell #-} import Control.Lens import Linear.V3
さらにBoxの状態を次の型により定義する。
data AppSt = AppSt { _appBox :: Box} deriving (Eq,Read,Show)
また、フィールド名を関数にする。
makeLenses ''AppBox
4.3 箱をキーからのイベントにより動かす
そこで、キーのA,Dが押された時は、それぞれ左右に0.1だけ移動させる関数は、以下のようになる(ここではこんな感じになると思って欲しい。後で詳しい説明をする)。
updateAppSt :: AppSt -> Input -> Maybe AppSt updateAppSt appst input = case input of A -> Just $ appst & appBox . boxPosition . _x -~ 0.1 D -> Just $ appst & appBox . boxPosition . _x +~ 0.1 Quit -> Nothing
ここまでのプログラムをまとめると次のようになる。
{-# LANGUAGE TemplateHaskell #-} import Control.Lens import Linear.V3 data Input = A | D | Quit deriving (Eq,Read,Show) pollEvents :: IO [Input] pollEvents = fmap treatLine getLine where treatLine = concatMap (map fst . reads) . words newtype Box = Box { _boxPosition :: V3 Double } deriving (Eq, Read, Show) makeLenses ''Box data AppSt = AppSt { _appBox :: Box} deriving (Eq,Read,Show) makeLenses ''AppSt updateAppSt :: AppSt -> Input -> Maybe AppSt updateAppSt appst input = case input of A -> Just $ appst & appBox . boxPosition . _x -~ 0.1 D -> Just $ appst & appBox . boxPosition . _x +~ 0.1 Quit -> Nothing