You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Haskell SDL2绑定中绘制随机位置矩形的类型匹配问题

解决Haskell SDL2中生成随机矩形的IO Monad类型不匹配问题

问题根源

你遇到的类型错误核心在于:createRandomRectangles 10返回的是IO [RectangleObject](一个能生成矩形列表的IO动作),但GameState的objects字段需要的是纯值[RectangleObject]。Haskell严格区分纯值与IO动作,不能直接把IO动作当作纯值传给数据构造器,必须先执行IO动作提取出内部的纯值。

修复步骤

  1. 在main中提取IO动作结果:用do块绑定createRandomRectangles 10的执行结果,再将纯值传入GameState构造器。
  2. 修复appLoop中的未定义变量:原代码里的vGameState是笔误,应改为gameState;同时补充qPressed的判断逻辑,检查是否有按下Q键的退出事件。

修改后的完整代码

{-# 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
  -- 从IO动作中提取随机矩形列表
  randomRects <- createRandomRectangles 10
  let gameState = GameState (V2 10 10) (V2 40 40) (V2 1 1) randomRects
  appLoop renderer gameState
  destroyRenderer renderer
  destroyWindow window
  quit

appLoop :: Renderer -> GameState -> IO ()
appLoop renderer gameState = do
  events <- pollEvents
  -- 判断是否按下Q键退出
  let qPressed = any (\e -> eventPayload e == KeyboardEvent (KeyboardEventData True False False (Keysym SDLK_Q NoModifier NoModifier))) events

  clear renderer
  rendererDrawColor renderer $= V4 0 0 0 255

  -- 绘制随机生成的矩形
  let objs = objects gameState
  mapM_ (\(RectangleObject posn dim) -> fillRect renderer (Just $ Rectangle (P posn) dim)) objs

  -- 绘制背景黑框(匹配窗口初始尺寸)
  fillRect renderer (Just $ Rectangle (P $ V2 0 0) (V2 640 480))
  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 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
  x <- randomRIO (1, 590)  -- 调整范围避免超出窗口边界
  y <- randomRIO (1, 430)
  w <- randomRIO (10, 50)
  h <- randomRIO (10, 50)
  return $ RectangleObject (V2 x y) (V2 w h)

额外优化说明

  • 调整了随机矩形的坐标和尺寸范围,避免超出640x480的窗口边界
  • 修正了背景填充的尺寸,与窗口初始大小保持一致
  • 补充了完整的Q键退出逻辑

内容的提问来源于stack exchange,提问作者TheSinisterStone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 05:13:29