From b1bc1263da64d2584318fb90cf56eb167cde8d3e Mon Sep 17 00:00:00 2001 From: Dominik Niemiro Date: Sat, 25 Jun 2022 18:11:15 +0200 Subject: [PATCH 1/5] Add side paddle collisions --- collisions.lua | 37 ++++++++++++++++++++++++++++++++++++- main.lua | 25 +++++++++++++++++++------ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/collisions.lua b/collisions.lua index 914345d..cf80b97 100644 --- a/collisions.lua +++ b/collisions.lua @@ -289,6 +289,39 @@ local function calculateBallPaddleCollisions(ball, newBallPosition, paddle) table.insert(collisions, { point = collisionPoint, newAngle = newAngle, type = TypeEnum.PADDLE }) end + + -- horizontal collisions + if ball.position.y + ball.radius > paddle.position.y - paddle.height / 2 and ball.position.y - ball.radius <= paddle.position.y + paddle.height / 2 then + + local yOffset = (ball.position.y - paddle.position.y) / (paddle.height) + local velocityBoost = 1.3 + local leftDegs = 180 + local rightDegs = 0 + + if ball.position.x < paddle.position.x and ball.position.x + ball.radius > paddleLeft.x then + local degs = leftDegs - angleRange * yOffset + local dVelocity + if degs < leftDegs + 1.0 and degs > leftDegs - 1.0 then newAngle = math.rad(leftDegs + 5) else newAngle = math.rad(degs) end + if paddle.velocity.x < 0 and degs < leftDegs + angleRange then + dVelocity = math.max(ball.dVelocity, math.abs(paddle.dVelocity) * velocityBoost) + end + table.insert(collisions, { point = Vector.new(paddleLeft.x - ball.radius, ball.position.y), newAngle = newAngle, type = TypeEnum.PADDLE, newDVelocity = dVelocity }) + end + + if ball.position.x > paddle.position.x and ball.position.x - ball.radius < paddleRight.x then + local degs = rightDegs + angleRange * yOffset + local dVelocity + if degs < rightDegs + 1.0 and degs > rightDegs - 1.0 then newAngle = math.rad(rightDegs - 5) else newAngle = math.rad(degs) end + if paddle.velocity.x > 0 and degs > -angleRange then + dVelocity = math.max(ball.dVelocity, math.abs(paddle.dVelocity) * velocityBoost) + end + + table.insert(collisions, { point = Vector.new(paddleRight.x + ball.radius, ball.position.y), newAngle = newAngle, type = TypeEnum.PADDLE, newDVelocity = dVelocity }) + end + + end + + return findClosestCollision(ball.position, collisions) end @@ -325,6 +358,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) @@ -336,7 +371,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 diff --git a/main.lua b/main.lua index a50aef0..e48dcf5 100644 --- a/main.lua +++ b/main.lua @@ -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", @@ -64,7 +65,7 @@ local ball = { height = 22, position = Vector.new(), velocity = Vector.new(), - dVelocity = 300, + dVelocity = startBalldVelocity, } @@ -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() @@ -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 @@ -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()) From d9ac0e569f79b3c41fe2e410b9a0b3f81593751a Mon Sep 17 00:00:00 2001 From: Dominik Niemiro Date: Sat, 25 Jun 2022 20:25:58 +0200 Subject: [PATCH 2/5] Fix ball going through the walls --- collisions.lua | 52 +++++++++++++++++++++++++++++++++++++++++++++++++- main.lua | 21 ++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/collisions.lua b/collisions.lua index cf80b97..2708bb5 100644 --- a/collisions.lua +++ b/collisions.lua @@ -325,6 +325,34 @@ local function calculateBallPaddleCollisions(ball, newBallPosition, paddle) 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 + if ballVelocityDegs >= leftDegs and ballVelocityDegs < leftDegs + 25 then + local diff = ballVelocityDegs - leftDegs + change = -math.max(0, 15 - math.abs(diff)) + end + + if ballVelocityDegs <= rightDegs and ballVelocityDegs > rightDegs - 25 then + local diff = ballVelocityDegs - rightDegs + change = math.max(0, 15 - math.abs(diff)) + end + + 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 @@ -342,7 +370,29 @@ 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) diff --git a/main.lua b/main.lua index e48dcf5..2bc7674 100644 --- a/main.lua +++ b/main.lua @@ -294,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()) @@ -383,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) From 4ef59ab37da7865694ac7a45c35c514a9f1693f2 Mon Sep 17 00:00:00 2001 From: Dominik Niemiro Date: Sun, 26 Jun 2022 08:19:05 +0200 Subject: [PATCH 3/5] Reduce code reuse in the horizontal collisions section --- collisions.lua | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/collisions.lua b/collisions.lua index 2708bb5..11e3b29 100644 --- a/collisions.lua +++ b/collisions.lua @@ -291,32 +291,44 @@ local function calculateBallPaddleCollisions(ball, newBallPosition, paddle) -- horizontal collisions - if ball.position.y + ball.radius > paddle.position.y - paddle.height / 2 and ball.position.y - ball.radius <= paddle.position.y + paddle.height / 2 then - - local yOffset = (ball.position.y - paddle.position.y) / (paddle.height) - local velocityBoost = 1.3 - local leftDegs = 180 - local rightDegs = 0 + 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 ball.position.x < paddle.position.x and ball.position.x + ball.radius > paddleLeft.x then - local degs = leftDegs - angleRange * yOffset - local dVelocity - if degs < leftDegs + 1.0 and degs > leftDegs - 1.0 then newAngle = math.rad(leftDegs + 5) else newAngle = math.rad(degs) end - if paddle.velocity.x < 0 and degs < leftDegs + angleRange then - dVelocity = math.max(ball.dVelocity, math.abs(paddle.dVelocity) * velocityBoost) - end - table.insert(collisions, { point = Vector.new(paddleLeft.x - ball.radius, ball.position.y), newAngle = newAngle, type = TypeEnum.PADDLE, newDVelocity = dVelocity }) + if ballXInsideRightPaddleSide then + rightDegs = 0 end - if ball.position.x > paddle.position.x and ball.position.x - ball.radius < paddleRight.x then - local degs = rightDegs + angleRange * yOffset + 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 - if degs < rightDegs + 1.0 and degs > rightDegs - 1.0 then newAngle = math.rad(rightDegs - 5) else newAngle = math.rad(degs) end - if paddle.velocity.x > 0 and degs > -angleRange then + local paddleMovesToBall = (leftDegs ~= nil and paddle.velocity.x < 0) or (rightDegs ~= nil and paddle.velocity.x > 0) + if paddleMovesToBall then + local velocityBoost = 1.3 dVelocity = math.max(ball.dVelocity, math.abs(paddle.dVelocity) * velocityBoost) end - table.insert(collisions, { point = Vector.new(paddleRight.x + ball.radius, ball.position.y), newAngle = newAngle, type = TypeEnum.PADDLE, newDVelocity = dVelocity }) + 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 From d92a9841208789b49d19a87c9c4f26f2fb88c279 Mon Sep 17 00:00:00 2001 From: Dominik Niemiro Date: Sun, 26 Jun 2022 08:25:11 +0200 Subject: [PATCH 4/5] Add some comments --- collisions.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/collisions.lua b/collisions.lua index 11e3b29..e930941 100644 --- a/collisions.lua +++ b/collisions.lua @@ -344,16 +344,19 @@ local function calculateFlatAngleWallCollision(ball, wallCollision) 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) @@ -391,7 +394,6 @@ local function calculateBallWallsCollisions(ball, newBallPosition, window) 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 } From 3d9ed4ef1f8642bb236ad11ef8bdc7aae3c5cf64 Mon Sep 17 00:00:00 2001 From: Dominik Niemiro Date: Sun, 26 Jun 2022 08:39:35 +0200 Subject: [PATCH 5/5] Add one missing condition --- collisions.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/collisions.lua b/collisions.lua index e930941..c08997e 100644 --- a/collisions.lua +++ b/collisions.lua @@ -323,7 +323,8 @@ local function calculateBallPaddleCollisions(ball, newBallPosition, paddle) -- 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) - if paddleMovesToBall then + 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