Page 1 of 1

[SOLVED] (breakout clone) powered up big ball doesn't remove several bricks at once

Posted: Tue Feb 12, 2019 4:01 pm
by nice
Hello everyone!
I'm currently working on a power-up for my endless breakout clone where the ball becomes big (with the intent to remove several bricks at once). Upon collision, the big ball collides with a set of 3 bricks. when the ball collides with the bricks, it removes the first and the third bricks but not the second(see attached gif for a visual explanation) IMPORTANT: the attached GIF has flashing colors!

What I actually want to happen is that when the Ball collides, is that it removes all three bricks at once. The bricks are part of a table. If I've understood my problem correctly, it removes the elements and moves the "list" of Bricks into a new "position" in that list.

Ball.lua

Code: Select all

Player = require "Player"
Bricks = require "Brick"
powerUp = require "Powerup"

numActiveBalls = 1

Balls = {}
ballsController = {}
ballsController.balls = {}
--bonusBall = {}

ballSprite = love.graphics.newImage("ball.png")

function ballsController.spawnBall(posX, posY)
    Ball = {}
    Ball.ballPosX = Player.posX - 8 -- posX
    Ball.ballPosY = Player.posY - 16 --posY
    Ball.ballOriginX = 0
    Ball.ballOriginY = 0
    Ball.ballOrientation = 0
    Ball.ballScaleFactor = 1
    Ball.ballWidth = ballSprite:getWidth()
    Ball.ballHeight = ballSprite:getHeight()
    Ball.ballSpeed = 300
    Ball.ballMaxSpeed = 580
    love.math.setRandomSeed(love.timer.getTime())
    Ball.ballDirX = 0
    Ball.ballDirY = 1
    Ball.ballClampRoof = 0
    Ball.ballClampLeft = 0
    Ball.isBallAlive = true
    Ball.stuckToPlayer = true
    Ball.extraSizeFactor = 1

    table.insert(ballsController.balls, Ball)
end

function Balls:ballStart()
  if (Ball.stuckToPlayer) then
    Ball.ballPosX = Player.posX - 8
    Ball.ballPosY = Player.posY - 16
  end
end

function Balls:handlePlayerBounce(isPlayer)
  local playerHalfWidth = Player.Sprite:getWidth() * 0.5
  local playerHalfHeight = Player.Sprite:getHeight() * 0.5
  local playerPosX = 0
  local playerPosY = 0
  local playerOriginOffset = 35

  if isPlayer then
    playerPosX = Player.posX
    playerPosY = Player.posY + playerOriginOffset -- ball origin offset
  end

  local playerToBallX = Ball.ballPosX + Ball.ballWidth * 0.5 - playerPosX
  local playerToBallY = Ball.ballPosY - playerPosY
  local ballToPlayerLength = math.sqrt(playerToBallX * playerToBallX +
                          playerToBallY * playerToBallY)

  Ball.ballDirX = playerToBallX / ballToPlayerLength
  Ball.ballDirY = playerToBallY / ballToPlayerLength

end

function Balls:handleBrickBounce()
  Ball.ballDirY = - Ball.ballDirY
end

function Balls:ballHitRoof()
  if Ball.ballPosY < Ball.ballClampRoof then
    Ball.ballPosY = ballSprite:getHeight()
    Ball.ballDirY = -Ball.ballDirY
  end
end

function Balls:ballHitLeft()
  if Ball.ballPosX < 0 then
    Ball.ballPosX = 0
    Ball.ballDirX = -Ball.ballDirX
  end
end

function Balls:ballHitRight()

-- When the Ball collides with the right wall, change direction
if Ball.ballPosX + ballSprite:getWidth() * Ball.extraSizeFactor >
   Window.Width then
    Ball.ballDirX = -Ball.ballDirX
  end
end

function Balls:ballHitPlayer()
  local playerHalfWidth = Player.Sprite:getWidth() * 0.5
  local playerHalfHeight = Player.Sprite:getHeight() * 0.5
  local ballWidth = ballSprite:getWidth() * Ball.extraSizeFactor
  local ballHeight = ballSprite:getHeight() * Ball.extraSizeFactor

  if Ball.ballPosX <=
     Player.posX + playerHalfWidth and
     Ball.ballPosY + ballHeight >= Player.posY and
     Ball.ballPosY < Player.posY + Player.Height and
     Ball.ballPosX + ballWidth >= Player.posX - playerHalfWidth then
        Balls:handlePlayerBounce(true)
  end
end

function Balls:ballHitBrick()
  local brickWidth = brickSprite:getWidth()
  local brickHeight = brickSprite:getHeight()
  local ballWidth = ballSprite:getWidth() * Ball.extraSizeFactor
  local ballHeight = ballSprite:getHeight() * Ball.extraSizeFactor
  local hasCollided = false
 
  -- Here's where the Balls collision with Bricks happens
  for i,b in ipairs(bricksLineController.bricks) do
    if ((Ball.ballPosX <= (b.brickPosX + brickWidth)) and
        ((Ball.ballPosX + ballWidth) >= b.brickPosX) and
       ((Ball.ballPosY + ballHeight) >= b.brickPosY) and
       (Ball.ballPosY < b.brickPosY + brickHeight)) then

        
         if hasCollided == false then
           Balls:handleBrickBounce()
           hasCollided = true
         end

         --print("Ball hit Brick")

         if b.isPowerUp then
           powerUpsController:spawnPowerUp(b.brickPosX, b.brickPosY)
         end


         -- Highscore multiplier, multiplier will increase by 1 every 5th Brick
         Manager.countToTriggerMultiplier = Manager.countToTriggerMultiplier + 1
         if Manager.countToTriggerMultiplier >= Manager.triggerMultiplier then
           Manager.hasMultiplier = true
           Manager.scoreMultiplier = Manager.scoreMultiplier + 1
           Manager.countToTriggerMultiplier = 0
         end
         Manager.Highscore = Manager.Highscore + 10 * Manager.scoreMultiplier
    end
  end
  
  
  --[[
  local isColliding = true
  while(isColliding) do
    Ball.ballPosX = Ball.ballPosX + (Ball.ballDirX * Ball.ballSpeed * 0.01)
    Ball.ballPosY = Ball.ballPosY + (Ball.ballDirY * Ball.ballSpeed * 0.01)
    isColliding = (Ball.ballPosX <= (b.brickPosX + brickWidth)) and
                  ((Ball.ballPosX + ballWidth) >= b.brickPosX) and
                  ((Ball.ballPosY + ballHeight) >= b.brickPosY) and
                  (Ball.ballPosY < b.brickPosY + brickHeight)
  end
  ]]
end

-- Update Ball if it's outside the screen
function Balls:ballReset()
  if Ball.ballPosY > Window.Height then
    -- Reset the Ball and Multiplier
    ballsController.spawnBall(Ball)
    Manager.hasMultiplier = false
    Manager.scoreMultiplier = 1
    Manager.countToTriggerMultiplier = 0

  end
end

function Balls:update(dt)

  Balls:ballStart()
  -- Update "collisions" between the game window, bricks and player
  Balls:ballHitRoof()
  Balls:ballHitLeft()
  Balls:ballHitRight()
  Balls:ballHitPlayer()
  Balls:ballHitBrick()
  Balls:ballReset()

  -- note: create a "tag" system so
  if isPowerUpActive == true then
    -- When the power-up is active, start counting down from 30
    powerUpTimer = powerUpTimer - love.timer.getDelta()
  end
  -- When the timer has reached 0, reset the Ball size
  if powerUpTimer <= 0 then
    isPowerUpActive = false
    Ball.extraSizeFactor = 1
    Ball.ballScaleFactor = 1
    powerUpTimer = addTime
  end

  -- Manages several balls
  for _,b in pairs(ballsController.balls) do
    b.ballPosX = b.ballPosX + (b.ballDirX * b.ballSpeed * dt)
    b.ballPosY = b.ballPosY + (b.ballDirY * b.ballSpeed * dt)
  end

end

function Balls:draw()
  -- Draws several balls
  for _, b in pairs(ballsController.balls) do
    love.graphics.draw(ballSprite,
                       b.ballPosX,
                       b.ballPosY,
                       b.ballOrientation,
                       b.ballScaleFactor,
                       b.ballScaleFactor,
                       b.ballOriginX,
                       b.ballOriginY)
  end
end

function Balls:keypressed(key)
  -- Makes the ball "unstuck" from player
  if key == "space" then
    Ball.stuckToPlayer = false
    print("In Ball:keypressed")
  end
end

return Balls
Powerup.lua

Code: Select all

Brick = require "Brick"
--Ball = require "Ball"
Player = require "Player"

powerUpDoubleBallSprite = love.graphics.newImage("doubleBallSprite.png")
powerUpDoubleBallColor = {}
powerUpBigBallSprite = love.graphics.newImage("bigBallSprite.png")

isPowerUpActive = false

addTime = 30
powerUpTimer = addTime
powerUpsController = {}
powerUpsController.powerups = {}

function powerUpsController:spawnPowerUp(posX, posY)
  powerUp = {}
  powerUp.powerUpPosX = posX
  powerUp.powerUpPosY = posY
  powerUp.powerUpOrientation = 0
  powerUp.powerUpScaleX = 1
  powerUp.powerUpScaleY = 1
  powerUp.powerUpOriginX = 0
  powerUp.powerUpOriginY = 0
  powerUp.powerUpVelocity = 2
  powerUp.powerUpDoubleX = Ball.ballPosX
  powerUp.powerUpDoubleY = Ball.ballPosY
  powerUp.powerUpAddSizeFactor = 9
  local randomizePowerUp = 1
    if randomizePowerUp == 1 then
      powerUp.setRandomPowerUp = powerUpBigBallSprite

    end

  table.insert(powerUpsController.powerups, powerUp)

end

function powerUpsController:doubleBall()
  --[[

  if something then
    ballsController.spawnBall(Ball) -- But should it be here then?
  end
  ]]
end

function powerUpsController:bigBall()
    Ball.extraSizeFactor = powerUp.powerUpAddSizeFactor
    Ball.ballScaleFactor = Ball.extraSizeFactor
end

function powerUpsController:update(dt)

  local playerHalfWidth = Player.Width * 0.5
  local playerHalfHeight = Player.Height * 0.5
  local randomizePowerUp = 1
-- Removes power-up if it moves "outside" of the screen
  for i, p in pairs(powerUpsController.powerups) do
    -- Moves the power-up positively on Y

    p.powerUpPosY = p.powerUpPosY + p.powerUpVelocity
    local powerUpWidth = p.setRandomPowerUp:getWidth()
    local powerUpHeight = p.setRandomPowerUp:getHeight()

    if ((p.powerUpPosX <= (Player.posX + playerHalfWidth)) and
       ((p.powerUpPosX + powerUpWidth) >= Player.posX - playerHalfWidth) and
       ((p.powerUpPosY + powerUpHeight) >= Player.posY) and
       (p.powerUpPosY < Player.posY + playerHalfHeight)) then
         isPowerUpActive = true
        if p.setRandomPowerUp == powerUpBigBallSprite then
          powerUpsController.bigBall()
        end

         -- Adds 1000 points to Highscore and removes power-up
         Manager.Highscore = Manager.Highscore + 1000
         table.remove(powerUpsController.powerups, i)

    end

    -- Removes power-up if it reaches a certain point
    if p.powerUpPosY >= 950 then
      table.remove(powerUpsController.powerups, i)
    end
  end

end

function powerUpsController:draw()
  --[[
     Depending on what power-up that spawns, change 'setRandomUp' (which is nil)
     into the appropriate Sprite.
    ]]

    if isPowerUpActive == true then
      love.graphics.print(math.floor(powerUpTimer),
                          love.graphics.getWidth() * 0.5,
                          love.graphics.getHeight() - 50)
    elseif powerUpTimer <= 0 then
      isPowerUpActive = false
    end
    for _,p in ipairs(powerUpsController.powerups) do
      love.graphics.setColor(1, 0, 0, 1)
      love.graphics.rectangle("fill", p.powerUpPosX, p.powerUpPosY, 4, 4)
      love.graphics.setColor(1, 1, 1, 1)
      love.graphics.draw(p.setRandomPowerUp,
                        p.powerUpPosX,
                        p.powerUpPosY,
                        p.powerUpOrientation,
                        p.powerUpScaleX,
                        p.powerUpScaleY,
                        p.powerUpOriginX,
                        p.powerUpOriginY)
    end

  end

  --love.graphics.setColor(1, 1, 1, 1)


return powerUpsController
Brick.lua

Code: Select all

Player = require "Player"

Bricks = {}
-- Find a method to place next set of brick lines directly the first one?
bricksLineTimer = 2
bricksLineController = {}
bricksLineController.bricks = {}
-- Moved this variables outside of Controller to make the bricks usable
brickSprite = love.graphics.newImage("brick.png")

function bricksLineController.spawnBrick(posX, posY)
  Brick = {}
  --Brick.brickSprite = love.graphics.newImage("brick.png")
  Brick.brickPosX = posX
  Brick.brickPosY = posY
  Brick.brickWidth = brickSprite:getWidth()
  Brick.brickHeight = brickSprite:getHeight()
  Brick.brickOriginX = 0
  Brick.brickOriginY = 0
  Brick.brickSpeed = 0.009
  if love.math.random(1, 5) < 5 then
    --print("YES")
    Brick.isPowerUp = true
  else
    Brick.isPowerUp = false
    --print("NO")
  end

  table.insert(bricksLineController.bricks, Brick)
end

function Bricks:load()
end

function Bricks:update(dt)


  bricksLineTimer = bricksLineTimer - 0.01
  brickHighestYPos = 1000
  for _, b in pairs(bricksLineController.bricks) do
    if b.brickPosY < brickHighestYPos then
      brickHighestYPos = b.brickPosY - 23
    end
  end
  --print("Brick Pos Y: "..brickHighestYPos)
  if( brickHighestYPos > -24) then
    for i = 0, 16 do
      for j = 1, 1 do
        -- 'i' is the one that spawns the bricks
        -- 'j' is the one that sets the height of the bricks
        -- Start spawning brick lines here?
        -- and maybe spawn 3 - 4 lines ahead?
        bricksLineController.spawnBrick(i * 50, -24)
      end
    end
  end
  for _,b in pairs(bricksLineController.bricks) do
    b.brickPosY = b.brickPosY + b.brickSpeed
  end
  for i, b in ipairs(bricksLineController.bricks) do
    if b.brickPosY >= 900 then
      table.remove(bricksLineController.bricks, i)
      print("Removed BRICKS")
    end
  end
end

function Bricks:draw()
  local powerUpBrickColor = {math.random(0, 255) / 255,
                            math.random(0, 255) / 255,
                            math.random(0, 255) / 255,
                            1}

  for _, b in ipairs(bricksLineController.bricks) do
    if b.isPowerUp then
      love.graphics.setColor(powerUpBrickColor)
    else
      love.graphics.setColor(1, 1, 1, 1)
    end
    love.graphics.draw(brickSprite,
                       b.brickPosX,
                       b.brickPosY,
                       0, 1, 1)
  end

  love.graphics.setColor(1, 1, 1, 1)

end

return Bricks

I thank you for your patience, if there's something that's unclear or needs to be specified better, please let me know!

Re: [HELP] (breakout clone) powered up big ball doesn't remove several bricks at once

Posted: Tue Feb 12, 2019 5:03 pm
by grump
When you remove table entries in a loop like this, entries will be skipped. The simplest solution is to iterate backwards over the bricks when your remove them.

Re: [SOLVED] (breakout clone) powered up big ball doesn't remove several bricks at once

Posted: Wed Feb 13, 2019 8:05 am
by nice
grump wrote: Tue Feb 12, 2019 5:03 pm When you remove table entries in a loop like this, entries will be skipped. The simplest solution is to iterate backwards over the bricks when your remove them.
it is as you said, with a little help from a programmer friend to explain exactly it works by using this:

Code: Select all

  for i = #bricksLineController.bricks, 1, -1 do
      local b = bricksLineController.bricks[i]
      if ((Ball.ballPosX <= (b.brickPosX + brickWidth)) and
        ((Ball.ballPosX + ballWidth) >= b.brickPosX) and
        ((Ball.ballPosY + ballHeight) >= b.brickPosY) and
        (Ball.ballPosY < b.brickPosY + brickHeight)) then