我如何使用MVars在我的乒乓haskell游戏中移动球拍? [英] How can i use MVars to move paddles on my pingpong haskell game?
问题描述
我已经具有在Haskell的乒乓球游戏中移动2个桨的功能.我要更改,因此现在使用MVars.
I already have a function that moves 2 paddles in a ping pong game in haskell. I want to change so it uses MVars now.
我知道我需要将wHeld,sHeld,downHeld和upHeld更改为MVars,但是关于如何更改movePaddle以处理MVars的任何想法?
I know that i need to change wHeld, sHeld, downHeld and upHeld to MVars but any ideas on how to change movePaddle to deal with MVars?
另外,当我声明将MVars举为MVar时,它在派生show时也会显示错误((Show MVar Bool的非实例)
Also when i declare wHeld an MVars it shows a error on deriving show (Non instance for (Show MVar Bool))
data PongGame = Game
{ ballLoc :: (Float, Float) -- ^ Pong ball (x, y) location.
, ballVel :: (Float, Float) -- ^ Pong ball (x, y) velocity.
, player1 :: Float -- ^ Left player paddle height.
-- Zero is the middle of the screen.
, player2 :: Float -- ^ Right player paddle height.
, playerRPos :: Float -- posicao do player right
, playerLPos :: Float --- posicao do player left
, ghci :: Bool -- pausar
, showMenu :: Bool -- mostrar o menu
, wHeld :: MVar Bool -- segura o w
, sHeld :: Bool -- segura os
, downHeld :: Bool -- segura down
, upHeld :: Bool -- segura para cima
, playerLScore :: Int -- score do jogador left
, playerRScore :: Int -- score do jogador right
, paused :: Bool
} deriving Show
movePaddle :: PongGame -> PongGame
movePaddle = moveLeftPaddle . moveRightPaddle
moveLeftPaddle game
| (wHeld game) = game {playerLPos = paddleUp (playerLPos game)}
| (sHeld game) = game {playerLPos = paddleDn (playerLPos game)}
| otherwise = game
moveRightPaddle game
| (upHeld game) = game {playerRPos = paddleUp (playerRPos game)}
| (downHeld game) = game {playerRPos = paddleDn (playerRPos game)}
| otherwise = game
paddleUp pos = min (pos + 10) paddleMax
paddleDn pos = max (pos - 10) paddleMin
推荐答案
MVars的工作方式是类型MVar Bool
的值是不透明的令牌",引用了Bool
的存储位置.您可以创建此类令牌,并使用IO操作读取和修改其关联存储位置的内容.
The way MVars work is that a value of type MVar Bool
is an opaque "token" referencing a storage location for a Bool
. You create such a token and read and modify the contents of its associated storage location using IO actions.
默认情况下,令牌本身(MVar Bool
值)没有Show
实例.为了进行调试,您可以添加以下内容:
The token itself (the MVar Bool
value) has no Show
instance by default. For debugging purposes, you could add one:
instance Show (MVar a) where show _ = "<MVar>"
这将允许您派生PongGame
的Show
实例,而不会收到错误消息.但是,如果没有严重的IO滥用,Show
实例将无法显示存储在MVars
中的值,因此您可能需要考虑编写一个函数dumpGame :: PongGame -> IO ()
,该函数可以用所有MVar值,可让您完全跳过Show
实例.
This will allow you to derive a Show
instance for PongGame
without getting an error message. However, the values stored in the MVars
can't be displayed by the Show
instance without some egregious IO abuse, so you might want to consider writing a function dumpGame :: PongGame -> IO ()
that pretty-prints the current game state with all the MVar values, letting you skip the Show
instance entirely.
无论如何,要重写程序以在关键的PongGame
字段中使用MVar:
Anyway, to rewrite your program to use MVars in key PongGame
fields:
data PongGame = Game
{
...
, wHeld :: MVar Bool -- segura o w
, sHeld :: MVar Bool -- segura os
, downHeld :: MVar Bool -- segura down
, upHeld :: MVar Bool -- segura para cima
...
}
您将要重写movePaddle
及其子功能以在IO monad中运行:
you'll want to rewrite your movePaddle
and its subfunctions to run in the IO monad:
movePaddle :: PongGame -> IO PongGame
moveLeftPaddle :: PongGame -> IO PongGame
moveRightPaddle :: PongGame -> IO PongGame
在movePaddle
中,您可以将.
运算符替换为Control.Monad
中的<=<
,这是功能组合的单原子等效项:
In movePaddle
, you can replace the .
operator with <=<
from Control.Monad
, which is the monadic equivalent of function composition:
movePaddle = moveLeftPaddle <=< moveRightPaddle
这基本上是以下简称:
movePaddle game = do
game1 <- moveRightPaddle game
game2 <- moveLeftPaddle game1
return game2
然后,您需要通过执行IO操作来重写moveLeftPaddle
和moveRightPaddle
来访问MVar的内容:
Then, you'll need to rewrite moveLeftPaddle
and moveRightPaddle
to access the contents of the MVars by executing IO actions:
moveLeftPaddle game
= do up <- readMVar (wHeld game)
dn <- readMVar (sHeld game)
case (up, dn) of
(True, False) -> return $ game {playerLPos = paddleUp (playerLPos game)}
(False, True) -> return $ game {playerLPos = paddleDn (playerLPos game)}
_ -> return game
与moveRightPaddle
的定义类似.
要清楚,函数调用wHeld game
是一个简单的纯函数调用,它检索与wHeld
字段关联的类型MVar Bool
的令牌.然后,我们执行IO操作readMVar <this_token>
以实际检索Bool
值,然后可以在case
语句中对其进行操作以更新游戏状态.
To be clear, the function call wHeld game
is a simple, pure function call that retrieves the token of type MVar Bool
associated with the wHeld
field. We then execute the IO action readMVar <this_token>
to actually retrieve the Bool
value, which we can then act on in a case
statement to update the game state.
在程序的其他地方,您将需要一个setup
例程来创建这些MVar:
Elsewhere in your program, you'll need a setup
routine that creates these MVars:
initGame :: IO PongGame
initGame = do
...
wHeld <- newMVar False
sHeld <- newMVar False
...
return $ Game ... wHeld sHeld ...
您可能会有一些线程正在运行类似这样的函数:
and you'll presumably have some thread that's running a function like:
processKeyEvent :: KeyEvent -> PongGame -> IO ()
processKeyEvent event game = do
...
case event of
...
KeyDown 'W' -> void $ swapMVar (wHeld game) True
KeyUp 'W' -> void $ swapMVar (wHeld game) False
...
这篇关于我如何使用MVars在我的乒乓haskell游戏中移动球拍?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!