Статьи

JavaFX 2 GameTutorial, часть 4

Вступление

Это четвертая часть серии из шести статей, относящихся к учебному пособию по JavaFX 2 . Если вы пропустили часть 1 , часть 2 или часть 3 , я призываю вас ознакомиться с ними, прежде чем начинать этот урок. Напомним, что в третьей части я рассказал вам о многих классических аркадных играх и различных устройствах ввода, которые использовались. Затем я показал вам, как создать простую игру, похожую на знаменитую аркаду «Астероиды». Управление (движение корабля), однако, было больше похоже на управление в компьютерной игре Star Craft. В третьей части вы должны хорошо понимать, как получать ввод с клавиатуры и мыши.

Рисунок 1. Учебное пособие по JavaFX 2, часть 4

Это руководство посвящено настройке игрового движка части 2 и обновлению существующей игры в стиле «Астероиды» из части 3 для обработки обнаружения столкновений. В этом уроке я кратко расскажу о спрайтах и ​​о том, как справляться с обнаружением столкновений. Космический корабль теперь будет иметь возможность создавать силовое поле, чтобы защитить себя от врагов и астероидов. Это напоминает классическую аркаду « Астероиды Делюкс ». Если вы хотите запустить демонстрацию, прокрутите вниз и нажмите кнопку WebStart ниже. Пожалуйста, ознакомьтесь с требованиями перед запуском игры.

Что такое спрайт?

Согласно Википедии, « спрайт — это двумерное изображение или анимация, которая интегрирована в большую сцену». С точки зрения игрового мира Java спрайт — это объект, содержащий кадры изображения и дополнительные данные, основанные на контексте актера, который должен быть анимирован в области сцены. Во времена Уолта Диснея , когда мультфильмы рисовались карандашом и бумагой, художник создавал множество рисунков, которые стали анимацией. Этот пример указывает на создание флипбук . Я уверен, что вы создали флип-книги в детстве. Я знаю, что сделал. Я обычно рисовал и делал крутые анимации со всех уголков моих тетрадей. В нашей игре типа астероидов я создал спрайтовый объект, который содержит все изображения ( ImageView ) корабля, предварительно повернутые, как флипчарт. Чтобы оживить поворот корабля, я сделал текущий кадр видимым, а остальные кадры не видимыми. Подобно перевернутой книге, она будет вращаться вокруг своей центральной (поворотной) точки. Спрайт может также содержать другую информацию, такую ​​как скорость или очки здоровья.

Обнаружение столкновения

Когда актеры или спрайты анимируются по сцене, игровой движок будет проверять каждый спрайт против других спрайтов, чтобы определить, не столкнулись ли они друг с другом. Этот процесс должен быть очень эффективным, особенно если вокруг экрана движутся многочисленные спрайты. Есть компромиссы, когда дело доходит до эффективности. Поскольку каждый цикл в игровом цикле будет проверять наличие столкновений, его точность, как правило, снижает вашу производительность. Многие игры используют ограничивающую область изображения, чтобы определить, столкнулись ли два спрайта друг с другом. Некоторые игры используют прямоугольники в качестве ограничивающих областей. Ниже на рисунке 2 показано столкновение двух спрайтов:

Рисунок 2 Ограничительная рамка в виде прямоугольной области столкновения.

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

Рисунок 3 изображает изображение актера

Те игры, которые используют прямоугольные ограничивающие области, обычно делают ограничивающий прямоугольник, вписанный в изображение спрайта. Ниже на рисунке 4 показаны две прямоугольные ограничивающие области (оранжевая и зеленая) на изображении космического корабля.

Рисунок 4 Два прямоугольника, используемые в качестве ограничивающих рамку столкновений.

Я уверен, что вы заметите, что кончик носа корабля и крылья не закрыты ни одной ограничительной рамкой. Это означает, что когда астероид перекрывает неограниченную область спрайта, столкновение не произойдет. Некоторые игры используют эту стратегию; вы заметите, что прямоугольные ограничивающие области спрайтов малы и расположены в ключевых областях изображения спрайта. Большая точность будет найдена с лучшими алгоритмами для многоугольников и других непрямоугольных форм. В этой записи блога я в основном использую круги как ограничивающие области, а не прямоугольники. Я мог бы сделать так, чтобы каждый спрайт содержал массив форм столкновений, но вместо этого я выбрал только одну область столкновений для каждого спрайта. Каждая область столкновения будет иметь форму круга на графе сцены. Для космического корабля я вписал круг, основанный на центральной точке корабля с радиусом, вытянутым к яме корабля. Показанная ниже на рисунке 5 ограниченная круговая зона столкновения корабля изображена в виде красного круга.

Рисунок 5 Область столкновения корабля.

Я выбрал круг в качестве ограничивающей области из-за относительной легкости определения столкновения двух объектов на основе формулы расстояния ( теорема Пифагора ), которая требует только центральную точку каждой области спрайта и их радиусы. После расчета расстояния на основе двух центральных точек, вы сравните результат, чтобы увидеть, меньше ли оно или равно сумме двух радиусов. Если результат действительно окажется меньше или равен сумме двух радиусов, то произошло столкновение. На рисунке 6 показано, как формула расстояния относится к двум центральным точкам круговых ограничивающих областей.

Рисунок 6 формула расстояния между двумя центральными точками.

Следующий код создает основной игровой цикл из класса GameWorld :

01
02
03
04
05
06
07
08
09
10
11
12
13
@Override
public void handle(javafx.event.ActionEvent event) {
 
   // update actors
   updateSprites();
 
   // check for collision
   checkCollisions();
 
   // removed dead things
   cleanupSprites();
 
}

Приведенный ниже код создает метод checkCollision () из класса GameWorld :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
protected void checkCollisions() {
    // check other sprite's collisions
    spriteManager.resetCollisionsToCheck();
    // check each sprite against other sprite objects.
    for (Sprite spriteA : spriteManager.getCollisionsToCheck()) {
        for (Sprite spriteB : spriteManager.getAllSprites()) {
            if (handleCollision(spriteA, spriteB)) {
                // The break helps optimize the collisions
                //  The break statement means one object only hits another
                // object as opposed to one hitting many objects.
                // To be more accurate comment out the break statement.
                break;
            }
        }
    }
}

Реализация производного класса Game World ( TheExpanse ) его метода handleCollision () :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
 * How to handle the collision of two sprite objects.
 *
 * @param spriteA Sprite from the first list.
 * @param spriteB Sprite from the second list.
 * @return boolean returns a true if the two sprites have collided otherwise false.
 */
@Override
protected boolean handleCollision(Sprite spriteA, Sprite spriteB) {
    if (spriteA != spriteB) {
        if (spriteA.collide(spriteB)) {
 
            if (spriteA != myShip) {
                spriteA.handleDeath(this);
            }
            if (spriteB != myShip) {
                spriteB.handleDeath(this);
            }
        }
    }
 
    return false;
}

Реализация класса Sprite по умолчанию для его метода collide () с использованием формулы расстояния:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public boolean collide(Sprite other) {
 
    if (collisionBounds == null || other.collisionBounds == null) {
        return false;
    }
 
    // determine it's size
    Circle otherSphere = other.collisionBounds;
    Circle thisSphere = collisionBounds;
    Point2D otherCenter = otherSphere.localToScene(otherSphere.getCenterX(), otherSphere.getCenterY());
    Point2D thisCenter = thisSphere.localToScene(thisSphere.getCenterX(), thisSphere.getCenterY());
    double dx = otherCenter.getX() - thisCenter.getX();
    double dy = otherCenter.getY() - thisCenter.getY();
    double distance = Math.sqrt(dx * dx + dy * dy);
    double minDist = otherSphere.getRadius() + thisSphere.getRadius();
 
    return (distance < minDist);
}

Реализация класса Sprite по умолчанию его метода handleDeath () :

1
2
3
public void handleDeath(GameWorld gameWorld) {
    gameWorld.getSpriteManager().addSpritesToBeRemoved(this);
}

Класс Atom (астероид или ракета) переопределит метод handleDeath () :

1
2
3
4
public void handleDeath(GameWorld gameWorld) {
    implode(gameWorld);
    super.handleDeath(gameWorld);
}

JavaFX 2 Sprite and Collision Демо

Эта простая демонстрационная игра будет смесью StarCraft и Asteroids. Используя мышь для навигации по кораблю, вы заметите, что элементы управления будут напоминать StarCraft’s Battle Cruiser . Цель состоит в том, чтобы стрелять своим оружием по сферам до того, как они поразят ваш космический корабль или другие сферы, которые обрушатся при ударе. Поскольку это простой учебник или даже игра на ранних стадиях разработки, игра не отслеживает счет. Я призываю вас перейти на GitHub, чтобы загрузить код и улучшить игру. Для краткости я не буду показывать все изменения кода, но я надеюсь, что вы посетите GitHub здесь: https://github.com/carldea/JFXGen для всех демонстраций и исходного кода.

Требования :

  • Java 7 или более поздняя версия
  • JavaFX 2.1 или более поздняя версия
  • Windows XP или более поздняя версия (скоро будет доступна для Linux / MacOS)

Простая игра типа астероидов под названием «Пространство».

Инструкции:

  • Щелкните правой кнопкой мыши (на Windows), чтобы летать на корабле.
  • Щелчок левой кнопкой мыши (левой кнопкой мыши на Windows), чтобы стрелять оружие.
  • Нажмите клавишу «2? перейти на большие ракеты. (синий круговой снаряд)
  • Другое нажатие клавиши по умолчанию для меньших ракет. (красные круглые снаряды)
  • Нажатие клавиши пробела переключит силовое поле, чтобы защитить корабль от врагов и астероидов.

Нажмите кнопку Launch ниже, чтобы начать демонстрацию: Учебное демо

Перейдите к части 5 этого урока.

Статьи по Теме

Определение спрайта: http://en.wikipedia.org/wiki/Sprite_%28computer_graphics%29

Уолт Дисней: http://en.wikipedia.org/wiki/Walt_Disney

Как сделать флипбук: http://www.bitrebels.com/design/how-to-create-a-flip-book/

ImageView JavaFX: http://docs.oracle.com/javafx/2/api/javafx/scene/image/ImageView.html

Обнаружение столкновения: http://zetcode.com/tutorials/javagamestutorial/collision/

Обнаружение столкновений AABB в Java: http://www.youtube.com/watch?v=JIxV-LXqa1g

Теорема Пифагора: http://en.wikipedia.org/wiki/Pythagorean_theorem

Формула расстояния: http://en.wikipedia.org/wiki/Distance

Серьезная игра Asteroids Deluxe (Youtube): http://www.youtube.com/watch?v=6DG-GJENHgg

Ссылка: JavaFX 2 GameTutorial, часть 4 от нашего партнера по JCG Карла Деа в блоге Carl’s FX Blog .