Skip to content

Spawning Bonuses

noooway edited this page Mar 30, 2017 · 36 revisions

So far the game has been somewhat dull. To add some diversity to the gameplay, bonuses are introduced. They alter the game mechanics in minor ways and provide additional challenge for the player. Parts 3-04 -- 3-09 are dedicated to the bonuses. This part opens the sequence by addressing a question of how the bonuses appear in the game and their basic behavior.

A bonus is an object that emerges on destruction of a brick. After it appears, it falls down with constant velocity. If the player catches it with the platform, i.e. on bonus-platform collision, the effect of the bonus is applied.

There could be several bonuses on the screen simultaneously. For that reason, basic definitions for the bonuses are similar to the bricks.

Functions, responsible for behavior of a single bonus are shown below.

local bonuses = {}

.....
bonuses.radius = 14                                           --(*2)
bonuses.speed = vector( 0, 100 )                              --(*3)
.....

function bonuses.new_bonus( position, bonustype )             --(*1)
   return( { position = position,
             bonustype = bonustype,
             quad = bonuses.bonustype_to_quad( bonustype ) } )
end

function bonuses.draw_bonus( single_bonus )                   --(*2)
   if single_bonus.quad then
      love.graphics.draw(
         bonuses.image,
         single_bonus.quad, 
         single_bonus.position.x - bonuses.tile_width / 2,
         single_bonus.position.y - bonuses.tile_height / 2 )
   end
   local segments_in_circle = 16
   love.graphics.circle( 'line',
                         single_bonus.position.x,
                         single_bonus.position.y,
                         bonuses.radius,
                         segments_in_circle )
end

function bonuses.update_bonus( single_bonus )                 --(*3)
   single_bonus.position = single_bonus.position + bonuses.speed * dt
end

.....
return bonuses

(*1): To create a single bonus, it necessary to provide to the constructor a position and a bonus type. Similarly to the bricks, a quad is inferred from the provided bonustype.
(*2): A bonus is represented as a sphere with the radius defined by bonuses.radius variable. In the drawing function, apart from the quad, the circle is drawn with the love.graphics.circle for testing purposes.
(*3): A bonus falls down with a constant speed, defined by bonuses.speed variable.

Functions that apply to bonus container

bonuses.current_level_bonuses = {}

function bonuses.update( dt )
   for _, bonus in pairs( bonuses.current_level_bonuses ) do
      bonuses.update_bonus( bonus )
   end
end

function bonuses.draw()
   for _, bonus in pairs( bonuses.current_level_bonuses ) do
      bonuses.draw_bonus( bonus )
   end
end

function bonuses.add_bonus( bonus )
   table.insert( bonuses.current_level_bonuses, bonus )
end

function bonuses.generate_bonus( position, bonustype )
   if bonuses.valid_bonustype( bonustype ) then
      bonuses.add_bonus( bonuses.new_bonus( position, bonustype ) )
   end
end

function bonuses.valid_bonustype( bonustype )
   if bonustype and bonustype > 10 and bonustype < 19 then
      return true
   else
      return false
   end
end

function bonuses.clear_current_level_bonuses()
   for i in pairs( bonuses.current_level_bonuses ) do
      bonuses.current_level_bonuses[i] = nil
   end
end

And a tileset definition

bonuses.image = love.graphics.newImage( "img/800x600/bonuses.png" )
bonuses.tile_width = 64
bonuses.tile_height = 32
bonuses.tileset_width = 512
bonuses.tileset_height = 32

function bonuses.bonustype_to_quad( bonustype )
   if bonustype == nil or bonustype <= 10 or bonustype >= 20 then
      return nil
   end
   local row = math.floor( bonustype / 10 )
   local col = bonustype % 10
   local x_pos = bonuses.tile_width * ( col - 1 )
   local y_pos = bonuses.tile_height * ( row - 1 )
   return love.graphics.newQuad(
      x_pos, y_pos,
      bonuses.tile_width, bonuses.tile_height,
      bonuses.tileset_width, bonuses.tileset_height )
end

When this is done, it is necessary to incorporate the bonuses in the "game" state: insert the calls to the bonuses.update and bonuses.draw methods into corresponding gamestate callbacks and so on. To avoid missing something, it helps to trace the occurrences of the bricks table in the "game" functions. Since the bricks and the bonuses are alike, bonuses occur mostly in the same places as the bricks.

.....
local bricks = require "bricks"
local bonuses = require "bonuses"
local walls = require "walls"
.....

function game.update( dt )
   .....
   bricks.update( dt )
   bonuses.update( dt )
   walls.update( dt )
   .....
end

.....
  1. Spawning bonuses. Now it is necessary to address a question of how to actually add new bonuses into the game. So, it means that each brick should be associated with a certain bonustype. The bricks are defined in level files in a form of a table. It is natural to add into the level file a table with bonustypes. It should have the same size as the bricks table and should hold bonustypes for each brick.
return {
   bricks = {
      {51, 51, 00, 00, 00, 00, 51, 51},
      {51, 00, 00, 00, 00, 00, 00, 51},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {21, 21, 22, 23, 24, 25, 26, 26},
      {31, 31, 32, 33, 34, 35, 36, 36},
      {41, 41, 42, 43, 44, 45, 46, 46},
      {11, 11, 12, 13, 14, 15, 16, 16},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00}
   },
   bonuses = {
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {17, 11, 12, 13, 14, 15, 16, 18},
      {17, 11, 12, 13, 14, 15, 16, 18},
      {17, 11, 12, 13, 14, 15, 16, 18},
      {17, 11, 12, 13, 14, 15, 16, 18},
      {00, 00, 00, 00, 00, 00, 00, 00},
      {00, 00, 00, 00, 00, 00, 00, 00}
   }
}

Since the bonustype is going to be stored inside the brick, it is necessary to add an appropriate argument to bricks constructor bricks.new_brick( ....., bonustype ). The bricks for the level are constructed in a bricks.construct_level function. Now apart from parsing brick types, it is also necessary to extract bonustypes and pass it to bricks constructor.

function bricks.construct_level( level )
   .....
   for row_index, row in pairs( level.bricks ) do
      for col_index, bricktype in pairs( row ) do
         if bricktype ~= 0 then
            .....
            local new_brick_position = vector( new_brick_position_x,
                                               new_brick_position_y )
            local bonustype = level.bonuses[ row_index ][ col_index ]
            local new_brick = bricks.new_brick( new_brick_position,
                                                bricks.brick_width,
                                                bricks.brick_height,
                                                bricktype,
                                                bonustype )
            table.insert( bricks.current_level_bricks, new_brick )
         end
      end
   end
end

function bricks.new_brick( position, width, height, bricktype, bonustype )
   return( { position = position,
             width = width or bricks.brick_width,
             height = height or bricks.brick_height,
             bricktype = bricktype,          
             quad = bricks.bricktype_to_quad( bricktype ),
             bonustype = bonustype } )
end

Now, when the bonustypes are associated with bricks, it is necessary to spawn the corresponding bonuses on brick destruction. First, it is necessary to pass bonuses table into a function that resolves brick-ball collision. Since the brick destruction is done in the bricks.brick_hit_by_ball function, and bonuses are created only on brick destruction, it is necessary to pass bonuses table into that function as an argument.

function game.update( dt )
   .....
   collisions.resolve_collisions( ball, platform, walls, bricks, bonuses )
   .....
end

function collisions.resolve_collisions( ball, platform,
					walls, bricks, bonuses )
   .....
   collisions.ball_bricks_collision( ball, bricks, bonuses )
   .....
end

function collisions.ball_bricks_collision( ball, bricks, bonuses )
   .....
      if overlap then    
         ball.brick_rebound( shift_ball )
         bricks.brick_hit_by_ball( i, brick, shift_ball, bonuses )
      end
   .....
end

If the brick is to be destroyed, bonuses.generate_bonus is called. It is supplied with the bonus position and the bonustype. This function checks the validity of the bonustype and if it is valid, adds the corresponding bonus into the bonuses.current_level_bonuses table with a call to bonuses.add_bonus function.

function bricks.brick_hit_by_ball( i, brick, shift_ball, bonuses )
   if bricks.is_simple( brick ) then
      bonuses.generate_bonus(
         vector( brick.position.x + brick.width / 2,
                 brick.position.y + brick.height / 2 ),
         brick.bonustype )
      table.remove( bricks.current_level_bricks, i )
      simple_break_sound:play()
   elseif 
      .....
   elseif bricks.is_cracked( brick ) then
      bonuses.generate_bonus(
         vector( brick.position.x + brick.width / 2,
                 brick.position.y + brick.height / 2 ),
         brick.bonustype )
      table.remove( bricks.current_level_bricks, i )
      armored_break_sound:play()
   elseif bricks.is_heavyarmored( brick ) then
      .....
   end
end

    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