我正在使用Gloss库在Haskell中编写Sokoban程序,并且我达到了我想要的程度,当玩家击败关卡时,从文本文件加载到新关卡并拥有程序继续。
由于Gloss的限制,我有一点点困难 - 设置游戏的play
功能让它不断更新如下:
play :: forall world
. Display -- ^ Display mode.
-> Color -- ^ Background color.
-> Int -- ^ Number of simulation steps to take for each second of real time.
-> world -- ^ The initial world.
-> (world -> Picture) -- ^ A function to convert the world a picture.
-> (Event -> world -> world) -- ^ A function to handle input events.
-> (Float -> world -> world) -- ^ A function to step the world one iteration.
-- It is passed the period of time (in seconds) needing to be advanced.
-> IO ()
以下是我正在使用的world
类型:
data Game = Game
{ levelNumber :: Int,
currentLevel :: Level Square,
won :: Bool }
其中Level
包含当前级别的块。我正在使用类似的东西在Game
中读取(实际上还没有一个广义的,但这基本上就是文件名参数):
startGame = do
lvl <- readFile "levels/level001.lvl"
let lvl' = parseLevel lvl
return $ Game 1 lvl' False
所以,由于play
中的更新功能,我的困难正在出现。如果我只是在单个级别上操作,我可以轻松地获取Game
并生成Picture
(以及Game
等),而无需从文件系统中读取任何数据,但由于我在游戏过程中加载文件中的级别,我不知道如何避免制作Game
IO Game
的所有内容。也许在这种情况下这是不可能的,也许这是有充分理由的?我将始终在从文件中提取的Game
上运行,但我不知道在任何给定点是否可以避免,如果是,我想避免它。
答案 0 :(得分:3)
我最终使用来自Graphics.Gloss.Interface.IO.Game的Gloss playIO
。我需要更改我的几个函数来操作纯数据类型,并将它们包装在IO
monad中。以下是我必须改变的类型:
worldToPicture :: World -> IO Picture
eventHandler :: Event -> World -> IO Picture
stepWorld :: Float -> World -> IO World
在大多数情况下,这只会导致在我当前现有的功能中添加一些return
,但它最终添加了很多功能(例如即时保存,加载新级别,使用BMP文件图形等)。我还能够保留几乎所有现有代码IO
,因为新函数仍将纯数据类型作为参数。它最终成为一个非常简单的重构,完美地解决了我的问题。