bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

Netwireでゲームを作成する(3)

5.スイッチ

イベントの発生により、振舞いは変化する。例えば、ボールを床に落下させると、床にぶつかって、ボールは跳ね返ってくる。このような状況を記述するために、関数型リアクティブ・プログラミングではスイッチを用いる。

スイッチの型シグネチャは次のようになる。

switch :: Behavior t a -> Event t (Behavior t a) -> Behavior t a

ところで、キーボードからの入力inputを受けたときの新たな振舞いは次のように記すことができる。

box input = Behavior {stepBehavior = \ _ -> イベントを受けての振舞いを記述}

前々回の記事で、Behaviorの方は次のように定義していた。

newtype Behavior t a = Behavior {stepBehavior :: t -> a}

しかし、振舞いの定義ではtは(推移する)時間であったのに対し、イベントではこの部分はキーボードからの入力、即ち、inputとなっている。これはイベントの発生源ともいえるものなので、時間とは全く異なるものである。

そこで、相容れない二つの従来の振舞いの定義とイベントを一つにまとめるために、Behaviorの型を次のように再定義する。

newtype Behavior t a b = Behavior {stepBehavior :: t -> a -> b}

上記で、tは推移する時間、aはイベントの入力、bは振舞いとする。

ここまでをまとめると、

{-# LANGUAGE TemplateHaskell #-}

import Linear.V3
import Control.Lens

data Input
  = A | D | Quit deriving (Eq,Read,Show)

newtype Box = Box { _boxPosition :: V3 Float } deriving (Eq,Read,Show)
makeLenses ''Box

newtype Behavior t a b = Behavior { stepBehavior :: t -> a -> b }
newtype Event t a = Event { getEvent :: (t,a) }

それでは、四角い箱の移動をどのように記述したらよいであろうか。

直感的に思い付くのは次の候補である。

box :: Behavior t [Input] Box
box = Behavior (const treatEvents)
  where
    treatEvents events
      | A `elem` events = Box $ V3 (-0.1) 0 0
      | D `elem` events = Box $ V3 0.1 0 0
      | otherwise = Box $ V3 0 0 0

上記のプログラムは、Aのキーが押し下げられたとき箱の位置を(-0.1 0 0)に、Dのキーが押し下げられたとき箱の位置を(0.1 0 0)に、その他のキーの時は(0 0 0)にする。しかし、AあるいはDのキーが初めて押された時だけ、左あるいは右に移動するだけで、それ以降は意図したようにはいかない。

そこで、上記のプログラムを改良する必要がある。これについては、次の記事で紹介する。