Сделайте FlappyBird - игру аналогичную этой.

1) Создайте пустой проект FlappyBird. Как и с прошлым заданием про единорога - пишите все с чистого листа. Подглядывать в старые материалы можно, но без копирования и прямого перепечатывания.

2) Создайте окно, добавьте на него ваш класс BirdPanel наследованный от JPanel с переопределенным методом protected void paintComponent(Graphics g) { ... }.

3) В методе paintComponent нарисуйте картинкой игрового персонажа. Можете взять произвольную маленькую картинку из интернета или одну из этих:

Unicorn

Flabby bird

Замечание Если ничего не рисуется, то проверьте что:

  • вы создали объект BirdPanel через new BirdPanel()
  • вы добавили этот объект в окно через frame.add
  • вы сделали frame.add(birdPanel) ДО frame.setVisible(true)
  • вы добавили while(true) { frame.repaint(); }
  • в BirdPanel в методе paintComponent вызывается рисование картинки

4) Создайте класс Bird описывающий птицу (ее положение в пространстве, ее скорость и угол наклона и т.п.) и реализующий метод void draw(Graphics g) который теперь инкапсулирует (т.е. прячет детали реализации того как нарисовать птицу внутри себя) отрисовку птицы. Иначе говоря тот код который рисовал картинку в paintComponent теперь должен быть перенесен в метод Bird.draw(Graphics g). Обратите внимание что поле с картинкой птицы тоже надо перенести в класс Bird (и соответственно его инициализацию - в конструктор птицы).

5) Создайте объект этого класса, и вызовите его отрисовку (т.е. метод draw объекта класса Bird) из метода BirdPanel.paintComponent. Объект например хорошо хранить как поле внутри BirdPanel. Обратите внимание что у вас должен быть ровно один объект птицы на всю программу, в т.ч. это означает что new Bird() должен быть написан ровно один раз во все программе.

6) Добавьте в класс Bird метод void update(double dt) который будет обновлять положение птицы (с учетом прошедшего времени \(dt\) и ускорения гравитацией).

7) Добавьте в main-функции цикл, который будет вызывать frame.repaint() и bird.update, тем самым постоянно обновляя положение птицы (она должна падать) и отрисовывая ее.

8) Добавьте обработчик клавиатуры, который при нажатии на пробел будет подкидывать птицу вверх (спойлер: для подкидывания нужно просто выставить значение вертикальной скорости вверх)

Обратите внимание что нужно изменять скорость птицы хранящейся в панели, т.е. panel.bird.

9) Убедитесь что птица ведет себя естественно на ваш взгляд при нажатии на пробел. Если нет - задумайтесь и подгоните константы и метод update.

10) Сделайте так, чтобы птица поворачивалась на небольшой угол пропорционально тому, с какой скоростью она стремиться ко дну в данный момент. (быстро летит вниз - смотрит вниз, летит вверх - смотрит вверх, только что летела вверх а теперь почти начинает лететь вниз - смотрит горизонтально)

11) Добавьте класс Wall, описывающий стену, которая движется справа налево. Метод wall.update должен обновлять ее местоположение и соответственно так же должен быть вызван из цикла в main-функции. Метод wall.paint должен рисовать ее и соответственно должен быть вызван из paintComponent

12) В цикле добавьте создание стены, ее движение и создание новой случайной стены когда текущая стена ушла за пределы экрана налево.

13) Добавьте метод checkCollision который будет проверять, не врезалась ли птица в стену, и если врезалась - печатайте сообщение в консоль “Game over”

14) Сделайте так, чтобы при врезании игра начиналась заново. (обратите внимание что состояние игры исчерпывающе описывается птицей и стеной, поэтому для “перезапуска” игры достаточно вернуть их состояние в изначальное - например пересоздав их)

15) Можете попробовать добавить промежуточное состояние - показывать на несколько секунд черный экран с надписью “Game over” при врезании с последующим перезапуском (или перезапуск только после клика на пробел).

16) ???

17) Вы восхитительны!