I have my powerup class and I've done all the code in playstate.lua to inact the powerup once it hits the paddle by generating a new ball. The problem is in my actual game, balls spawn even before the powerup is there, and all of a sudden I have like 100 balls everywhere.
I've looked over everything, and I don't see anywhere where there would be an error, especially one like 100 balls spawning when the powerup hasn't even spawnned yet.
I made my powerups class
--[[
-- Powerups Class --
Is used to call powerups that appear at random.
They fall from whatever place they were in.
If they touch the paddle then 2 more balls spawn.
If they go past the screen they are simply removed.
]]--
Powerups = Class{}
--[[
Initalize the variables
]]--
function Powerups:init(x, y)
-- input the x and y that will be used
self.x = x
self.y = y
-- set dx and dy to 0 at first, it shouldnt move
self.dx = 0
self.dy = 0
-- all powerups are 16x16
self.width = 16
self.height = 16
end
function Powerups:update(dt)
self.dy = 60
self.y = self.y + self.dy * dt
end
function Powerups:render()
love.graphics.draw(gTextures['main'], gFrames['powerups'][9], self.x, self.y)
end
-- Collision detection using AABB
function Powerups:collision(paddle)
if self.x > paddle.x + paddle.width and self.x + self.width < paddle.x then
return false
end
if self.y > paddle.y + paddle.height and self.y + self.height < paddle.y then
return false
end
return true
end
Then I implement everything in the PlayState class
--[[
GD50
Breakout Remake
-- PlayState Class --
Author: Colton Ogden
[email protected]
Represents the state of the game in which we are actively playing;
player should control the paddle, with the ball actively bouncing between
the bricks, walls, and the paddle. If the ball goes below the paddle, then
the player should lose one point of health and be taken either to the Game
Over screen if at 0 health or the Serve screen otherwise.
]]
PlayState = Class{__includes = BaseState}
--[[
We initialize what's in our PlayState via a state table that we pass between
states as we go from playing to serving.
]]
function PlayState:enter(params)
self.paddle = params.paddle
self.bricks = params.bricks
self.health = params.health
self.score = params.score
self.highScores = params.highScores
self.balls = { params.ball }
self.level = params.level
self.recoverPoints = 5000
self.powerups = {}
self.powerupSpawn = false
self.timer = 0
self.interval = 10
-- give ball random starting velocity
self.balls[1].dx = math.random(-200, 200)
self.balls[1].dy = math.random(-50, -60)
end
function PlayState:update(dt)
self.timer = self.timer + 1
if self.timer >= self.interval then
self.powerupSpawn = true
end
if self.paused then
if love.keyboard.wasPressed('space') then
self.paused = false
gSounds['pause']:play()
else
return
end
elseif love.keyboard.wasPressed('space') then
self.paused = true
gSounds['pause']:play()
return
end
-- update positions based on velocity
self.paddle:update(dt)
for k, ball in pairs(self.balls) do
ball:update(dt)
end
for k, powerup in pairs(self.powerups) do
if self.powerupSpawn == true then
powerup:update(dt)
end
if self.powerupSpawn == true then
if powerup:collision(self.paddle) == true then
for i = 0, 1 do
local ball = Ball(self.balls[1].skin)
ball.x = self.paddle.x + self.paddle.width / 2 - ball.width / 2
ball.y = self.paddle.y - ball.height
ball.dx = math.random(-200, 200)
ball.dy = math.random(-50, -60)
table.insert(self.balls, ball)
table.remove(self.powerups, k)
end
self.powerupSpawn = false
end
end
if powerup.y > VIRTUAL_HEIGHT then
table.remove(self.powerups, k)
end
end
for k, ball in pairs(self.balls) do
if ball:collides(self.paddle) then
-- raise ball above paddle in case it goes below it, then reverse dy
ball.y = self.paddle.y - 8
ball.dy = -ball.dy
--
-- tweak angle of bounce based on where it hits the paddle
--
-- if we hit the paddle on its left side while moving left...
if ball.x < self.paddle.x + (self.paddle.width / 2) and self.paddle.dx < 0 then
ball.dx = -50 + -(8 * (self.paddle.x + self.paddle.width / 2 - ball.x))
-- else if we hit the paddle on its right side while moving right...
elseif ball.x > self.paddle.x + (self.paddle.width / 2) and self.paddle.dx > 0 then
ball.dx = 50 + (8 * math.abs(self.paddle.x + self.paddle.width / 2 - ball.x))
end
gSounds['paddle-hit']:play()
end
end
-- detect collision across all bricks with the ball
for k, brick in pairs(self.bricks) do
for i, ball in pairs(self.balls) do
-- only check collision if we're in play
if brick.inPlay and ball:collides(brick) then
-- add to score
self.score = self.score + (brick.tier * 200 + brick.color * 25)
-- trigger the brick's hit function, which removes it from play
brick:hit()
-- if we have enough points, recover a point of health
if self.score > self.recoverPoints then
-- can't go above 3 health
self.health = math.min(3, self.health + 1)
-- multiply recover points by 2
self.recoverPoints = self.recoverPoints + math.min(100000, self.recoverPoints * 2)
-- make the paddle bigger, not going above size 4
self.paddle.size = math.min(4, self.paddle.size + 1)
-- play recover sound effect
gSounds['recover']:play()
end
if love.math.random(1, 3) == 1 then
local powerup = Powerups(brick.x + brick.width / 2, brick.y + brick.height / 2)
table.insert(self.powerups, powerup)
end
-- go to our victory screen if there are no more bricks left
if self:checkVictory() then
gSounds['victory']:play()
gStateMachine:change('victory', {
level = self.level,
paddle = self.paddle,
health = self.health,
score = self.score,
highScores = self.highScores,
balls = { self.balls[1] },
recoverPoints = self.recoverPoints
})
end
--
-- collision code for bricks
--
-- we check to see if the opposite side of our velocity is outside of the brick;
-- if it is, we trigger a collision on that side. else we're within the X + width of
-- the brick and should check to see if the top or bottom edge is outside of the brick,
-- colliding on the top or bottom accordingly
--
-- left edge; only check if we're moving right, and offset the check by a couple of pixels
-- so that flush corner hits register as Y flips, not X flips
if ball.x + 2 < brick.x and ball.dx > 0 then
-- flip x velocity and reset position outside of brick
ball.dx = -ball.dx
ball.x = brick.x - 8
-- right edge; only check if we're moving left, , and offset the check by a couple of pixels
-- so that flush corner hits register as Y flips, not X flips
elseif ball.x + 6 > brick.x + brick.width and ball.dx < 0 then
-- flip x velocity and reset position outside of brick
ball.dx = -ball.dx
ball.x = brick.x + 32
-- top edge if no X collisions, always check
elseif ball.y < brick.y then
-- flip y velocity and reset position outside of brick
ball.dy = -ball.dy
ball.y = brick.y - 8
-- bottom edge if no X collisions or top collision, last possibility
else
-- flip y velocity and reset position outside of brick
ball.dy = -ball.dy
ball.y = brick.y + 16
end
-- slightly scale the y velocity to speed up the game, capping at +- 150
if math.abs(ball.dy) < 150 then
ball.dy = ball.dy * 1.02
end
-- only allow colliding with one brick, for corners
break
end
end
end
-- if ball goes below bounds, revert to serve state and decrease health
for k, ball in pairs(self.balls) do
if ball.y >= VIRTUAL_HEIGHT then
table.remove(self.balls, k)
self.health = self.health - 1
-- make the paddle a size smaller
self.paddle.size = math.max(1, self.paddle.size - 1)
gSounds['hurt']:play()
if self.health == 0 then
gStateMachine:change('game-over', {
score = self.score,
highScores = self.highScores
})
else
gStateMachine:change('serve', {
paddle = self.paddle,
bricks = self.bricks,
health = self.health,
score = self.score,
highScores = self.highScores,
level = self.level,
recoverPoints = self.recoverPoints
})
end
end
end
-- for rendering particle systems
for k, brick in pairs(self.bricks) do
brick:update(dt)
end
for k, ball in pairs(self.balls) do
ball:update(dt)
end
if love.keyboard.wasPressed('escape') then
love.event.quit()
end
end
function PlayState:render()
-- render bricks
for k, brick in pairs(self.bricks) do
brick:render()
end
for k, ball in pairs(self.balls) do
ball:render()
end
for k, powerup in pairs(self.powerups) do
powerup:render()
end
-- render all particle systems
for k, brick in pairs(self.bricks) do
brick:renderParticles()
end
self.paddle:render()
renderScore(self.score)
renderHealth(self.health)
-- pause text, if paused
if self.paused then
love.graphics.setFont(gFonts['large'])
love.graphics.printf("PAUSED", 0, VIRTUAL_HEIGHT / 2 - 16, VIRTUAL_WIDTH, 'center')
end
end
function PlayState:checkVictory()
for k, brick in pairs(self.bricks) do
if brick.inPlay then
return false
end
end
return true
end
Powerups:collisionis incorrect:andshould be replaced withor