-
Notifications
You must be signed in to change notification settings - Fork 20
Spawning Bonuses
So far the game has been somewhat dull. To add some variety and diversity, bonuses are introduced. They provide additional challenge and create minor modifications to the gameplay. Parts 3-04 -- 3-09 are dedicated to the bonuses. This part opens the sequence by addressing a question of how bonuses appear in the game and their basic behavior.
A bonus is an object that is spawned on destruction of a brick. After it is created, it falls down with constant velocity. If we catch it with the platform, i.e. on bonus-platform collision, the effect of the bonus is applied.
There could be several bonuses on a screen simultaneously. For that reason, the basic definitions for the bonuses are similar to the bricks.
Functions for a single bonus.
local bonuses = {}
bonuses.radius = 14
bonuses.speed = vector( 0, 100 )
function bonuses.new_bonus( position, bonustype )
return( { position = position,
bonustype = bonustype,
quad = bonuses.bonustype_to_quad( bonustype ) } )
end
function bonuses.update_bonus( single_bonus )
single_bonus.position = single_bonus.position + bonuses.speed * dt
end
function bonuses.draw_bonus( single_bonus )
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
.....
return bonuses
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.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
.....
- 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
4.2) New bonus is added on brick destruction.
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
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
Feedback is crucial to improve the tutorial!
Let me know if you have any questions, critique, suggestions or just any other ideas.
Chapter 1: Prototype
- The Ball, The Brick, The Platform
- Game Objects as Lua Tables
- Bricks and Walls
- Detecting Collisions
- Resolving Collisions
- Levels
Appendix A: Storing Levels as Strings
Appendix B: Optimized Collision Detection (draft)
Chapter 2: General Code Structure
- Splitting Code into Several Files
- Loading Levels from Files
- Straightforward Gamestates
- Advanced Gamestates
- Basic Tiles
- Different Brick Types
- Basic Sound
- 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
- Improved Ball Rebounds
- Ball Launch From Platform (Two Objects Moving Together)
- Mouse Controls
- Spawning Bonuses
- Bonus Effects
- Glue Bonus
- Add New Ball Bonus
- Life and Next Level Bonuses
- Random Bonuses
- Menu Buttons
- Wall Tiles
- Side Panel
- Score
- Fonts
- More Sounds
- Final Screen
- Packaging
Appendix D: GUI Layouts
Appendix E: Love-release and Love.js
Beyond Programming: