From a734600390574315e58caa94e4e09402ecd893fe Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Sun, 19 Jul 2026 17:41:51 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F=20pro?= =?UTF-8?q?cessBrickCollision=20=D0=B4=D0=BB=D1=8F=20=D0=B8=D0=BD=D0=BA?= =?UTF-8?q?=D0=B0=D0=BF=D1=81=D1=83=D0=BB=D1=8F=D1=86=D0=B8=D0=B8=20=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=D0=B8=D0=BA=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D1=80=D0=BA=D0=B8=20=D1=81=D1=82=D0=BE=D0=BB=D0=BA=D0=BD=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=BC=D1=8F=D1=87=D0=B0=20?= =?UTF-8?q?=D1=81=20=D0=BA=D0=B8=D1=80=D0=BF=D0=B8=D1=87=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../processBrickCollision.js | 74 +++++++++++++++++++ .../processBrickCollision.test.js | 33 +++++++++ 2 files changed, 107 insertions(+) create mode 100644 src/lib/processBrickCollision/processBrickCollision.js create mode 100644 src/lib/processBrickCollision/processBrickCollision.test.js diff --git a/src/lib/processBrickCollision/processBrickCollision.js b/src/lib/processBrickCollision/processBrickCollision.js new file mode 100644 index 0000000..ef2a3cf --- /dev/null +++ b/src/lib/processBrickCollision/processBrickCollision.js @@ -0,0 +1,74 @@ +import { PERK_DROP_CHANCE, PERK_HEIGHT, PERK_WIDTH } from '../../config'; +import { Ball } from '../../entities/ball/ball'; +import { Game } from '../../game'; +import { calculateAABBCollision } from '../calculateAABBCollision/calculateAABBCollision'; +import { calculateDirection } from '../calculateDirection/calculateDirection'; +import { spawnRandomPerk } from '../spawnRandomPerk/spawnRandomPerk'; + +/** + * Базовое взаимодействие мяча и кирпичей: меняет направление, разрушает кирпич и роняет бонус + * @param {Game} game экземпляр класса игры + * @param {Ball} ball мяч + * @returns {boolean | null} true, если произошло столкновение с кирпичом + */ +export function processBrickCollision(game, ball) { + try { + if (!(game instanceof Game)) { + throw new Error('Аргумент game должен быть экземпляром класса игра'); + } + + if (!(ball instanceof Ball)) { + throw new Error('Аргумент ball должен быть экземпляром класса мяч'); + } + + for (const brick of game.bricks) { + if (!brick.alive) { + continue; + } + + const ballObject = { + left: ball.x - ball.radius, + right: ball.x + ball.radius, + top: ball.y - ball.radius, + bottom: ball.y + ball.radius, + }; + const brickObject = { + left: brick.x, + right: brick.x + brick.width, + top: brick.y, + bottom: brick.y + brick.height, + }; + + const isCollided = calculateAABBCollision(ballObject, brickObject); + + if (!isCollided) { + continue; + } + + const directions = calculateDirection(ballObject, brickObject); + + if (directions === null) { + continue; + } + + ball.horizontalSpeed *= directions[0]; + ball.verticalSpeed *= directions[1]; + brick.kill(); + + if (!brick.alive && Math.random() < PERK_DROP_CHANCE) { + const perk = spawnRandomPerk( + brick.x + brick.width / 2 - PERK_WIDTH / 2, + brick.y + brick.height / 2 - PERK_HEIGHT / 2, + ); + game.perks.push(perk); + } + + return true; + } + + return false; + } catch (err) { + console.error(err); + return null; + } +} diff --git a/src/lib/processBrickCollision/processBrickCollision.test.js b/src/lib/processBrickCollision/processBrickCollision.test.js new file mode 100644 index 0000000..e0321fb --- /dev/null +++ b/src/lib/processBrickCollision/processBrickCollision.test.js @@ -0,0 +1,33 @@ +import { Game } from '../../game'; +import { processBrickCollision } from './processBrickCollision'; + +describe('processBrickCollision', () => { + it('Возвращает корректное значение при неверных типах аргументов', () => { + const game = new Game([[[1]]]); + + expect(processBrickCollision(null, game.ball)).toBeNull(); + expect(processBrickCollision(game, null)).toBeNull(); + }); + + it('Возвращает креектное значение если мяч не касается кирпичей', () => { + const game = new Game([[[1]]]); + game.ball.x = 5; + game.ball.y = 500; + + expect(processBrickCollision(game, game.ball)).toBe(false); + }); + + it('При столкновении разрушает кирпич и меняет направление мяча', () => { + const game = new Game([[[1]]]); + const brick = game.bricks[0]; + game.ball.x = brick.x + game.ball.radius + 1; + game.ball.y = brick.y + brick.height + game.ball.radius + 1; + game.ball.horizontalSpeed = 0; + game.ball.verticalSpeed = -10; + game.ball.moveForward(1); + + expect(processBrickCollision(game, game.ball)).toBe(true); + expect(brick.alive).toBe(false); + expect(game.ball.verticalSpeed).toBe(10); + }); +});