[Java] Летающий в окне шарик
1) Создайте пустой проект
2) На базе недавней статьи:
- Создайте новый класс
MyPanel
, удалите человечков (поля-объектыHuman
), удалите из метода отрисовки все кромеsuper.paintComponent(g);
- Создайте класс
Main
сmain
функцией - в ней создается окно, создается панель, человечков оттуда тоже удалите
3) Посмотрите на оставшийся код, он должен компилироваться и запускаться - пустое окно. Этот код вам должен быть понятен, если есть по нему непонятки что где - спросите.
4) Создайте класс Ball
:
- Целочисленные поля
x
,y
- координаты мячика в данный момент - Конструктор который инициализирует эти поля в переданные значения
- Метод
void paint(Graphics g)
, который отрисовывает мячик по его координатам - и в этом методе нарисуйте мячик
5) В main
функции создайте объект класса Ball
и передайте его как аргумент в конструктор MyPanel
(чтобы панель знала про существование этого мяча и смогла его отрисовать)
6) В MyPanel
:
- Добавьте поле которое будет хранить ссылку на мячик (объект класса
Ball
, как было с человечками) - Добавьте в конструкторе аргумент типа
Ball
, сохраните этот аргумент в только что созданное поле - В методе
paintComponent
сразу послеsuper.paintComponent(g)
мы хотим вызвать у нашего мячика метод “нарисуй себя”
7) Запустите программу и убедитесь что мячик нарисовался
8) Добавьте в мячик метод update(int width, int height)
- который будет обновлять положение мячика и какого размера окошко в данный момент (для этого в MyPanel
в вызове ball.update(...)
нажмите this.
начните печатать width
и выберите самый подходящий метод - он даст вам ширину панели, то же самое - с height
, результаты работы этих методов надо передать в метод обновления положения мячика)
9) В этом методе просто двигайте его строго горизонтально или строго вертикально пока что на \(1\), с проверкой - что если он выйдет за пределы окна - пусть телепортируется в начало
10) В MyPanel
в методе отрисовки перед тем как рисовать мячик добавьте вызов обновления его положения (тогда каждый кадр отрисовки будет двигать мяч чуть дальше)
11) Запустите программу и убедитесь что мячик летит как надо и не исчезает за пределами окна, попробуйте изменить размеры окна - правда ли что мяч долетает как раз до нового края
12) Попробуйте теперь замедлить мячик - сдвигать не на \(1\) а на \(0.1\) - запустите программу, что будет с мячиком? Почему так?
13) Подумайте как это исправить? Если ничего не приходит в голову - обратите внимание что координаты мячика целочисленные, какое будет там значение после прибавления \(0.1\) после округления? Т.е. достаточно перейти на double. А при отрисовке (в самый последний момент) преобразовывать число в целое округлением, см. пример: int xInt = (int) (x + 0.12412);
- т.е. дописав в круглых скобках тип к которому нужно преобразовать.
14) Сделайте так чтобы мячиков было много - вспомните про динамический список - ArrayList<Ball>
(см. статью) - в main
-функции его нужно будет создать и наполнить шариками - создайте хотя бы штук 10-100 случайных шариков:
// Это пример как создать динамический список из 10 случайных целых чисел
// С мячиками почти то же самое - только нужно создать два случайных числа - x, y
// И создав очередной случайный мячик - добавить его в список (который будет хранить не Integer, а Ball)
ArrayList<Integer> values = new ArrayList<>();
Random r = new Random(); // этот объект генерирует случайные числа
int n = 10;
for (int i = 0; i < n; i = i + 1) {
Integer value = r.nextInt(100); // r.nextInt(X) создаст случайное число от 0 (включительно) до X=100 (исключительно, т.е. до 99 включительно)
values.add(value);
}
15) Храните теперь в панели не один мячик - а этот динамический список (т.е. замените поле с мячика на динамический список, поправьте конструктор, и теперь надо вызывать обновление состояния каждого мячика, и отрисовывать тоже каждый мячик - пробежав по ним в for
-цикле)
16) Запустите программу и убедитесь что мячиков теперь много и они все вместе движутся. Укажите диапазон случайных значений такой чтобы мячики заполнили все окно.
Подумайте: Почему если окно увеличить - то мячики не заполняют все окно равномерно? Потому что код который распределяет мячики отработал в момент запуска программы - именно тогда размер окна был учтен и мячики постарались заполнить окно равномерно.
17) Сделайте мячики двигающимися в разных направлениях - для этого надо создать еще два поля в классе Ball
- vx
и vy
- скорости по обеим осям (кроме обновления конструктора нужно так же обновить метод обновления состояния мячика). Запустите программу.
Подумайте: Почему нельзя просто создать случайное смещение в момент обновления координат шарика в методе update
? Потому что тогда мячик будет колбасить - он каждый раз будет двигаться в разном направлении, поэтому скорость должна быть выбрана в момент создания мячика.
18) Сделайте так чтобы некоторые мячики двигались в т.ч. налево или вверх. Запустите программу.
Подсказка: Как создать отрицательное случайное число от \(-10\) до \(10\)? Давайте создадим число от \(0\) до \(20\) (включительно, поэтому r.nextInt(21)
) и вычтем \(10\).
19) Сделайте так чтобы у шариков был случайный радиус и скорость была обратно пропорциональна радиусу (большие - медленные, маленькие - быстрые). Запустите программу.
Пример красоты:
20) Сделайте шарики разноцветными и залитыми этой краской (цвет не должен мигать - он назначается шарику с рождения). Запустите программу.
21) Сделайте так чтобы при залетании за стенку мячик не телепортировался на другой край - а отражался как бывает по законам физики (угол падения равен углу отражения). Запустите программу.
22) Добавьте в панели рассчет сколько прошло времени с отрисовки предыдущего кадра (достаточно использовать System.currentTimeMillis()
и хранить в поле панели время предыдущей отрисовки), передавайте это число в метод update
- чтобы мяч двигался на скорость перемноженную с этим прошедшим временем (движение будет плавнее)
23) Режим УльтраХардкор++: сделайте рассчет физики столкновения мячиков (считая их абсолютно упругими телами). Запустите программу.