Different Brick Types - noooway/love2d_arkanoid_tutorial GitHub Wiki

Currently different bricks respond identically to collisions with the ball - they disappears on the first hit. In this part I'm going to add bricks, that can take several hits.

To implement different reactions, it is necessary to change the brick response on collision with the ball. The idea is simple: we know the type of each brick, and we need to check it in collision. That is, most of the changes will concern the bricks.brick_hit_by_ball function.

Currently, the brick type is encoded by a two-digit number. We can continue to work with that, however, it helps the code readability to define human readable types. It is possible to create a table, that maps two-digit types to human readable descriptions. Instead, I'll use a simpler approach and just define several methods that determine brick properties based on it's type number.

Brick colors are blue, green, orange, purple, red and yellow. In the first row bricks are 'simple', in the second -- 'armored', third -- 'scratched', fourth -- 'cracked'. Fifth row is 'heavyarmored'.

The appropriate methods are following:

function bricks.is_simple( single_brick )
   local row = math.floor( single_brick.bricktype / 10 )
   return ( row == 1 )
end

function bricks.is_armored( single_brick )
   local row = math.floor( single_brick.bricktype / 10 )
   return ( row == 2 )
end

function bricks.is_scratched( single_brick )
   local row = math.floor( single_brick.bricktype / 10 )
   return ( row == 3 )
end

function bricks.is_cracked( single_brick )
   local row = math.floor( single_brick.bricktype / 10 )
   return ( row == 4 )
end

function bricks.is_heavyarmored( single_brick )
   local row = math.floor( single_brick.bricktype / 10 )
   return ( row == 5 )
end

If the brick is 'simple', it is destroyed on the collision with the ball. If it is 'armored', after collision it's type is changed to 'scratched'. 'Scratched' becomes 'cracked'. 'Cracked' is destroyed on collision. 'Heavyarmored' bricks are unaffected by collisions.

Here is a template for case analysis in the bricks.brick_hit_by_ball:

function bricks.brick_hit_by_ball( i, brick, shift_ball )
   if bricks.is_simple( brick ) then
      .....
   elseif bricks.is_armored( brick ) then
      .....
   elseif bricks.is_scratched( brick ) then
      .....
   elseif bricks.is_cracked( brick ) then
      .....
   elseif bricks.is_heavyarmored( brick ) then
      .....
   end
end

If the brick should be destroyed, it is enough to simply call the table.remove function. To change brick type, it is convenient to have a special functions: armored_to_scratched and scratched_to_cracked. It is possible implement them simply by adding 10 to the brick type. After brick type is changed it is also necessary to update it's quad; this is done by calling the bricktype_to_quad function.

function bricks.armored_to_scratched( single_brick )
   single_brick.bricktype = single_brick.bricktype + 10
   single_brick.quad = bricks.bricktype_to_quad( single_brick.bricktype )
end

function bricks.scratched_to_cracked( single_brick )
   single_brick.bricktype = single_brick.bricktype + 10
   single_brick.quad = bricks.bricktype_to_quad( single_brick.bricktype )
end

With these functions, the collision resolution code looks like this:

function bricks.brick_hit_by_ball( i, brick, shift_ball )
   if bricks.is_simple( brick ) then
      table.remove( bricks.current_level_bricks, i )
   elseif bricks.is_armored( brick ) then
      bricks.armored_to_scratched( brick )
   elseif bricks.is_scratched( brick ) then
      bricks.scratched_to_cracked( brick )
   elseif bricks.is_cracked( brick ) then
      table.remove( bricks.current_level_bricks, i )
   elseif bricks.is_heavyarmored( brick ) then
   end
end

It is also necessary not to forget to remove 'heavyarmored' bricks from the check for next level switch and remove them from the bricks.current_level_bricks before switching to the next level:

function bricks.update( dt )
   local no_more_bricks = true
   for _, brick in pairs( bricks.current_level_bricks ) do
      if bricks.is_heavyarmored( brick ) then
         no_more_bricks = no_more_bricks and true           --(*1)
      else
         no_more_bricks = no_more_bricks and false
      end
   end
   bricks.no_more_bricks = no_more_bricks
end

function game.switch_to_next_level( bricks, ball, levels )
   if bricks.no_more_bricks then
      bricks.clear_current_level_bricks()                   --(*2)
      .....
end

(*1): If there is a brick, but it's type is 'heavyarmored', we ignore it.
(*2): The remaining 'heavyarmored' bricks are removed from the bricks.current_level_bricks before switching to the next level.

Also, in the objects' draw methods I remove the auxiliary shapes and leave only quads.