-
Notifications
You must be signed in to change notification settings - Fork 20
Add New Ball Bonus
- Storage for the balls and constructor for a single one.
balls.current_balls = {}
balls.no_more_balls = false
function balls.new_ball( position, speed, stuck_to_platform )
return( { position = position,
speed = speed,
radius = balls.radius,
collision_counter = 0,
stuck_to_platform = stuck_to_platform,
quad = balls.quad } )
end
function balls.add_ball( single_ball )
table.insert( balls.current_balls, single_ball )
end
- Update and draw
function balls.update_ball( single_ball, dt, platform )
single_ball.position = single_ball.position + single_ball.speed * dt
if single_ball.stuck_to_platform then
balls.follow_platform( single_ball, platform )
end
end
function balls.update( dt, platform )
for _, ball in pairs( balls.current_balls ) do
balls.update_ball( ball, dt, platform )
end
balls.check_balls_escaped_from_screen()
end
function balls.draw_ball( single_ball )
love.graphics.draw( balls.image,
single_ball.quad,
single_ball.position.x - single_ball.radius,
single_ball.position.y - single_ball.radius )
end
function balls.draw()
for _, ball in pairs( balls.current_balls ) do
balls.draw_ball( ball )
end
end
- Collisions
3.1) In general: balls instead of a single ball.
function collisions.resolve_collisions( balls, platform,
walls, bricks, bonuses )
collisions.balls_platform_collision( balls, platform )
collisions.balls_walls_collision( balls, walls )
collisions.balls_bricks_collision( balls, bricks, bonuses )
collisions.platform_walls_collision( platform, walls )
collisions.platform_bonuses_collision( platform, bonuses, balls )
end
3.2) Balls-platform. Loop over balls.
function collisions.balls_platform_collision( balls, platform )
local overlap, shift_ball
local a = { x = platform.position.x,
y = platform.position.y,
width = platform.width,
height = platform.height }
for _, ball in pairs( balls.current_balls ) do
local b = { x = ball.position.x - ball.radius,
y = ball.position.y - ball.radius,
width = 2 * ball.radius,
height = 2 * ball.radius }
overlap, shift_ball =
collisions.check_rectangles_overlap( a, b )
if overlap then
balls.platform_rebound( ball, shift_ball, platform )
end
end
end
function balls.platform_rebound( single_ball, shift_ball, platform )
balls.increase_collision_counter( single_ball )
balls.increase_speed_after_collision( single_ball )
if not platform.glued then
balls.normal_rebound( single_ball, shift_ball )
balls.rotate_speed( single_ball, shift_ball, platform )
else
balls.normal_rebound( single_ball, shift_ball )
balls.rotate_speed( single_ball, shift_ball, platform )
single_ball.platform_launch_speed = single_ball.speed:clone()
single_ball.stuck_to_platform = true
single_ball.speed = vector( 0, 0 )
local platform_center = vector(
platform.position.x + platform.width / 2,
platform.position.y + platform.height / 2 )
local ball_center = single_ball.position:clone()
single_ball.separation_from_platform_center =
ball_center - platform_center
print( ball.separation_from_platform_center )
end
end
function balls.normal_rebound( single_ball, shift_ball )
local actual_shift = balls.determine_actual_shift( shift_ball )
single_ball.position = single_ball.position + actual_shift
if actual_shift.x ~= 0 then
single_ball.speed.x = -single_ball.speed.x
end
if actual_shift.y ~= 0 then
single_ball.speed.y = -single_ball.speed.y
end
end
function balls.rotate_speed( single_ball, shift_ball, platform )
local actual_shift = balls.determine_actual_shift( shift_ball )
if actual_shift.y ~= 0 then
local max_rotation_degrees = 30
local ball_center = single_ball.position
local platform_center = platform.position +
vector( platform.width / 2, platform.height / 2 )
local separation = ( ball_center - platform_center )
local rotation_deg = separation.x / ( platform.width / 2 ) *
max_rotation_degrees
local rotation_rad = rotation_deg / 180 * math.pi
single_ball.speed:rotate_inplace( rotation_rad )
end
end
function balls.increase_collision_counter( single_ball )
single_ball.collision_counter = single_ball.collision_counter + 1
end
function balls.increase_speed_after_collision( single_ball )
local speed_increase = 20
local each_n_collisions = 10
if single_ball.collision_counter ~= 0 and
single_ball.collision_counter % each_n_collisions == 0 then
single_ball.speed = single_ball.speed +
single_ball.speed:normalized() * speed_increase
end
end
3.3) Balls-walls.
Double loop in the collisions.balls_walls_collision
function collisions.balls_walls_collision( balls, walls )
local overlap, shift_ball
for _, ball in pairs( balls.current_balls ) do
local b = { x = ball.position.x - ball.radius,
y = ball.position.y - ball.radius,
width = 2 * ball.radius,
height = 2 * ball.radius }
for _, wall in pairs( walls.current_level_walls ) do
local a = { x = wall.position.x,
y = wall.position.y,
width = wall.width,
height = wall.height }
overlap, shift_ball =
collisions.check_rectangles_overlap( a, b )
if overlap then
balls.wall_rebound( ball, shift_ball )
end
end
end
end
function balls.min_angle_rebound( single_ball )
local min_horizontal_rebound_angle = math.rad( 20 )
local vx, vy = single_ball.speed:unpack()
local new_vx, new_vy = vx, vy
rebound_angle = math.abs( math.atan( vy / vx ) )
if rebound_angle < min_horizontal_rebound_angle then
new_vx = sign( vx ) * single_ball.speed:len() *
math.cos( min_horizontal_rebound_angle )
new_vy = sign( vy ) * single_ball.speed:len() *
math.sin( min_horizontal_rebound_angle )
end
single_ball.speed = vector( new_vx, new_vy )
end
3.4) Balls-bricks.
function collisions.balls_bricks_collision( balls, bricks, bonuses )
local overlap, shift_ball
for _, ball in pairs( balls.current_balls ) do
local b = { x = ball.position.x - ball.radius,
y = ball.position.y - ball.radius,
width = 2 * ball.radius,
height = 2 * ball.radius }
for i, brick in pairs( bricks.current_level_bricks ) do
local a = { x = brick.position.x,
y = brick.position.y,
width = brick.width,
height = brick.height }
overlap, shift_ball =
collisions.check_rectangles_overlap( a, b )
if overlap then
balls.brick_rebound( ball, shift_ball )
bricks.brick_hit_by_ball( i, brick, shift_ball, bonuses )
end
end
end
end
function balls.brick_rebound( single_ball, shift_ball )
balls.normal_rebound( single_ball, shift_ball )
balls.increase_collision_counter( single_ball )
balls.increase_speed_after_collision( single_ball )
end
- Changes in "game".
local balls = require "balls"
local platform = require "platform"
local bricks = require "bricks"
.....
function game.update( dt )
balls.update( dt, platform )
.....
game.check_no_more_balls( balls, lives_display )
.....
end
and so on.
4.1) Check no more balls.
function game.check_no_more_balls( balls, lives_display )
if balls.no_more_balls then
lives_display.lose_life()
if lives_display.lives < 0 then
gamestates.set_state( "gameover",
{ balls, platform, bricks,
bonuses, walls, lives_display } )
else
balls.reset()
platform.remove_bonuses_effects()
end
end
end
function balls.check_balls_escaped_from_screen()
for i, single_ball in pairs( balls.current_balls ) do
local x, y = single_ball.position:unpack()
local ball_top = y - single_ball.radius
if ball_top > love.graphics.getHeight() then
table.remove( balls.current_balls, i )
end
end
if next( balls.current_balls ) == nil then
balls.no_more_balls = true
end
end
4.2) Construct the ball at level start a.k.a. reset
function balls.reset()
balls.no_more_balls = false
for i in pairs( balls.current_balls ) do
balls.current_balls[i] = nil
end
local position = platform_starting_pos +
ball_separation_from_platform_center_on_start
local speed = vector( 0, 0 )
local platform_launch_speed = first_launch_speed:clone()
local stuck_to_platform = true
balls.add_ball(
balls.new_ball(
position, speed, platform_launch_speed, stuck_to_platform ) )
end
- Reaction on bonuses
5.1) Accelerate-decelerate
function collisions.platform_bonuses_collision( platform, bonuses, balls )
.....
if overlap then
bonuses.bonus_collected( i, bonus, balls, platform )
end
.....
end
function bonuses.bonus_collected( i, bonus, balls, platform )
.....
if bonuses.is_slowdown( bonus ) then
balls.react_on_slow_down_bonus()
elseif bonuses.is_accelerate( bonus ) then
balls.react_on_accelerate_bonus()
elseif .....
end
function balls.react_on_slow_down_bonus()
local slowdown = 0.7
for _, single_ball in pairs( balls.current_balls ) do
single_ball.speed = single_ball.speed * slowdown
end
end
function balls.react_on_accelerate_bonus()
local accelerate = 1.3
for _, single_ball in pairs( balls.current_balls ) do
single_ball.speed = single_ball.speed * accelerate
end
end
5.2) Glue and launch from platform
function balls.launch_single_ball_from_platform()
for _, single_ball in pairs( balls.current_balls ) do
if single_ball.stuck_to_platform then
single_ball.stuck_to_platform = false
single_ball.speed = single_ball.platform_launch_speed:clone()
end
break
end
end
function bonuses.bonus_collected( i, bonus, balls, platform )
if not bonuses.is_glue( bonus ) then
platform.remove_glued_effect()
balls.launch_all_glued_balls()
end
.....
end
function balls.launch_all_balls_from_platform()
for _, single_ball in pairs( balls.current_balls ) do
if single_ball.stuck_to_platform then
single_ball.stuck_to_platform = false
single_ball.speed = single_ball.platform_launch_speed:clone()
end
end
end
5.3) Add new ball bonus!!!
function bonuses.bonus_collected( i, bonus, balls, platform )
.....
elseif bonuses.is_add_new_ball( bonus ) then
balls.react_on_add_new_ball_bonus()
end
.....
end
function bonuses.is_add_new_ball( single_bonus )
local col = single_bonus.bonustype % 10
return ( col == 4 )
end
function balls.react_on_add_new_ball_bonus()
local first_ball = balls.current_balls[1]
local new_ball_position = first_ball.position:clone()
local new_ball_speed = first_ball.speed:rotated( math.pi / 4 )
local new_ball_launch_speed = first_ball.platform_launch_speed:clone()
local new_ball_stuck = first_ball.stuck_to_platform
balls.add_ball(
balls.new_ball( new_ball_position, new_ball_speed,
new_ball_launch_speed, new_ball_stuck )
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: