28 ноября 2016 г

Отправлено 28 нояб. 2016 г., 12:28 пользователем Dimitrijs Fedotovs   [ обновлено 28 нояб. 2016 г., 12:55 ]


Игра-стрелялка

На этот раз мы научимся делать игру-стрелялку. Снизу будет ездить каретка. Кареткой можно будет управлять клавишами вправо и влево. А при нажатии на пробел - из каретки должен вылететь шарик.

Игровое поле и каретка

В первую очередь нам надо разместить на поле и зарегистрировать каретку.

Мы обозначим каретку знаком равно '=' и разместим ее в самом низу игрового поля:

##################################
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# = #
##################################
и затем зарегистрируем:
register('=', CaretSprite::new)
установим скорость движения:
.onInit(c -> c.setSpeed(10))
направление:
.onInit(c -> c.setDirection(Direction.E))

Больше о направлениях можно почитать на страничке одного из прошлых уроков (старшей группы)

За тем на каждый кадр будем смещать каретку по указанному выше направлению:
.onLoop(ai::followDirection)
И напоследок - остановка каретки, если она врезалась в стенку:
.onCollision(ai::stopX)

Управление кареткой

Управлять кареткой мы будем при помощи клавиш вправо и влево. Для этого будем использовать специальное событие - onKeyPressed. Когда игрок нажмет клавишку влево, нам надо изменить направление движения кареткой на Direction.W - (West - запад). 
.onKeyPressed(c -> c.setDirection(Direction.W), KeyCode.LEFT)
Когда игрок нажимает клавишу вправо - Direction.E - (East - восток).
.onKeyPressed(c -> c.setDirection(Direction.E), KeyCode.RIGHT)

Результат

Код каретки должен получиться примерно такой:
register('=', CaretSprite::new)
.onInit(c -> c.setSpeed(10))
.onInit(c -> c.setDirection(Direction.E))
.onLoop(ai::followDirection)
.onCollision(ai::stopX)
.onKeyPressed(c -> c.setDirection(Direction.W), KeyCode.LEFT)
.onKeyPressed(c -> c.setDirection(Direction.E), KeyCode.RIGHT);
В результате каретка сразу начинает двигаться вправо (Direction.E). Но можно поменять направление движения при помощи клавиш вправо (KeyCode.RIGHT) и влево (KeyCode.LEFT).

Выстрел

К коду каретки надо добавить реакцию на клавишу пробел - KeyCode.SPACE.
.onKeyPressed(this::fire, KeyCode.SPACE)

Слово fire отмечено красным цветом - ошибка! Это потому, что наша программа пока еще не знает что такое fire. this - данном случае подсказка, что fire будет "объяснено" в этом же файле MyGame.

Чтобы продолжить, нужно поставить курсор (кликнуть мышкой) на слово fire. Слева появится красная лампочка. Кликнуть по этой лампочке и выбрать create method fire:

Если сделать все правильно, то появятся такие вот строчки:

Сейчас мы будем писать код в методе fire. То есть между открывающей фигурной скобкой и закрывающей { }

Этот код будет выполняться, когда игрок нажмет клавишу пробел. Значит написанное нами this::fire как раз и будет вызывать этот блок кода.

Создание спрайта шарика на экране

Нам нужно создать спрайт шарика и поместить его в те же координаты, где сейчас находится каретка, только на одну клеточку выше:

sprite(BallSprite::new, caretSprite.getX(), caretSprite.getY() - 1);

Движение шарика

Если теперь запустить игру, то окажется, при нажатии на пробел - появляется шарик. Но он никуда не исчезает и не движется.
Чтобы заставить его двигаться - все что нам нужно: 

Установить скорость:

.onInit(b -> b.setSpeed(10))
Установить направление:
.onInit(b -> b.setDirection(Direction.N))
Двигаться в заданном направление:
.onLoop(ai::followDirection)

Все готово?

Если теперь запустить игру, то шарики будут улетать вверх. Выглядит, как будто все готово. Но на самом деле у нас закралась одна ошибка: 

При каждом выстреле мы создаем новый шарик на экране. И он улетает вверх. Но исчезнув и экрана - это не значит, что шарик исчез из игры. Он продолжает лететь вверх даже в "космосе", даже если мы его не видим.

Если выстрелить очень много шариков, то рано или поздно игра начнет "тормозить".

Как же быть? Нужно удалять шарик из игры (halt) когда он коснется стены сверху. То есть написать обработку события onCollision:
.onCollision(ai::halt, WallSprite.class)

Итог

Код шарика должен получится следующий:
private void fire(CaretSprite caretSprite) {
sprite(BallSprite::new, caretSprite.getX(), caretSprite.getY() - 1)
.onInit(b -> b.setSpeed(10))
.onInit(b -> b.setDirection(Direction.N))
.onLoop(ai::followDirection)
.onCollision(ai::halt, WallSprite.class);
}
Все готово!

Домашнее задание

Нужно сделать так, чтобы кареткой можно было управлять не только вправо и влево, но так же вверх и вниз!
Подсказки:
  • коды клавиш
    • KeyCode.UP - клавиша "вверх"
    • KeyCode.DOWN - клавиша "вниз"
  • чтобы каретка не улетала вверх или вниз за экран нужно воспользоваться ai::stopXY.

Код

Полный код, написанный во время занятия:
import guru.bug.game.*;
import guru.bug.game.sprite.*;
import guru.bug.game.background.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;


public class MyGame extends Game {

@Override
public void setup() {
setBackground(new DesertBackground());

register('#', WallSprite::new)
.onInit(w -> w.setColor(WallColor.BLUE));

register('=', CaretSprite::new)
.onInit(c -> c.setSpeed(10))
.onInit(c -> c.setDirection(Direction.E))
.onLoop(ai::followDirection)
.onCollision(ai::stopX)
.onKeyPressed(c -> c.setDirection(Direction.W), KeyCode.LEFT)
.onKeyPressed(c -> c.setDirection(Direction.E), KeyCode.RIGHT)
.onKeyPressed(this::fire, KeyCode.SPACE);

load("/level.txt");
}

private void fire(CaretSprite caretSprite) {
sprite(BallSprite::new, caretSprite.getX(), caretSprite.getY() - 1)
.onInit(b -> b.setSpeed(10))
.onInit(b -> b.setDirection(Direction.N))
.onLoop(ai::followDirection)
.onCollision(ai::halt, WallSprite.class);
}

@Override
public void loop() {

}
}

Comments