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

如何使用Gloss库实现自定义图片的屏幕移动?

问题描述

我正在使用Gloss库开发一款简易游戏,采用BMP文件作为精灵,加载后需实现动画与移动效果,尝试用animate或simulate函数来完成。simulate函数的一个参数类型为(model -> Picture),即接收存储游戏状态的自定义数据类型并返回Picture的函数,但加载文件常用的loadBMP函数类型为FilePath -> IO Picture。我难以从整体层面把握实现图片移动的具体方案,尤其困惑于loadBMP返回IO Picture而非Picture的问题。

提供的代码

module Main(main) where

import Graphics.Gloss
import System.IO  
import Control.Monad
import Graphics.Gloss.Data.ViewPort

data GameState = State{ 
      boardpic::Picture
    , ballpic :: Picture
    , ballCoords :: (Float, Float)  
    , ballSpeed :: (Float, Float) 
    , paddle1pic::Picture  
    , p1Coords :: (Float, Float)
    , paddle2pic::Picture
    , p2Coords:: (Float,Float)
    , paused::Bool
    , turnscore::Integer                                  
    } deriving (Show) 

moveBall time state = state {ballCoords = (xnew, ynew)} 
    where
    (xposbefore, yposbefore) = ballCoords state 
    (xspeed, yspeed) = ballSpeed state
    (xnew, ynew) = (xposbefore + xspeed*time, yposbefore+xspeed*time)

render::GameState->Picture
render state =  
  pictures[board, 
           paddle1, 
           paddle2,
           ball]
  where
    board = boardpic state
    ball = translate 0 0 $ ballpic state
    paddle1 = translate (-300) 0 $ paddle1pic state
    paddle2 = translate 300 0 $ paddle2pic state  

--calculates running sum of score for the player from list 
score xs = tail.reverse $ foldl f [0] xs 
  where 
    f (y:ys) x = (x+y):y:ys
 
window :: Display
window = InWindow "SuperPong" (800, 800) (0,0)

background::Color
background = blue

fps :: Int
fps = 60    
    
printState::GameState->IO ()
printState state = do
  putStrLn $ show $ boardpic state
  putStrLn $ show $ ballpic state
  putStrLn $ ("ball coordinates: ")++(show $ ballCoords state)
  putStrLn $ ("paddle1 coords:  " )++(show $ p1Coords state)
  putStrLn $ ("paddle2 coords: " )++(show $ p2Coords state)
  putStrLn $ ("paused or not: " ) ++ (show $ paused state)
  putStrLn $ ("score: " )++(show $ turnscore state)

--paddlecollision::

--outofbounds::

main :: IO ()
main = do
  pad1 <- loadBMP "paddle1.bmp"
  pad2 <- loadBMP "paddle2.bmp"
  board <- loadBMP "board.bmp"
  ball1 <- loadBMP "ball.bmp"

  let initstate = State{
    boardpic = board
  , ballpic = ball1
  , ballCoords = (-10, 10)
  , ballSpeed = (50, -50)
  , paddle1pic = pad1
  , p1Coords = (-300, 0)
  , paddle2pic = pad2
  , p2Coords = (300, 0)
  , paused = False
  , turnscore = 0
  }
    
  let update _ = moveBall 
  
  printState initstate
  let allpics = render initstate     

  simulate window background fps initstate render update
解决方案

1. 理解IO Picture的处理逻辑

loadBMP返回IO Picture是因为文件读取属于IO操作,必须在IO上下文执行。你当前的代码已经做对了核心步骤:在main函数(IO上下文)里加载所有BMP资源,然后将它们存入GameState中。把IO操作限制在程序入口,后续用纯函数处理游戏状态,这是Haskell处理IO的标准方式,你不需要带着IO类型的Picture进入render或update函数,当前的代码结构已经解决了这个困惑。

2. 修复图片移动的核心实现

修正moveBall的计算错误

你在计算球的Y坐标时误用了X方向的速度,改成如下:

moveBall time state = state {ballCoords = (xnew, ynew)} 
    where
    (xposbefore, yposbefore) = ballCoords state 
    (xspeed, yspeed) = ballSpeed state
    (xnew, ynew) = (xposbefore + xspeed*time, yposbefore + yspeed*time)

让render根据游戏状态动态平移图片

当前render用的是固定坐标,需要改成读取GameState里的坐标值来平移精灵:

render::GameState->Picture
render state =  
  pictures[board, 
           paddle1, 
           paddle2,
           ball]
  where
    board = boardpic state
    -- 根据ballCoords动态平移球
    (ballX, ballY) = ballCoords state
    ball = translate ballX ballY $ ballpic state
    -- 根据p1Coords动态平移左 paddle
    (p1X, p1Y) = p1Coords state
    paddle1 = translate p1X p1Y $ paddle1pic state
    -- 根据p2Coords动态平移右 paddle
    (p2X, p2Y) = p2Coords state
    paddle2 = translate p2X p2Y $ paddle2pic state  

3. 修正update函数的类型匹配

simulate要求的update函数类型是ViewPort -> Float -> GameState -> GameState,当前代码忽略了ViewPort参数,同时可以加入暂停逻辑:

update :: ViewPort -> Float -> GameState -> GameState
update _ time state = if paused state then state else moveBall time state

4. 整体流程梳理

  1. 资源加载:在main的IO上下文里加载所有BMP资源,构造包含初始坐标、速度的GameState
  2. 状态更新:simulate每帧调用update,根据时间差更新游戏状态中的坐标(比如球的位置),暂停时不更新
  3. 画面渲染:每帧调用render,根据GameState里的坐标和图片资源,用translate将精灵放到正确位置,生成要显示的Picture
  4. 自动渲染:Gloss库自动将生成的Picture渲染到窗口,形成连续动画效果

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 20:09:26