Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 102 additions & 2 deletions collisions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,86 @@ local function calculateBallPaddleCollisions(ball, newBallPosition, paddle)
table.insert(collisions, { point = collisionPoint, newAngle = newAngle, type = TypeEnum.PADDLE })
end


-- horizontal collisions
local ballYInsidePaddleYRange = (
ball.position.y + ball.radius > paddle.position.y and
ball.position.y - ball.radius <= paddle.position.y + paddle.height / 2
)
if ballYInsidePaddleYRange then

local ballXInsideLeftPaddleSide = ball.position.x < paddle.position.x and ball.position.x + ball.radius > paddleLeft.x
local ballXInsideRightPaddleSide = ball.position.x > paddle.position.x and ball.position.x - ball.radius < paddleRight.x
local leftDegs, rightDegs

if ballXInsideLeftPaddleSide then
leftDegs = 180
end

if ballXInsideRightPaddleSide then
rightDegs = 0
end

if leftDegs ~= nil or rightDegs ~= nil then
local yOffset = (ball.position.y - paddle.position.y) / (paddle.height)
local horizontalDegs = leftDegs or rightDegs

local sign
if leftDegs ~= nil then sign = -1 else sign = 1 end

-- compute new angle - in case it is 0 or 180 degs, lift it up 5 degrees
local degs = horizontalDegs + (angleRange * yOffset) * sign
if degs < horizontalDegs + 1.0 and degs > horizontalDegs - 1.0 then newAngle = math.rad(horizontalDegs - (5 * sign)) else newAngle = math.rad(degs) end

-- if paddle speeds towards the ball, increase the ball's velocity
local dVelocity
local paddleMovesToBall = (leftDegs ~= nil and paddle.velocity.x < 0) or (rightDegs ~= nil and paddle.velocity.x > 0)
local angleInRange = (leftDegs ~= nil and degs < leftDegs + angleRange) or (rightDegs ~= nil and degs > -angleRange)
if paddleMovesToBall and angleInRange then
local velocityBoost = 1.3
dVelocity = math.max(ball.dVelocity, math.abs(paddle.dVelocity) * velocityBoost)
end

table.insert(collisions, { point = Vector.new((paddle.position.x + (paddle.width / 2) * sign) + ball.radius * sign, ball.position.y), newAngle = newAngle, type = TypeEnum.PADDLE, newDVelocity = dVelocity })
end

end


return findClosestCollision(ball.position, collisions)
end

local function calculateFlatAngleWallCollision(ball, wallCollision)
local ballVelocityDegs = math.deg(ball.velocity:getAngle())
local leftDegs = -180
local rightDegs = 0

local change
local newAngle
-- check if the ball goes too flat in a top-left direction
if ballVelocityDegs >= leftDegs and ballVelocityDegs < leftDegs + 25 then
local diff = ballVelocityDegs - leftDegs
change = -math.max(0, 15 - math.abs(diff))
end

-- check if the ball goes too flat in a top-right direction
if ballVelocityDegs <= rightDegs and ballVelocityDegs > rightDegs - 25 then
local diff = ballVelocityDegs - rightDegs
change = math.max(0, 15 - math.abs(diff))
end

-- boost the angle
if change and wallCollision then
local newVelocity = ball.velocity:copy()
newVelocity:reflect(wallCollision.normal)
local angle = newVelocity:getAngle()

newAngle = math.rad(math.deg(angle) + change)
end

return newAngle
end

local function calculateBallWallsCollisions(ball, newBallPosition, window)
local minX = 0
local maxX = window.width
Expand All @@ -309,7 +386,28 @@ local function calculateBallWallsCollisions(ball, newBallPosition, window)
{ pos0 = topRightCorner, pos1 = bottomRightCorner, normal = left }, -- right wall
}

return calculateBallEdgesCollision(ball, newBallPosition, walls, TypeEnum.WALL)
local closestWallCollision = calculateBallEdgesCollision(ball, newBallPosition, walls, TypeEnum.WALL)

-- change ball velocity angle when too "flat" on wall hit
local newAngle = calculateFlatAngleWallCollision(ball, closestWallCollision)
if newAngle then
closestWallCollision.newAngle = newAngle
closestWallCollision.normal = nil
end

-- prevent ball going through left border
if ball.position.x - ball.radius < bottomLeftCorner.x then
return { point = Vector.new(bottomLeftCorner.x + ball.radius, ball.position.y), normal=left, type = TypeEnum.Wall }
end

-- prevent ball going through left border
if ball.position.x + ball.radius > bottomRightCorner.x then
return { point = Vector.new(bottomRightCorner.x - ball.radius, ball.position.y), normal=right, type = TypeEnum.Wall }
end



return closestWallCollision
end

function module.calculateNextCollision(ball, newBallPosition, bricks, paddle, window)
Expand All @@ -325,6 +423,8 @@ function module.calculateNextCollision(ball, newBallPosition, bricks, paddle, wi

local newVelocity = ball.velocity:copy()

local newDVelocity = collision.newDVelocity

if collision.normal then
-- calculate reflected velocity using normal
newVelocity:reflect(collision.normal)
Expand All @@ -336,7 +436,7 @@ function module.calculateNextCollision(ball, newBallPosition, bricks, paddle, wi
error()
end

return collision.point, newVelocity, collision.hitElement, collision.type
return collision.point, newVelocity, collision.hitElement, collision.type, newDVelocity
end

return module
46 changes: 40 additions & 6 deletions main.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ local paddle = {
hitSound = love.audio.newSource("assets/sounds/soft-bang.ogg", "static")
}

local startBalldVelocity = 300
local ball = {
imagePath = "assets/png/ballBlue.png",
spriteName = "ball",
Expand All @@ -64,7 +65,7 @@ local ball = {
height = 22,
position = Vector.new(),
velocity = Vector.new(),
dVelocity = 300,
dVelocity = startBalldVelocity,
}


Expand Down Expand Up @@ -162,6 +163,7 @@ end
local function resetBall()
ball.position:setXY(window.width / 2, paddle.position.y - (ball.height + 8))
ball.velocity:setXY(0, 1)
ball.dVelocity = startBalldVelocity
end

local function resetPaddle()
Expand Down Expand Up @@ -226,17 +228,23 @@ local function updateBall(dt)
return
end

local collisionPoint, newVelocity, hitElement, collisionType = Collisions.calculateNextCollision(ball,
newBallPosition, bricks, paddle, window)
local collisionPoint, newVelocity, hitElement, collisionType, newDVelocity = Collisions.calculateNextCollision(
ball,newBallPosition, bricks, paddle, window)


if not collisionPoint then
-- no collision detected, simply move ball
-- no collision detected

-- slow the ball boosted velocity
local slowingStages = 20
if ball.dVelocity ~= startBalldVelocity and ball.position.y < paddle.position.y then
ball.dVelocity = ( (slowingStages - 1) * ball.dVelocity + startBalldVelocity) / slowingStages
end

-- move the ball
ball.position:transform(dBallPosition)
return true
else

if collisionType == Collisions.TypeEnum.WALL then
if wallSound:isPlaying() then wallSound2:play() else wallSound:play() end
elseif collisionType == Collisions.TypeEnum.BRICK then
Expand All @@ -262,9 +270,14 @@ local function updateBall(dt)
end
end

-- apply new reflected velocity
-- apply new reflected velocity and dVelocity
ball.velocity = newVelocity

if newDVelocity then
ball.dVelocity = newDVelocity
end


-- calculate remaining dt after the collision
local distanceTravelled = (collisionPoint - ball.position):getLength()
dt = dt * (1 - distanceTravelled / dBallPosition:getLength())
Expand All @@ -281,6 +294,22 @@ local function updateBall(dt)
end
end

local function paddleSqueezesBallLeft()
local paddleLeftX = paddle.position.x - paddle.width / 2
local ballBottomY = ball.position.y + ball.radius
local paddleTopY = paddle.position.y - paddle.height / 2

return ball.position.x <= ball.width and paddleLeftX <= ball.width and ball.position.y <= paddle.position.y and ballBottomY >= paddleTopY
end

local function paddleSqueezesBallRight()
local paddleRightX = paddle.position.x + paddle.width / 2
local ballBottomY = ball.position.y + ball.radius
local paddleTopY = paddle.position.y - paddle.height / 2

return ball.position.x + ball.width >= window.width and paddleRightX >= window.width - ball.width and ball.position.y <= paddle.position.y and ballBottomY >= paddleTopY
end

function love.load()
-- init random seed
math.randomseed(os.time())
Expand Down Expand Up @@ -370,6 +399,11 @@ function love.update(dt)
end
end

-- bounce paddle if it's to squeeze the ball to the wall
if paddleSqueezesBallLeft() or paddleSqueezesBallRight() then
paddle.velocity = paddle.velocity * (-0.7)
end

-- move paddle
paddle.position:transform(paddle.velocity * paddle.dVelocity * dt)

Expand Down