haskellgloss

How to implement multiple levels in Gloss Haskell?


I am trying to implement a game with multiple levels in Haskell. Each level has different but generalized game state. We can define the game state like the following-

type Player = (Float, Float) -- Player coordinates
data State a = State Player a Bool

Here, a is the environment. In one level it is defined as String and in another level it is defined as ([Int], [Int]). And the Bool determines if level is complete or not.

I have two different functions implemented that uses play from Graphics.Gloss.Interface.Pure.Game. I can inject any of this functions to the main and play them individually. For example-

lv1 = play window black 90 (State (0,0) "Hello" False) drawLv1 handleLv1 updateLv1
lv2 = play window black 90 (State (0,0) ([1,2,3,4], [0,0,0,0]) False) drawLv2 handleLv2 updateLv2

main :: IO()
main = lv1
-- main = lv2

However, I want to go from lv1 to lv2 somehow. I know that Haskell functions can be passed through as values and there is obviously a way to change the main function from lv1 to lv2 but I cannot figure that out.

A solution or maybe an idea on how the problem can be generalized and handled properly, would be very nice.


Solution

  • I decided to write how I finally solved the problem since others might face the same problem as me-

    First of all, I changed the State data to make a generalized system for better transition between different levels. So instead of current State a data, I now have State but with a new parameter Level

    type Player = (Float, Float) -- Player coordinates
    -- data State a = State Player a Bool
    data State = State {
        getPlayer :: Player,
        getLevel :: Level,
        levelCompleted :: Bool
    }
    data Level = Lv1 String | Lv2 ([Int], [Int])
    

    Then I needed to fix the level specific functions accordingly. For example-

    updateLv1 :: Float -> State -> State
    -- If com is true then level is complete
    updateLv1 _ (State player (Lv1 st) com) =
        if com then State (0,0) ([1,2,3,4], [0,0,0,0]) False
        else
            ...
            ...
    
    updateLv1 time state = commonUpdater time state
    

    Same goes for drawing and event handlers.

    Finally I had to fix the common handlers-

    commonUpdater time state = case getLevel state of
        Lv1 _ = updateLv1 time state
        Lv2 _ = updateLv2 time state
    

    I had to implement the commonHandler and commonDrawer too. The process is almost the same.

    Finally the play function was easy. I just had to initialize the Lv1 state and rest were to be handled by common functions-

    game :: IO()
    game = play window black 90 (State (0,0) (Lv2 "Hello") False) commonDrawer commonHandler commonUpdater
    
    main :: IO()
    main = game
    

    That's all!