52700.fb2
data Sense = Sense
{ senseHero
:: HeroBall
, senseBalls
:: [Ball]
}
Через Query чистые данные могут рассказать грязным о том, что необходимо удалить шар из игры, об-
новить скорость шара игрока или создать новый шар (Freq отвечает за параметры создания шара). Грязные
данные могут рассказать чистым на языке Event и Sense о том, что один из шаров коснулся до шара иг-
рока, или игрок кликнул мышкой в определённой точке. Также мы сообщаем все обновлённые положения
параметры шаров в типе Sense. Тип Event отвечает за события, которые происходят иногда, а тип Sense за
те параметры, которые мы наблюдаем непрерывно (это типы глазарук), Query – это язык действий (это тип
руконог). Нам понадобится ещё один маленький язык, на котором мы будем объясняться с OpenGL.
data Picture = Prim Color Primitive
| Join Picture Picture
data Primitive = Line Point Point | Circle Point Radius
data Point
= Point Double Double
type Radius = Double
data Color = Color Double Double Double
Эти три языка станут барьером, которым мы ограничим влияние IO. У нас будут функции:
percept
:: Dirty -> IO (Sense, [Event])
updatePure
:: Sense -> [Event] -> Pure -> (Pure, [Query])
react
:: [Query] -> Dirty -> IO Dirty
updateDirty :: Dirty -> IO Dirty
picture
:: Pure -> Picture
draw
:: Picture -> IO ()
Вся логика игры будет происходить в чистой функции updatePure, обновлять модель мира мы будем в
updateDirty. Давайте опять начнём проектироваание сверху-вниз. С этими функциями мы уже можем напи-
сать основную функцию цикла игры:
loop :: IORef World -> IO ()
loop worldRef = do
world <- get worldRef
300 | Глава 20: Императивное программирование
drawWorld world
(world, dt) <- updateWorld world
worldRef $= world
G. addTimerCallback (max 0 $ frameTime - dt) $ loop worldRef
updateWorld :: World -> IO (World, Time)
updateWorld world = do
t0 <- get G. elapsedTime
(sense, events) <- percept dirty
let (pure’, queries) = updatePure sense events pure
dirty’ <- updateDirty =<< react queries dirty