Skip to content
noooway edited this page Jul 31, 2018 · 18 revisions

The next step is to implement different levels.

All levels-related information will be held at the levels table. The actual sequence of levels will be stored in the levels.sequence array.

local levels = {}
.....
levels.sequence = {}

I represent each level as a 2d-table. Inner tables correspond to the rows of the bricks. In the future, the numbers will indicate the brick types; currently they only show whether the brick is present on certain position or not.

levels.sequence[1] = {
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
   { 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1 },
   { 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1 },
   { 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0 },
   { 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0 },
   { 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
}

levels.sequence[2] = {
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
   { 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1 },
   { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0 },
   { 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0 },
   { 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
   { 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1 },
   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
}

Since there are several levels, it is necessary to maintain the current level number:

levels.current_level = 1

Changes are necessary in bricks.construct_level function, to make it receive one of the levels.sequence[.....] tables with bricks arrangement.

function bricks.construct_level( level_bricks_arrangement )
   bricks.no_more_bricks = false
   for row_index, row in ipairs( level_bricks_arrangement ) do         --(*1)
      for col_index, bricktype in ipairs( row ) do
         if bricktype ~= 0 then                                        --(*2)
            local new_brick_position_x = bricks.top_left_position_x +
               ( col_index - 1 ) *
               ( bricks.brick_width + bricks.horizontal_distance )
            local new_brick_position_y = bricks.top_left_position_y +
               ( row_index - 1 ) *
               ( bricks.brick_height + bricks.vertical_distance )
            local new_brick = bricks.new_brick( new_brick_position_x,
                                                new_brick_position_y )
            table.insert( bricks.current_level_bricks, new_brick )
         end
      end
   end
end

(*1): Instead of the fixed number of rows and columns, the iteration goes over the elements of the level_bricks_arrangement.
(*2): The brick is created if it's type is nonzero.

When there are no more bricks left, switch to the next level happens. The number of remaining bricks is monitored by bricks.update function. If there are none, a function to switch to the next level could be called. However, instead of that, I raise the bricks.no_more_bricks flag, which is checked each update cycle

function bricks.update( dt )
   if #bricks.current_level_bricks == 0 then    --(*1)
      bricks.no_more_bricks = true
   else
      for _, brick in pairs( bricks.current_level_bricks ) do
         bricks.update_brick( brick )
      end      
   end
end

function love.update( dt )
   .....
   levels.switch_to_next_level( bricks )
end

(*1): The number of remaining bricks is equivalent to the length of the bricks.current_level_bricks table.

If it is on, then the current_level is updated, the new set of bricks is constructed and the ball is repositioned.

function levels.switch_to_next_level( bricks )
   if bricks.no_more_bricks then
      .....
         levels.current_level = levels.current_level + 1
         bricks.construct_level( levels.sequence[levels.current_level] )
         ball.reposition()
      .....
      end
   end
end

function ball.reposition()
   ball.position_x = 200
   ball.position_y = 500   
end

It is possible to directly call the levels.switch_to_next_level in the bricks.update instead of rising the bricks.no_more_bricks flag. However, the flag will be more convenient later, when gamestates are introduced.

It is also necessary to do something special when there are no more levels, i.e. when the game is finished. For now, displaying a simple congratulations message should be sufficient.

The total number of levels equals to the length of the levels.sequence array. It is possible to check whether the levels.current_level is the last one by comparing it with the #levels.sequence. When there are no more levels left, switch_to_next_level function raises the levels.gamefinished flag.

function levels.switch_to_next_level( bricks )
   if bricks.no_more_bricks then
      if levels.current_level < #levels.sequence then       --(*1)
         levels.current_level = levels.current_level + 1                  --(*2)
         bricks.construct_level( levels.sequence[levels.current_level] ) 
         ball.reposition()                                                
      else
         levels.gamefinished = true                                       --(*3)
      end
   end
end

(*1): checks, whether the levels.current_level is the last one.
(*2): if not, update the level counter, construct new level and reposition the ball.
(*3): otherwise signal that the game is finished.

When levels.gamefinished is on, the message is displayed

function love.draw()
   .....
   walls.draw()
   if levels.gamefinished then
      love.graphics.printf( "Congratulations!\n" ..
			       "You have finished the game!",
			    300, 250, 200, "center" )
   end
end

Through this tutorial I've chosen to represent levels as 2d Lua tables. In the appendix A a method to store them as text strings is demonstrated.



← 1.5-Resolving Collisions     ↑ To the top     Next section: 2.1-Splitting Code Into Several Files →

    Home
    Acknowledgements
    Todo

Chapter 1: Prototype

  1. The Ball, The Brick, The Platform
  2. Game Objects as Lua Tables
  3. Bricks and Walls
  4. Detecting Collisions
  5. Resolving Collisions
  6. Levels

    Appendix A: Storing Levels as Strings
    Appendix B: Optimized Collision Detection (draft)

Chapter 2: General Code Structure

  1. Splitting Code into Several Files
  2. Loading Levels from Files
  3. Straightforward Gamestates
  4. Advanced Gamestates
  5. Basic Tiles
  6. Different Brick Types
  7. Basic Sound
  8. Game Over

    Appendix C: Stricter Modules (draft)
    Appendix D-1: Intro to Classes (draft)
    Appendix D-2: Chapter 2 Using Classes.

Chapter 3 (deprecated): Details

  1. Improved Ball Rebounds
  2. Ball Launch From Platform (Two Objects Moving Together)
  3. Mouse Controls
  4. Spawning Bonuses
  5. Bonus Effects
  6. Glue Bonus
  7. Add New Ball Bonus
  8. Life and Next Level Bonuses
  9. Random Bonuses
  10. Menu Buttons
  11. Wall Tiles
  12. Side Panel
  13. Score
  14. Fonts
  15. More Sounds
  16. Final Screen
  17. Packaging

    Appendix D: GUI Layouts
    Appendix E: Love-release and Love.js

Beyond Programming:

  1. Game Design
  2. Minimal Marketing (draft)
  3. Finding a Team (draft)

Archive

Clone this wiki locally