Feature/advanced game mechanics #5
@@ -0,0 +1,54 @@
|
|||||||
|
import { Ball } from '../../entities/ball/ball';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Двигает мяч и обрабатывает отскок от стен. Возвращает true, если мяч уже отскочил или вышел за нижнюю границу
|
||||||
|
* @param {Ball} ball мяч
|
||||||
|
* @param {number} containerWidth ширина игрового контейнера в пикселях
|
||||||
|
* @param {number} containerHeight высота игрового контейнера в пикселях
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function processBall(ball, containerWidth, containerHeight) {
|
||||||
|
try {
|
||||||
|
if (!(ball instanceof Ball)) {
|
||||||
|
throw new Error('Аргумент ball должен быть экземпляром класса мяча');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof containerWidth !== 'number' || typeof containerHeight !== 'number') {
|
||||||
|
throw new Error('Значения размеров игрового контейнера должены являться числами');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (containerWidth <= 0 || containerHeight <= 0) {
|
||||||
|
throw new Error('Размеры игрового контейнера должены быть положительными числами');
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftBoundary = ball.radius;
|
||||||
|
const rightBoundary = containerWidth - ball.radius;
|
||||||
|
const topBoundary = ball.radius;
|
||||||
|
const bottomBoundary = containerHeight - ball.radius;
|
||||||
|
|
||||||
|
// Не даем мячу выйти за границы стен слева / справав и меняем направление
|
||||||
|
if (ball.x <= leftBoundary || ball.x >= rightBoundary) {
|
||||||
|
ball.x = ball.x <= leftBoundary ? leftBoundary : rightBoundary;
|
||||||
|
ball.horizontalSpeed *= -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Не даем мячу выйти за границу стены сверху и меняем направление
|
||||||
|
if (ball.y <= topBoundary) {
|
||||||
|
ball.y = topBoundary;
|
||||||
|
ball.verticalSpeed *= -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем выход за границу стены снизу
|
||||||
|
if (ball.y >= bottomBoundary) {
|
||||||
|
ball.status = 'out';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Ball } from '../../entities/ball/ball';
|
||||||
|
import { processBall } from './processBall';
|
||||||
|
|
||||||
|
describe('processBall', () => {
|
||||||
|
it('Возвращает null при неверных типах аргументов', () => {
|
||||||
|
const ball = new Ball(100, 100, 10, 3);
|
||||||
|
|
||||||
|
expect(processBall(null, 800, 600)).toBeNull();
|
||||||
|
expect(processBall(ball, '800', 600)).toBeNull();
|
||||||
|
expect(processBall(ball, -1, 600)).toBeNull();
|
||||||
|
expect(processBall(ball, 800, -1)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Возвращает false, если мяч не касается стен', () => {
|
||||||
|
const ball = new Ball(400, 300, 10, 3);
|
||||||
|
|
||||||
|
expect(processBall(ball, 800, 600)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Отражает мяч от боковой стены и возвращает true', () => {
|
||||||
|
const ball = new Ball(10, 300, 10, 3);
|
||||||
|
ball.horizontalSpeed = -10;
|
||||||
|
|
||||||
|
expect(processBall(ball, 800, 600)).toBe(true);
|
||||||
|
expect(ball.x).toBe(ball.radius);
|
||||||
|
expect(ball.horizontalSpeed).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Отражает мяч от верхней стены и возвращает true', () => {
|
||||||
|
const ball = new Ball(400, 10, 10, 3);
|
||||||
|
ball.horizontalSpeed = 0;
|
||||||
|
ball.verticalSpeed = -10;
|
||||||
|
|
||||||
|
expect(processBall(ball, 800, 600)).toBe(true);
|
||||||
|
expect(ball.y).toBe(ball.radius);
|
||||||
|
expect(ball.verticalSpeed).toBe(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Помечает мяч вышедшим за нижнюю границу', () => {
|
||||||
|
const ball = new Ball(400, 595, 10, 3);
|
||||||
|
|
||||||
|
expect(processBall(ball, 800, 600)).toBe(true);
|
||||||
|
expect(ball.status).toBe('out');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user