Skip to content

Add New Ball Bonus

noooway edited this page Mar 8, 2017 · 38 revisions
  1. 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
  1. 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
  1. 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
  1. 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
  1. 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

    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