Compare commits
10
Commits
1305e5b64e
...
ecc3ea1e34
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecc3ea1e34 | ||
|
|
a405a91f65 | ||
|
|
b0bbb6df96 | ||
|
|
fbdd865da8 | ||
|
|
b2a76b0c74 | ||
|
|
562805b35c | ||
|
|
274cc93c55 | ||
|
|
fea05e6b97 | ||
|
|
c7721f2b1a | ||
|
|
e303535250 |
+4
-1
@@ -3,10 +3,13 @@ export const CONTAINER_HEIGHT = 600;
|
|||||||
|
|
||||||
export const PADDLE_WIDTH = 50;
|
export const PADDLE_WIDTH = 50;
|
||||||
export const PADDLE_HEIGHT = 10;
|
export const PADDLE_HEIGHT = 10;
|
||||||
|
export const MIN_PADDLE_REFLECTION_ANGLE = 20;
|
||||||
|
export const MAX_PADDLE_REFLECTION_ANGLE = 160;
|
||||||
|
export const PADDLE_SECTOR_AMOUNT = 20;
|
||||||
|
|
||||||
export const BALL_RADIUS = 10;
|
export const BALL_RADIUS = 10;
|
||||||
export const BALL_SPEED = 3;
|
export const BALL_SPEED = 3;
|
||||||
export const BALL_INITIAL_ANGLE = 180;
|
export const BALL_INITIAL_ANGLE = 0;
|
||||||
|
|
||||||
export const BRICK_WIDTH = 40;
|
export const BRICK_WIDTH = 40;
|
||||||
export const BRICK_HEIGHT = 10;
|
export const BRICK_HEIGHT = 10;
|
||||||
|
|||||||
@@ -7,13 +7,21 @@ export class Ball {
|
|||||||
* @param {number} x координата положения мяча по оси X
|
* @param {number} x координата положения мяча по оси X
|
||||||
* @param {number} y координата положения мяча по оси Y
|
* @param {number} y координата положения мяча по оси Y
|
||||||
* @param {number} radius положительное числовое значение радиуса мяча
|
* @param {number} radius положительное числовое значение радиуса мяча
|
||||||
|
* @param {number} verticalSpeed вектор движения мяча по оси Y
|
||||||
|
* @param {number} horizontalSpeed вектор движения мяча по оси X
|
||||||
*/
|
*/
|
||||||
constructor(x, y, radius) {
|
constructor(x, y, radius, verticalSpeed = 0, horizontalSpeed = 0) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.horizontalSpeed = 0;
|
|
||||||
this.verticalSpeed = 0;
|
|
||||||
this.radius = radius;
|
this.radius = radius;
|
||||||
|
this.verticalSpeed = verticalSpeed;
|
||||||
|
this.horizontalSpeed = horizontalSpeed;
|
||||||
|
this.isOut = false;
|
||||||
|
|
||||||
|
this.defaultX = x;
|
||||||
|
this.defaultY = y;
|
||||||
|
this.defaultVerticalSpeed = verticalSpeed;
|
||||||
|
this.defaultHorizontalSpeed = horizontalSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,4 +32,15 @@ export class Ball {
|
|||||||
this.x += this.horizontalSpeed * deltaTime;
|
this.x += this.horizontalSpeed * deltaTime;
|
||||||
this.y += this.verticalSpeed * deltaTime;
|
this.y += this.verticalSpeed * deltaTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Возвращает значения к дефолтным
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
this.x = this.defaultX;
|
||||||
|
this.y = this.defaultY;
|
||||||
|
this.verticalSpeed = this.defaultVerticalSpeed;
|
||||||
|
this.horizontalSpeed = this.defaultHorizontalSpeed;
|
||||||
|
this.isOut = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-3
@@ -13,6 +13,7 @@ import { Ball } from './entities/ball/ball';
|
|||||||
import { layBricks } from './entities/brick/layBricks/layBricks';
|
import { layBricks } from './entities/brick/layBricks/layBricks';
|
||||||
import { Paddle } from './entities/paddle/paddle';
|
import { Paddle } from './entities/paddle/paddle';
|
||||||
import { tick } from './lib/tick/tick';
|
import { tick } from './lib/tick/tick';
|
||||||
|
import { toRadians } from './lib/toRadians/toRadians';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Класс игры c информацией о всех игровых сущностях
|
* Класс игры c информацией о всех игровых сущностях
|
||||||
@@ -23,10 +24,16 @@ export class Game {
|
|||||||
* @param {number} rowAmount количество кирпичей по оси Y, неотрицателное, целое число
|
* @param {number} rowAmount количество кирпичей по оси Y, неотрицателное, целое число
|
||||||
*/
|
*/
|
||||||
constructor(columnAmount, rowAmount) {
|
constructor(columnAmount, rowAmount) {
|
||||||
this.ball = new Ball(100, 100, BALL_RADIUS);
|
this.livesAmount = 3;
|
||||||
this.ball.horizontalSpeed = BALL_SPEED * Math.cos(BALL_INITIAL_ANGLE);
|
this.status = 'in_process';
|
||||||
this.ball.verticalSpeed = -1 * BALL_SPEED * Math.sin(BALL_INITIAL_ANGLE);
|
|
||||||
|
|
||||||
|
this.ball = new Ball(
|
||||||
|
CONTAINER_WIDTH / 2 - BALL_RADIUS / 2,
|
||||||
|
CONTAINER_HEIGHT / 2 - BALL_RADIUS / 2,
|
||||||
|
BALL_RADIUS,
|
||||||
|
BALL_SPEED * Math.cos(toRadians(BALL_INITIAL_ANGLE)),
|
||||||
|
-1 * BALL_SPEED * Math.sin(toRadians(BALL_INITIAL_ANGLE)),
|
||||||
|
);
|
||||||
this.paddle = new Paddle(0, CONTAINER_HEIGHT - PADDLE_HEIGHT, PADDLE_WIDTH, PADDLE_HEIGHT);
|
this.paddle = new Paddle(0, CONTAINER_HEIGHT - PADDLE_HEIGHT, PADDLE_WIDTH, PADDLE_HEIGHT);
|
||||||
|
|
||||||
this.bricks = layBricks(columnAmount, rowAmount, BRICK_WIDTH, BRICK_HEIGHT);
|
this.bricks = layBricks(columnAmount, rowAmount, BRICK_WIDTH, BRICK_HEIGHT);
|
||||||
@@ -37,6 +44,25 @@ export class Game {
|
|||||||
* @param {*} deltaTime изменение времени из Ticker
|
* @param {*} deltaTime изменение времени из Ticker
|
||||||
*/
|
*/
|
||||||
update(deltaTime) {
|
update(deltaTime) {
|
||||||
|
if (this.status !== 'in_process') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
tick(this, CONTAINER_WIDTH, CONTAINER_HEIGHT, deltaTime);
|
tick(this, CONTAINER_WIDTH, CONTAINER_HEIGHT, deltaTime);
|
||||||
|
|
||||||
|
if (this.ball.isOut) {
|
||||||
|
this.livesAmount -= 1;
|
||||||
|
|
||||||
|
if (this.livesAmount === 0) {
|
||||||
|
this.status = 'over';
|
||||||
|
} else {
|
||||||
|
this.ball.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAnyBrickAlive = this.bricks.some((row) => row.some((brick) => brick.alive));
|
||||||
|
|
||||||
|
if (!isAnyBrickAlive) {
|
||||||
|
this.status = 'completed';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import { Ball } from '../../entities/ball/ball';
|
||||||
|
import { Paddle } from '../../entities/paddle/paddle';
|
||||||
|
import { toRadians } from '../toRadians/toRadians';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяет произошло ли столкновение ракетки и мяча
|
||||||
|
* @param {Paddle} paddle
|
||||||
|
* @param {Ball} ball
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function didCollide(paddle, ball) {
|
||||||
|
return (
|
||||||
|
ball.verticalSpeed > 0 &&
|
||||||
|
ball.y + ball.radius >= paddle.y &&
|
||||||
|
ball.x >= paddle.x &&
|
||||||
|
ball.x <= paddle.x + paddle.width
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Изменяет направление полета мяча в зависимости от места попадания в ракетку
|
||||||
|
* @param {Paddle} paddle экземпляр класса ракетка
|
||||||
|
* @param {Ball} ball экземпляр класса мяч
|
||||||
|
* @param {number} paddleSectorAmount количество секторов ракетки, положительное целое число
|
||||||
|
* @param {number} minAngle минимальный угол отскока мяча (от 0 до 180)
|
||||||
|
* @param {number} maxAngle максимальный угол отскока мяча (от 0 до 180)
|
||||||
|
* @param {number} ballSpeed константа скорости мяча, положительное число
|
||||||
|
*/
|
||||||
|
export function processReflection(paddle, ball, paddleSectorAmount, minAngle, maxAngle, ballSpeed) {
|
||||||
|
try {
|
||||||
|
if (!(paddle instanceof Paddle)) {
|
||||||
|
throw new Error('Аргумерт paddle должен являться экземпляром класса Paddle');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ball instanceof Ball)) {
|
||||||
|
throw new Error('Аргумерт ball должен являться экземпляром класса Ball');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof paddleSectorAmount !== 'number' || paddleSectorAmount <= 0 || paddleSectorAmount % 1 !== 0) {
|
||||||
|
throw new Error('Аргумент paddleSectorAmount должен являться положительным целым числом');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof minAngle !== 'number' || minAngle < 0 || minAngle > 180) {
|
||||||
|
throw new Error('Аргумент minAngle должен являться числом в промежутке от 0 до 180');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof maxAngle !== 'number' || maxAngle < 0 || maxAngle > 180) {
|
||||||
|
throw new Error('Аргумент maxAngle должен являться числом в промежутке от 0 до 180');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minAngle >= maxAngle) {
|
||||||
|
throw new Error('Значение аргумента maxAngle должено быть строго больше значения аргумента minAngle');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof ballSpeed !== 'number' || ballSpeed <= 0) {
|
||||||
|
throw new Error('Значение аргумента ballSpeed должно быть положительным целым числом');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!didCollide(paddle, ball)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const step = paddle.width / paddleSectorAmount;
|
||||||
|
const sectorIndex = Math.floor((ball.x - paddle.x) / step);
|
||||||
|
const normalizedSectorIndex = Math.min(Math.max(sectorIndex, 0), paddleSectorAmount - 1);
|
||||||
|
|
||||||
|
const angleStep = (maxAngle - minAngle) / (paddleSectorAmount - 1);
|
||||||
|
const angleInRadians = toRadians(maxAngle - angleStep * normalizedSectorIndex);
|
||||||
|
ball.horizontalSpeed = ballSpeed * Math.cos(angleInRadians);
|
||||||
|
ball.verticalSpeed = -1 * ballSpeed * Math.sin(angleInRadians);
|
||||||
|
ball.y = paddle.y - ball.radius;
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
import { Ball } from '../../entities/ball/ball';
|
||||||
|
import { Paddle } from '../../entities/paddle/paddle';
|
||||||
|
import { processReflection } from './processReflection';
|
||||||
|
|
||||||
|
describe('processReflection', () => {
|
||||||
|
it('Корректно обрабатывает неверное значения аргумента paddle', () => {
|
||||||
|
const ball = new Ball(0, 0, 1, 1, 0);
|
||||||
|
const sectorAmount = 1;
|
||||||
|
const minAngle = 1;
|
||||||
|
const maxAngle = 179;
|
||||||
|
const speed = 1;
|
||||||
|
|
||||||
|
const ballX = ball.x;
|
||||||
|
const ballY = ball.y;
|
||||||
|
const verticalSpeed = ball.verticalSpeed;
|
||||||
|
const horizontalSpeed = ball.horizontalSpeed;
|
||||||
|
|
||||||
|
const result = processReflection(null, ball, sectorAmount, minAngle, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
expect(ball.x).toEqual(ballX);
|
||||||
|
expect(ball.y).toEqual(ballY);
|
||||||
|
expect(ball.verticalSpeed).toEqual(verticalSpeed);
|
||||||
|
expect(ball.horizontalSpeed).toEqual(horizontalSpeed);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Корректно обрабатывает неверное значения аргумента ball', () => {
|
||||||
|
const paddle = new Paddle(0, 0, 2, 1);
|
||||||
|
const sectorAmount = 1;
|
||||||
|
const minAngle = 1;
|
||||||
|
const maxAngle = 179;
|
||||||
|
const speed = 1;
|
||||||
|
|
||||||
|
const result = processReflection(paddle, null, sectorAmount, minAngle, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Корректно обрабатывает неверное значения аргумента paddleSectorAmount', () => {
|
||||||
|
const paddle = new Paddle(0, 0, 2, 1);
|
||||||
|
const ball = new Ball(0, 0, 1, 1, 0);
|
||||||
|
const minAngle = 1;
|
||||||
|
const maxAngle = 179;
|
||||||
|
const speed = 1;
|
||||||
|
|
||||||
|
const ballX = ball.x;
|
||||||
|
const ballY = ball.y;
|
||||||
|
const verticalSpeed = ball.verticalSpeed;
|
||||||
|
const horizontalSpeed = ball.horizontalSpeed;
|
||||||
|
|
||||||
|
const result = processReflection(paddle, ball, null, minAngle, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
expect(ball.x).toEqual(ballX);
|
||||||
|
expect(ball.y).toEqual(ballY);
|
||||||
|
expect(ball.verticalSpeed).toEqual(verticalSpeed);
|
||||||
|
expect(ball.horizontalSpeed).toEqual(horizontalSpeed);
|
||||||
|
|
||||||
|
expect(processReflection(paddle, ball, 0, minAngle, maxAngle, speed)).toBeNull();
|
||||||
|
expect(processReflection(paddle, ball, -1, minAngle, maxAngle, speed)).toBeNull();
|
||||||
|
expect(processReflection(paddle, ball, 1.5, minAngle, maxAngle, speed)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Корректно обрабатывает неверное значения аргумента minAngle', () => {
|
||||||
|
const paddle = new Paddle(0, 0, 2, 1);
|
||||||
|
const ball = new Ball(0, 0, 1, 1, 0);
|
||||||
|
const sectorAmount = 1;
|
||||||
|
const maxAngle = 178;
|
||||||
|
const speed = 1;
|
||||||
|
|
||||||
|
const ballX = ball.x;
|
||||||
|
const ballY = ball.y;
|
||||||
|
const verticalSpeed = ball.verticalSpeed;
|
||||||
|
const horizontalSpeed = ball.horizontalSpeed;
|
||||||
|
|
||||||
|
const result = processReflection(paddle, ball, sectorAmount, null, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
expect(ball.x).toEqual(ballX);
|
||||||
|
expect(ball.y).toEqual(ballY);
|
||||||
|
expect(ball.verticalSpeed).toEqual(verticalSpeed);
|
||||||
|
expect(ball.horizontalSpeed).toEqual(horizontalSpeed);
|
||||||
|
|
||||||
|
expect(processReflection(paddle, ball, sectorAmount, -1, maxAngle, speed)).toBeNull();
|
||||||
|
expect(processReflection(paddle, ball, sectorAmount, 360, maxAngle, speed)).toBeNull();
|
||||||
|
|
||||||
|
const minAngleBiggerThanMaxAngle = 179;
|
||||||
|
expect(processReflection(paddle, ball, sectorAmount, minAngleBiggerThanMaxAngle, maxAngle, speed)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Корректно обрабатывает неверное значения аргумента maxAngle', () => {
|
||||||
|
const paddle = new Paddle(0, 0, 2, 1);
|
||||||
|
const ball = new Ball(0, 0, 1, 1, 0);
|
||||||
|
const sectorAmount = 1;
|
||||||
|
const minAngle = 1;
|
||||||
|
const speed = 1;
|
||||||
|
|
||||||
|
const ballX = ball.x;
|
||||||
|
const ballY = ball.y;
|
||||||
|
const verticalSpeed = ball.verticalSpeed;
|
||||||
|
const horizontalSpeed = ball.horizontalSpeed;
|
||||||
|
|
||||||
|
const result = processReflection(paddle, ball, sectorAmount, minAngle, null, speed);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
expect(ball.x).toEqual(ballX);
|
||||||
|
expect(ball.y).toEqual(ballY);
|
||||||
|
expect(ball.verticalSpeed).toEqual(verticalSpeed);
|
||||||
|
expect(ball.horizontalSpeed).toEqual(horizontalSpeed);
|
||||||
|
|
||||||
|
expect(processReflection(paddle, ball, sectorAmount, minAngle, -1, speed)).toBeNull();
|
||||||
|
expect(processReflection(paddle, ball, sectorAmount, minAngle, 360, speed)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Корректно обрабатывает неверное значения аргумента speed', () => {
|
||||||
|
const paddle = new Paddle(0, 0, 2, 1);
|
||||||
|
const ball = new Ball(0, 0, 1, 1, 0);
|
||||||
|
const sectorAmount = 1;
|
||||||
|
const minAngle = 1;
|
||||||
|
const maxAngle = 179;
|
||||||
|
|
||||||
|
const ballX = ball.x;
|
||||||
|
const ballY = ball.y;
|
||||||
|
const verticalSpeed = ball.verticalSpeed;
|
||||||
|
const horizontalSpeed = ball.horizontalSpeed;
|
||||||
|
|
||||||
|
const result = processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, null);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
expect(ball.x).toEqual(ballX);
|
||||||
|
expect(ball.y).toEqual(ballY);
|
||||||
|
expect(ball.verticalSpeed).toEqual(verticalSpeed);
|
||||||
|
expect(ball.horizontalSpeed).toEqual(horizontalSpeed);
|
||||||
|
|
||||||
|
expect(processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, -1)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Корректно обрабатывает неверное значения аргумента speed', () => {
|
||||||
|
const paddle = new Paddle(0, 0, 2, 1);
|
||||||
|
const ball = new Ball(0, 0, 1, 1, 0);
|
||||||
|
const sectorAmount = 1;
|
||||||
|
const minAngle = 1;
|
||||||
|
const maxAngle = 179;
|
||||||
|
|
||||||
|
const ballX = ball.x;
|
||||||
|
const ballY = ball.y;
|
||||||
|
const verticalSpeed = ball.verticalSpeed;
|
||||||
|
const horizontalSpeed = ball.horizontalSpeed;
|
||||||
|
|
||||||
|
const result = processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, null);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
expect(ball.x).toEqual(ballX);
|
||||||
|
expect(ball.y).toEqual(ballY);
|
||||||
|
expect(ball.verticalSpeed).toEqual(verticalSpeed);
|
||||||
|
expect(ball.horizontalSpeed).toEqual(horizontalSpeed);
|
||||||
|
|
||||||
|
expect(processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, -1)).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Ничего не изменяет при отсутствии столкновения ракетки и мяча', () => {
|
||||||
|
const paddle = new Paddle(0, 0, 2, 1);
|
||||||
|
const ball = new Ball(10, 10, 1, 1, 0);
|
||||||
|
const sectorAmount = 1;
|
||||||
|
const minAngle = 1;
|
||||||
|
const maxAngle = 179;
|
||||||
|
const speed = 1;
|
||||||
|
|
||||||
|
const ballX = ball.x;
|
||||||
|
const ballY = ball.y;
|
||||||
|
const verticalSpeed = ball.verticalSpeed;
|
||||||
|
const horizontalSpeed = ball.horizontalSpeed;
|
||||||
|
|
||||||
|
const result = processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(ball.x).toEqual(ballX);
|
||||||
|
expect(ball.y).toEqual(ballY);
|
||||||
|
expect(ball.verticalSpeed).toEqual(verticalSpeed);
|
||||||
|
expect(ball.horizontalSpeed).toEqual(horizontalSpeed);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Отражает мяч влево при попадании в левый сектор ракетки', () => {
|
||||||
|
const paddle = new Paddle(0, 100, 90, 10);
|
||||||
|
const ball = new Ball(5, 99, 1, 5, 0); // левый край, летит вниз
|
||||||
|
const sectorAmount = 3;
|
||||||
|
const minAngle = 30;
|
||||||
|
const maxAngle = 150;
|
||||||
|
const speed = 10;
|
||||||
|
|
||||||
|
processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(ball.horizontalSpeed).toBeLessThan(0);
|
||||||
|
expect(ball.verticalSpeed).toBeLessThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Отражает мяч вправо при попадании в правый сектор ракетки', () => {
|
||||||
|
const paddle = new Paddle(0, 100, 90, 10);
|
||||||
|
const ball = new Ball(85, 99, 1, 5, 0);
|
||||||
|
const sectorAmount = 3;
|
||||||
|
const minAngle = 30;
|
||||||
|
const maxAngle = 150;
|
||||||
|
const speed = 10;
|
||||||
|
|
||||||
|
processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(ball.horizontalSpeed).toBeGreaterThan(0);
|
||||||
|
expect(ball.verticalSpeed).toBeLessThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Отражает мяч вертикально вверх при попадании в центральный сектор', () => {
|
||||||
|
const paddle = new Paddle(0, 100, 90, 10);
|
||||||
|
const ball = new Ball(45, 99, 1, 5, 0);
|
||||||
|
const sectorAmount = 3;
|
||||||
|
const minAngle = 30;
|
||||||
|
const maxAngle = 150;
|
||||||
|
const speed = 10;
|
||||||
|
|
||||||
|
processReflection(paddle, ball, sectorAmount, minAngle, maxAngle, speed);
|
||||||
|
|
||||||
|
expect(ball.horizontalSpeed).toBeCloseTo(0);
|
||||||
|
expect(ball.verticalSpeed).toBeCloseTo(-speed);
|
||||||
|
});
|
||||||
|
});
|
||||||
+30
-14
@@ -1,7 +1,15 @@
|
|||||||
import { CONTAINER_HEIGHT, CONTAINER_WIDTH } from '../../config';
|
import {
|
||||||
|
BALL_SPEED,
|
||||||
|
CONTAINER_HEIGHT,
|
||||||
|
CONTAINER_WIDTH,
|
||||||
|
MAX_PADDLE_REFLECTION_ANGLE,
|
||||||
|
MIN_PADDLE_REFLECTION_ANGLE,
|
||||||
|
PADDLE_SECTOR_AMOUNT,
|
||||||
|
} from '../../config';
|
||||||
import { Game } from '../../game';
|
import { Game } from '../../game';
|
||||||
import { calculateCollision } from '../calculateCollision/calculateCollision';
|
import { calculateCollision } from '../calculateCollision/calculateCollision';
|
||||||
import { calculateDirection } from '../calculateDirection/calculateDirection';
|
import { calculateDirection } from '../calculateDirection/calculateDirection';
|
||||||
|
import { processReflection } from '../processReflection/processReflection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Функция для вычисления взаимодействий сущностей игры в зависимости от времени из Ticker
|
* Функция для вычисления взаимодействий сущностей игры в зависимости от времени из Ticker
|
||||||
@@ -41,12 +49,20 @@ export function tick(game, containerWidth, containerHeight, deltaTime) {
|
|||||||
if (ball.x <= leftBoundary || ball.x >= rightBoundary) {
|
if (ball.x <= leftBoundary || ball.x >= rightBoundary) {
|
||||||
ball.x = ball.x <= leftBoundary ? leftBoundary : rightBoundary;
|
ball.x = ball.x <= leftBoundary ? leftBoundary : rightBoundary;
|
||||||
ball.horizontalSpeed *= -1;
|
ball.horizontalSpeed *= -1;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Не даем мячу выйти за границы стен сверху / снизу и меняем направление
|
// Не даем мячу выйти за границу стены сверху и меняем направление
|
||||||
if (ball.y <= topBoundary || ball.y >= bottomBoundary) {
|
if (ball.y <= topBoundary) {
|
||||||
ball.y = ball.y <= topBoundary ? topBoundary : bottomBoundary;
|
ball.y = topBoundary;
|
||||||
ball.verticalSpeed *= -1;
|
ball.verticalSpeed *= -1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем выход за границу стены снизу
|
||||||
|
if (ball.y >= bottomBoundary) {
|
||||||
|
game.ball.isOut = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Базоввое взаимодействие мяча и кирпича
|
// Базоввое взаимодействие мяча и кирпича
|
||||||
@@ -78,21 +94,21 @@ export function tick(game, containerWidth, containerHeight, deltaTime) {
|
|||||||
ball.horizontalSpeed *= directions[0];
|
ball.horizontalSpeed *= directions[0];
|
||||||
ball.verticalSpeed *= directions[1];
|
ball.verticalSpeed *= directions[1];
|
||||||
brick.kill();
|
brick.kill();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Базоввое взаимодействие мяча и ракетки
|
// Взаимодействие мяча и ракетки - обновление горизонтальной и вертикальной скорости в зависимости от сектора попадания
|
||||||
if (
|
processReflection(
|
||||||
ball.verticalSpeed > 0 &&
|
paddle,
|
||||||
ball.y + ball.radius >= paddle.y &&
|
ball,
|
||||||
ball.x >= paddle.x &&
|
PADDLE_SECTOR_AMOUNT,
|
||||||
ball.x <= paddle.x + paddle.width
|
MIN_PADDLE_REFLECTION_ANGLE,
|
||||||
) {
|
MAX_PADDLE_REFLECTION_ANGLE,
|
||||||
ball.verticalSpeed *= -1;
|
BALL_SPEED,
|
||||||
}
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ describe('tick', () => {
|
|||||||
const game = new Game(1, 1);
|
const game = new Game(1, 1);
|
||||||
const brick = game.bricks[0][0];
|
const brick = game.bricks[0][0];
|
||||||
|
|
||||||
game.ball.x = brick.x + 1;
|
game.ball.x = brick.x + game.ball.radius + 1;
|
||||||
game.ball.y = brick.y + brick.height + game.ball.radius + 1;
|
game.ball.y = brick.y + brick.height + game.ball.radius + 1;
|
||||||
game.ball.horizontalSpeed = 0;
|
game.ball.horizontalSpeed = 0;
|
||||||
game.ball.verticalSpeed = -10;
|
game.ball.verticalSpeed = -10;
|
||||||
@@ -65,7 +65,7 @@ describe('tick', () => {
|
|||||||
const game = new Game(2, 1);
|
const game = new Game(2, 1);
|
||||||
const [firstBrick, secondBrick] = game.bricks[0];
|
const [firstBrick, secondBrick] = game.bricks[0];
|
||||||
|
|
||||||
game.ball.x = firstBrick.x + 1;
|
game.ball.x = firstBrick.x + game.ball.radius + 1;
|
||||||
game.ball.y = firstBrick.y + firstBrick.height + game.ball.radius + 1;
|
game.ball.y = firstBrick.y + firstBrick.height + game.ball.radius + 1;
|
||||||
game.ball.horizontalSpeed = 0;
|
game.ball.horizontalSpeed = 0;
|
||||||
game.ball.verticalSpeed = -10;
|
game.ball.verticalSpeed = -10;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
/**
|
||||||
|
* Конвертирует значение угла из градусов в радианы
|
||||||
|
* @param {number} degrees значение угла в градусах
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export function toRadians(angleInDegrees) {
|
||||||
|
return (angleInDegrees * Math.PI) / 180;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user