Стандартная функция when из модуля Control.Monad
выполняет действие только в том случае, если пер-вый аргумент равен True
. Для обновления положения и направления скорости шарика нам придётся вос-пользоваться глобальной переменной типа IORef Ball
:data Ball = Ball
{ ballPos :: Vec2d
, ballVel :: Vec2d
}
Код программы:
module Main where
import Control.Applicative
import Data.IORef
import Graphics.UI.GLFW
import Graphics.Rendering.OpenGL
import System.Exit
import Control.Monad
type Time = Double
title =
”Hello OpenGL”width, height :: GLsizei
fps :: Int
fps =
60frameTime :: Time
frameTime =
1000 * ((1::Double) / fromIntegral fps)width
=
700height
=
600w2, h2 :: GLfloat
w2 =
(fromIntegral $ width) / 2h2 =
(fromIntegral $ height)/
2dw2, dh2 :: GLdouble
dw2 =
fromRational $ toRational w2dh2 =
fromRational $ toRational h2type Vec2d =
(GLfloat, GLfloat)data Ball = Ball
{ ballPos :: Vec2d
, ballVel :: Vec2d
}
initBall = Ball
(0, 0) (0, 0)dt :: GLfloat
dt =
0.3minVel =
10main = do
initialize
openWindow (Size
width height) [] WindowwindowTitle $=
titleОсновные библиотеки | 293
clearColor $= Color4
1 1 1 1ortho (-
dw2) (dw2) (-dh2) (dh2) (-1) 1ball <-
newIORef initBallwindowCloseCallback $=
exitWith ExitSuccesswindowSizeCallback
$=
(\size -> viewport $= (Position 0 0, size))loop ball
loop :: IORef Ball -> IO
()loop ball = do
display ball
onMouse ball
sleep frameTime
loop ball
display ball = do
(px, py) <-
ballPos <$> get ball(vx, vy) <-
ballVel <$> get ballball $= Ball
(px + dt*vx, py + dt*vy) (vx, vy)clear [ColorBuffer
]color black
line (-
ow2) (-oh2) (-ow2) oh2line (-
ow2) oh2ow2
oh2
line ow2
oh2
ow2
(-
oh2)line ow2
(-
oh2)(-
ow2) (-oh2)color red
circle px py 10
swapBuffers
where
ow2 = w2 - 50oh2 =
h2 - 50onMouse ball = do
mb <-
getMouseButton ButtonLeftwhen (mb == Press
) (get mousePos >>= updateVel ball)updateVel ball pos = do
(p0x, p0y) <-
ballPos <$> get ballv0
<-
ballVel <$> get ballsize <-
get windowSizelet
(p1x, p1y) = mouse2canvas size posv1 =
scaleV (max minVel $ len v0) $ norm (p1x - p0x, p1y - p0y)ball $= Ball
(p0x, p0y) v1where
norm v@(x, y) = (x / len v, y / len v)len
(x, y) =
sqrt (x*x + y*y)scaleV k (x, y) =
(k*x, k*y)mouse2canvas :: Size -> Position ->
(GLfloat, GLfloat)mouse2canvas (Size
sx sy) (Position mx my) = (x, y)where
d a b=
fromIntegral a / fromIntegral bx
=
fromIntegral width * (d mx sx - 0.5)y
=
fromIntegral height * (negate $ d my sy - 0.5)vertex2f :: GLfloat -> GLfloat -> IO
()vertex2f a b =
vertex (Vertex3 a b 0)-- colors
...
white, black, red-- primitives
line
:: GLfloat -> GLfloat -> GLfloat -> GLfloat -> IO
()circle
:: GLfloat -> GLfloat -> GLfloat -> IO
()294 | Глава 20: Императивное программирование
Теперь функция display принимает ссылку на глобальную переменную, которая отвечает за движение
шарика. Функция mouse2canvas переводит координаты в окне GLFW
в координаты OpenGL. В GLFW начало ко-ординат лежит в левом верхнем углу окна и ось Oy
направлена вниз. Мы же переместили начало координатв центр окна и ось Oy направлена вверх.
Посмотрим что у нас получилось:
$ ghc --make Animation.hs
$ ./Animation
Chipmunk
Картинка ожила, но шарик движется не реалистично. Он проходит сквозь стены. Добавим в нашу про-
грамму немного физики. Воспользуемся библиотекой Hipmunk
cabal install Hipmunk
Она даёт возможность вызывать из Haskell функции С-библиотеки Chipmunk
. Эта библиотека позволя-ет строить двухмерные физические модели. Основным элементом модели является пространство (Space
).К нему мы можем добавлять различные объекты. Объект состоит из двух компонент: тела (Body
) и формы(Shape
). Тело отвечает за такие физические характеристики как масса, момент инерции, восприимчивость ксилам. По форме определяются моменты столкновения тел. Форма может состоять из нескольких примити-
вов: окружностей, линий и выпуклых многоугольников. Также мы можем добавлять различные ограничения
(Constraint
) они имитируют пружинки, шарниры. Мы можем назначать выполнение IO-действий на столк-новения.
Опишем в Hipmunk
модель шарика бегающего в замкнутой коробке:module Main where
import Data.StateVar
import Physics.Hipmunk
main = do
initChipmunk
space <-
newSpaceinitWalls space
ball <-
initBall space initPos initVelloop 100 space ball
loop :: Int -> Space -> Body -> IO
()loop 0 _
_
=
return ()loop n space ball = do
showPosition ball
step space 0.5