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

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
nice
Party member
Posts: 181
Joined: Sun Sep 15, 2013 12:17 am
Location: Sweden

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

Post by nice » Tue Feb 12, 2019 4:01 pm

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!
Attachments
removeElement.gif
removeElement.gif (101.63 KiB) Viewed 1064 times
Last edited by nice on Wed Feb 20, 2019 1:14 pm, edited 1 time in total.
:awesome: Have a good day! :ultraglee:

grump
Party member
Posts: 553
Joined: Sat Jul 22, 2017 7:43 pm

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

Post by grump » 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.

User avatar
nice
Party member
Posts: 181
Joined: Sun Sep 15, 2013 12:17 am
Location: Sweden

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

Post by nice » Wed Feb 13, 2019 8:05 am

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
:awesome: Have a good day! :ultraglee:

Post Reply

Who is online

Users browsing this forum: No registered users and 59 guests