如何在Haskell的SDL2绑定中绘制随机放置的矩形?

a7qyws3x  于 2023-11-18  发布在  其他
关注(0)|答案(1)|浏览(102)

我正在尝试使用Haskell在SDL 2中绘制随机定位的矩形。IO monad真的让我抓狂,我无法让它工作。我已经准备好了SDL 2的样板代码和RectangleObject的自定义数据类型,我想做的就是在main函数中随机生成矩形,然后在appLoop函数中绘制它们。
我得到的错误:

BadRandomRectangles.hs:17:61-85: error:
    • Couldn't match expected type: [RectangleObject]
                  with actual type: IO [RectangleObject]
    • In the fourth argument of ‘GameState’, namely
        ‘(createRandomRectangles 10)’
      In the expression:
        GameState
          (V2 10 10) (V2 40 40) (V2 1 1) (createRandomRectangles 10)
      In an equation for ‘gameState’:
          gameState
            = GameState
                (V2 10 10) (V2 40 40) (V2 1 1) (createRandomRectangles 10)
   |
17 |   let gameState = GameState (V2 10 10) (V2 40 40) (V2 1 1) (createRandomRectangles 10) in
   |                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^

字符串
我的代码是:

{-# LANGUAGE OverloadedStrings #-}

import SDL
import System.Random
import Control.Monad (unless, replicateM)
import Foreign.C.Types (CInt)

data RectangleObject = RectangleObject { rectPosn :: V2 CInt, rectDim :: V2 CInt };

data GameState = GameState { posn :: V2 CInt, dim :: V2 CInt, vel :: V2 CInt, objects :: [RectangleObject] };

main :: IO ()
main = do
  initializeAll
  window <- createWindow "Rectangles" (defaultWindow { windowInitialSize = V2 640 480 })
  renderer <- createRenderer window (-1) defaultRenderer
  let gameState = GameState (V2 10 10) (V2 40 40) (V2 1 1) (createRandomRectangles 10) in
    appLoop renderer gameState
  destroyRenderer renderer
  destroyWindow window
  quit

appLoop :: Renderer -> GameState -> IO ()
appLoop renderer gameState = do
  events <- pollEvents

  clear renderer
  rendererDrawColor renderer $= V4 0 0 0 255

  -- Get the RectangleObjects from the game state and draw them
  let objs = objects gameState
  mapM_ (\(RectangleObject posn dim) -> fillRect renderer (Just $ Rectangle (P $ posn) dim)) objs

  -- Fill a rectangle of size 1280x720 with black color
  fillRect renderer (Just $ Rectangle (P $ V2 0 0) (V2 1280 720))
  rendererDrawColor renderer $= V4 255 0 0 255

  fillRect renderer (Just $ Rectangle (P $ posn gameState) (dim gameState))
  present renderer
  SDL.delay 16
  unless qPressed (appLoop renderer (updateGameState vGameState))

updateGameState :: GameState -> GameState
updateGameState (GameState posn dim vel objs) =
  GameState (posn + vel) dim vel objs

createRandomRectangles :: Int -> IO [RectangleObject]
createRandomRectangles n = replicateM n generateRandomRectangle

generateRandomRectangle :: IO RectangleObject
generateRandomRectangle = do
  x <- randomRIO (1, 10)
  y <- randomRIO (1, 10)
  w <- randomRIO (1, 10)
  h <- randomRIO (1, 10)
  return $ RectangleObject (V2 x y) (V2 w h)

brgchamk

brgchamk1#

你的主要问题是,在这段代码中:

let gameState = GameState (V2 10 10) (V2 40 40) (V2 1 1) (createRandomRectangles 10) in
  appLoop renderer gameState

字符串
表达式createRandomRectangles 10的类型是IO [RectangleObject],但GameState对象中的第四个字段是纯[RectangleObject]
将其重写为:

rects <- createRandomRectangles 10
let gameState = GameState (V2 10 10) (V2 40 40) (V2 1 1) rects in
  appLoop renderer gameState


你的main函数应该进行类型检查。
在你的代码中仍然有一些未定义的变量(qPressedvGameState)和渲染器中的一些逻辑错误(例如,你画了随机矩形,然后用一个黑色矩形覆盖它们来擦除它们)。而且,随机矩形太小了,它们只是在左上角显示为小光点。
在做了一些调整之后,下面的代码似乎可以工作,甚至看起来像“游戏”。祝你好运!

{-# LANGUAGE OverloadedStrings #-}

import SDL
import System.Random
import Control.Monad (unless, replicateM)
import Foreign.C.Types (CInt)

data RectangleObject = RectangleObject { rectPosn :: V2 CInt, rectDim :: V2 CInt };

data GameState = GameState { posn :: V2 CInt, dim :: V2 CInt, vel :: V2 CInt, objects :: [RectangleObject] };

main :: IO ()
main = do
  initializeAll
  window <- createWindow "Rectangles" (defaultWindow { windowInitialSize = V2 640 480 })
  renderer <- createRenderer window (-1) defaultRenderer
  rects <- createRandomRectangles 10
  let gameState = GameState (V2 10 10) (V2 40 40) (V2 1 1) rects in
    appLoop renderer gameState
  destroyRenderer renderer
  destroyWindow window
  quit

appLoop :: Renderer -> GameState -> IO ()
appLoop renderer gameState = do
  events <- pollEvents

  clear renderer

  -- Fill a rectangle of size 1280x720 with black color
  rendererDrawColor renderer $= V4 0 0 0 255
  fillRect renderer (Just $ Rectangle (P $ V2 0 0) (V2 1280 720))

  -- Get the RectangleObjects from the game state and draw them in green
  let objs = objects gameState
  rendererDrawColor renderer $= V4 255 255 0 255
  mapM_ (\(RectangleObject posn dim) -> fillRect renderer (Just $ Rectangle (P $ posn) dim)) objs

  -- Draw the main game rectangle in red
  rendererDrawColor renderer $= V4 255 0 0 255
  fillRect renderer (Just $ Rectangle (P $ posn gameState) (dim gameState))

  present renderer
  SDL.delay 16

  -- unless qPressed (appLoop renderer (updateGameState vGameState))
  appLoop renderer (updateGameState gameState)

updateGameState :: GameState -> GameState
updateGameState (GameState posn dim vel objs) =
  GameState (posn + vel) dim vel objs

createRandomRectangles :: Int -> IO [RectangleObject]
createRandomRectangles n = replicateM n generateRandomRectangle

generateRandomRectangle :: IO RectangleObject
generateRandomRectangle = do
  let scale = 500
  x <- randomRIO (1, scale)
  y <- randomRIO (1, scale)
  w <- randomRIO (1, scale)
  h <- randomRIO (1, scale)
  return $ RectangleObject (V2 x y) (V2 w h)

相关问题