[Java] Arkanoid
Сделайте Arkanoid:
- Наверху некоторое количество объектов-кирпичей, у каждого свой цвет и прочность (количество ударов которое кирпич выдерживает до того как исчезнет)
- Внизу движется платформа, ее можно двигать влево и вправо (клавиатурой и/или мышкой)
- Игровой шарик в начале игры приклеен к платформе, но как только платформа двигается - шарик начинает лететь
- Шарик летит с константной скоростью
- Если шарик врезается в кирпич - прочность кирпича уменьшается на один
- Если шарик врезается в кирпич или стену с края экрана - шарик рикошетит под углом падения относительно встреченной поверхности (так же как отражается луч света от зеркала)
- Если шарик врезается внизу в платформу управляемую игроком - шарик рикошетит тем левее чем левее он упал на платформу, или тем правее чем правее упал на платформу
- Если шарик пролетает вниз мимо платформы - конец игры (здорово если вы добавите поддержку жизней)
- Здорово если вы добавите бонусы иногда выпадающие из кирпичей при их разрушении - расширение платформы, замедление мячика, раздвоение/растроение/раздесятерение мячика
Идеи про обновление игрового мира
Все объекты вроде платформы, шарика и кирпичей храните во вспомогательном объекте класса World
.
Все методы обработки коллизий реализуйте как методы этого класса World
:
void checkBallBricksCollisions()
- проверить мяч на врезание в кирпичи, обновить состояние мяча (например направление его скорости) и состояние кирпича (прочность)void checkBallPlatformCollision()
- проверить мяч на врезание в платформу игрока и обновить состояние мяча, возможно хорошо еще обновить очки игрокаvoid checkBallWallsCollisions()
- проверить мяч на врезание в границы экрана и обновить состояние мячаvoid checkBallOutOfScreenCollision()
- проверить мяч на вылет за пределы экрана мимо платформы игрока
И соответственно в World
будет общий метод обновления состояния всего игрового мира за временной промежуток \(dt\):
void update(double dt) {
checkBallBricksCollisions();
checkBallPlatformCollision();
checkBallWallsCollisions();
checkBallOutOfScreenCollision();
ball.update(dt); // обновляем положение шарика - т.е. двигает его на вектор v*dt, где v - вектор скорости
}
Как реализовать checkBallBricksCollisions()
, checkBallPlatformCollision()
, checkBallWallsCollisions()
?
Заметьте что это все методы которые проверяют, а не оказался ли шарик внутри препятствия-прямоугольника, и если оказался - то эти методы меняют скорость шарика на отраженную под углом падения (т.е. все так же как и если бы мяч был лучом света, а у кирпича была бы зеркальная поверхность).
Т.е. достаточно реализовать методы (в классе World
):
boolean isBallInsideRectangle(Ball ball, Rectangle rectangle) {
// Мяч внутри прямоугольника если его абсцисcа лежит между левой и правой стороной прямоугольника
// и его ордината лежит между верхней и нижней стороной прямоугольника
if (ball.x > ... && ...) {
return true;
} else {
return false;
}
}
void updateBallSpeed(Ball ball, Rectangle rectangle) {
if (...) { // Если мяч ударился о нижнюю сторону прямоугольника
ball.v.y = -ball.v.y;
} else if (...) { // Если мяч ударился о левую сторону прямоугольника
ball.v.x = -ball.v.x;
} else if (...) {
...
} else {
}
}
И тогда например метод проверяющий а не врезался ли мячик в кирпич, а так же обновляющий скорость шарика и уменьшающий прочность кирпича в случае врезания выглядит в классе World
примерно так:
void checkBallBricksCollisions() {
for (int i = 0; i < bricks.size(); ++i) {
if (isBallInsideRectangle(ball, bricks.get(i).rectangle)) {
updateBallSpeed(ball, bricks.get(i).rectangle);
bricks.get(i).hit(); // уменьшить у кирпича прочность
break; // эта строчка не обязательна, но если мы решили что в одном обновлении шарик врезается не больше чем в один кирпич - то можно остальные кирпичи не проверять (прервав пробег по циклу)
}
}
}
Рекомендуемый порядок разработки
1) Создайте класс Main
и создайте в main
-функции окно
2) Создайте класс ArkanoidPanel
: унаследуйте его от JPanel
и переопределите метод:
@Override
protected void paintComponent(Graphics g) {
// ...
}
3) Создайте класс Ball
: с полями хранящими его местоположение и скорость например
4) Создайте класс Brick
: с полями хранящими его прямоугольную фигуру (координаты и размеры), прочность и цвет
5) Создайте класс Platform
: с полями хранящими ее координаты и ширину
6) Создайте класс World
: с полями хранящими мячик, список кирпичей (ArrayList<Brick>
) и платформу игрока
7) Реализуйте метод void draw(Graphics g)
в Ball
, Brick
, Platform
8) Реализуйте метод void draw(Graphics g)
в World
например так:
void draw(Graphics g) {
ball.draw(g);
for (int i = 0; i < bricks.size(); ++i) {
bricks.get(i).draw(g);
}
platform.draw(g);
}
9) Убедитесь что ваш изначальный мир (шарик на платформе, сверху какие-то кирпичи) рисуется так как вы ожидаете (не забудьте вызвать отрисовку мира из метода paintComponent
в ArkanoidPanel
)
10) Объявите методы про проверку врезаний в World
(см. Идеи про обновление мира выше), но пока оставьте их пустыми
11) Реализуйте методы void update(double dt)
в классах Ball
и World
(см. Идеи про обновление мира выше)
12) Вызовите world.update(dt)
из вечного цикла в main
-функции
13) Убедитесь что ваш мир теперь не только рисуется, но шарик даже начал куда-то лететь
14) Реализуйте методы про проверку врезаний в World
15) Убедитесь что шарик начал ломать кирпичи
16) Добавьте обработку клавиатуры и/или мышки для управления платформой
17) Можете добавить бонусов или чего-то подобного