[英]Haskell Gloss, reading from console within animate function does not update drawing
[英]Reading user mouseclick position in haskells gloss
编辑:所以我遵循了您的指示并到达了这一点:(遵循游戏规则)
https://hackage.haskell.org/package/gloss-1.9.4.1/docs/Graphics-Gloss-Interface-Pure-Game.html
drawBoard :: IO ()
drawBoard = play (InWindow "Tic Tac Toe" (300,300)(10,10)) yellow 10 board (testBoard) (handleKeys) (iteration)
testBoard :: Board -> Picture
testBoard board = grid
where
grid =
color black (line [ (-100, -300), (-100, 300) ])
iteration :: Float -> Board -> Board
iteration _ board = board
handleKeys :: Event -> Board -> Board
handleKeys (EventKey (MouseButton LeftButton) Up _ (x, y)) board = board
handleKeys _ _ = board
board = []
它将正确打开窗口并根据我的testBoard
函数绘制一条黑线。
我现在不确定的是单击按钮时如何通过新板。 我是否创建另一个功能来绘制新板,或者单击时如何继续绘制新板?
您要使用play
而不是display
。
它显式采用Event -> world -> world
(在您的情况下, world
为Board
)类型的参数来描述如何对Event
作出反应。
编辑:好的,我实现了一个玩具示例,向您展示如何构造代码。 您可以在具有相应导入内容的独立设备中找到它。
您需要弄清的第一件事是如何表示板的状态。 在井字游戏中,棋盘上的给定坐标 (一对Int
等于-1、0或1)上有一堆小字和一个十字架 。 您还拥有一个当前播放器 (在下一个动作之后将会更改)。 因此,让我们开始:
type Coordinates = (Int, Int)
data Player = Nought | Cross
data Board = Board
{ noughts :: [Coordinates]
, crosses :: [Coordinates]
, player :: Player
}
然后,您可以描述各种简单的事情。 开始比赛时, 空板应该是什么样? 玩家的举动(将新令牌放置在板上)对系统状态有什么作用(它将令牌插入当前玩家的列表中,然后将当前玩家更改为对手):
emptyBoard :: Board
emptyBoard = Board [] [] Nought
pushToken:: Coordinates -> Board -> Board
pushToken c b = case player b of
Nought -> b { noughts = c : noughts b, player = Cross }
Cross -> b { crosses = c : crosses b, player = Nought }
接下来是绘制与当前状态相对应的Picture
的问题。 在这里,我假设图片将以(0, 0)
为中心,这意味着可以通过简单地将所有坐标乘以给定的常数来更改图片的大小。 我将使用Size
参数对我的所有函数进行参数设置,使我能够轻松调整显示板的大小。
type Size = Float
resize :: Size -> Path -> Path
resize k = fmap (\ (x, y) -> (x * k, y * k))
不容易显示:我们可以简单地使用适当大小的thickCircle
,然后将其转换为坐标! 十字有点难处理,因为您必须组合2个矩形才能绘制它们。
drawNought :: Size -> Coordinates -> Picture
drawNought k (x, y) =
let x' = k * fromIntegral x
y' = k * fromIntegral y
in color green $ translate x' y' $ thickCircle (0.1 * k) (0.3 * k)
drawCross :: Size -> Coordinates -> Picture
drawCross k (x, y) =
let x' = k * fromIntegral x
y' = k * fromIntegral y
in color red $ translate x' y' $ Pictures
$ fmap (polygon . resize k)
[ [ (-0.35, -0.25), (-0.25, -0.35), (0.35,0.25), (0.25, 0.35) ]
, [ (0.35, -0.25), (0.25, -0.35), (-0.35,0.25), (-0.25, 0.35) ]
]
为了画板,我们画了一个黑色的网格,然后用圆点和十字填充:
drawBoard :: Size -> Board -> Picture
drawBoard k b = Pictures $ grid : ns ++ cs where
ns = fmap (drawNought k) $ noughts b
cs = fmap (drawCross k) $ crosses b
grid :: Picture
grid = color black $ Pictures $ fmap (line . resize k)
[ [(-1.5, -0.5), (1.5 , -0.5)]
, [(-1.5, 0.5) , (1.5 , 0.5)]
, [(-0.5, -1.5), (-0.5, 1.5)]
, [(0.5 , -1.5), (0.5 , 1.5)]
]
现在我们有了一个板子并且可以显示它,我们只需要能够抓住用户的输入并对它们进行响应,这样我们就可以运行游戏。
鼠标单击以一对浮点的形式接收,对应于图形中鼠标的位置。 我们需要将此位置转换为适当的坐标。 这是checkCoordinate
所做的:将Float
除以我们为图形选择的尺寸,并检查该位置对应于板的哪个细分。
在这里,我使用guard
, (<$)
和(<|>)对各种情况进行声明式表示,但是if ... then ... else ...
愿意,可以使用if ... then ... else ...
checkCoordinate :: Size -> Float -> Maybe Int
checkCoordinate k f' =
let f = f' / k
in (-1) <$ guard (-1.5 < f && f < -0.5)
<|> 0 <$ guard (-0.5 < f && f < 0.5)
<|> 1 <$ guard (0.5 < f && f < 1.5)
最后, handleKeys
可以检测到鼠标单击,检查它们是否与板上的位置相对应,并可以通过调用pushToken
做出适当的反应:
handleKeys :: Size -> Event -> Board -> Board
handleKeys k (EventKey (MouseButton LeftButton) Down _ (x', y')) b =
fromMaybe b $ do
x <- checkCoordinate k x'
y <- checkCoordinate k y'
return $ pushToken (x, y) b
handleKeys k _ b = b
然后,我们可以声明一个main
函数来创建窗口,从一个emptyBoard
开始游戏,使用drawBoard
显示它,并使用handleKeys
处理用户输入。
main :: IO ()
main =
let window = InWindow "Tic Tac Toe" (300, 300) (10, 10)
size = 100.0
in play window yellow 1 emptyBoard (drawBoard size) (handleKeys size) (flip const)
我没有执行任何游戏逻辑:
玩家可以将令牌放置在已经占据的网格细分中,
游戏不会检测到何时有赢家
不能连续玩多个游戏
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.