Статьи

Tower Defense в JavaFX (4)

Хорошо, пока мы создали TileMap, отобразили его на экране и сделали его редактируемым в первой части . Во второй части мы реализовали расчет пути атаки с использованием алгоритма A * и заставили врагов следовать по этому пути. В третьей части мы создали несколько пользовательских TileSetAnimations, чтобы мы могли вращать Инсектоиды вокруг их центра на угол. Затем мы применили это к инсектоидам, чтобы они смотрели вперед, когда они летали, и к башенкам, чтобы они всегда нацеливались на ближайшую цель. Пора заставить турели стрелять по своим врагам.

Сначала нам нужны TileSets для взрыва и пуля для пеллетной пушки. Я нашел хороший бесплатный лист взрыва здесь . Он немного больше (128 * 128), чем спрайты, и взрыв не начинается в центре, но после некоторого возни с позицией относительно взрывающегося насекомого он работает довольно хорошо. Я создал пулю сам и знаю, что должен придумать что-то лучше :-), но по крайней мере это то, что видно на экране. После некоторой корректировки правильной начальной позиции я создал следующее поведение BulletLaunching:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
new SpriteBehavior() {
@Override
public boolean perform(Sprite sprite) {
double angle = rotateAnimation.getAngle();
double xVelocity = Math.cos(Math.toRadians(angle));
double yVelocity = Math.sin(Math.toRadians(angle));
final double centerX = x + (width / 2);
final double centerY = y + (height / 2);
double startX = centerX + (xVelocity * (width / 2)) - 4;
double startY = centerY + (yVelocity * (height / 2)) - 4;
Sprite bullet = new Sprite(getParent(), shoot, "bullet" + (bulletCounter++), startX, startY,
8, 8, Lookup.EMPTY);
bullet.setVelocityX(xVelocity);
bullet.setVelocityY(yVelocity);
// add bullet behavior
 
return true;
}
 
@Override
public long getEvaluationInterval() {
return 2000000000; //To change body of generated methods, choose Tools | Templates.
}
});

Большая часть кода рассчитывает исходное положение и следит за тем, чтобы пуля направилась в правильном направлении. Теперь нам нужно добавить обнаружение столкновений. Некоторые системы имеют централизованную систему коллизий и позволяют добавлять слушателей. Я предпочитаю снова делать это с помощью поведения, потому что считаю более естественным и интуитивно понятным, что пуля сама проверяет, попала ли она во что-то:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bullet.addBehaviour(new SpriteBehavior() {
private double range = 75;
 
@Override
public boolean perform(Sprite sprite) {
Collection checkCollisions = sprite.getParent().checkCollisions(sprite);
for (Collision collision : checkCollisions) {
if (collision.getSpriteOne() instanceof EnemySprite) {
sprite.getParent().removeSprite(sprite);
((EnemySprite) collision.getSpriteOne()).hit(6);
return false;
} else if (collision.getSpriteTwo() instanceof EnemySprite) {
sprite.getParent().removeSprite(sprite);
((EnemySprite) collision.getSpriteTwo()).hit(6);
return false;
}
}
if (distance(sprite.getX(), sprite.getY(), centerX, centerY) > range) {
sprite.getParent().removeSprite(sprite);
return false;
}
return true;
}
});

Мы просто просим GameCanvas о столкновениях этого конкретного Спрайта и пытаемся нанести урон, если он враг. Позже анонимный внутренний маркер Sprite и Behavior будут преобразованы в обычные классы, чтобы сделать код более приятным, а также облегчить его создание и настройку. На стороне Enemy Sprite нам нужно реализовать метод «удара»:

1
2
3
4
5
6
public void hit(int impact) {
power = power - impact;
if (power getParent().removeSprite(this);
getParent().addSprite(new Sprite(getParent(), explosionAnimation, "explosion", getX() - 30, getY() - 80, 128, 128, Lookup.EMPTY));
}
}

Очень просто: если попадание было смертельным, мы удаляем спрайт и добавляем взрывной спрайт. Если бы размеры спрайтов совпадали, мы могли бы просто установить анимацию взрыва на существующем спрайте. Если вы можете создать свой собственный SpriteSheets, вы должны это сделать, это значительно облегчает жизнь. ExplosionAnimation настроена для запуска только один раз, и у нее есть EventHandler, который удаляет спрайт после завершения анимации:

01
02
03
04
05
06
07
08
09
10
explosionAnimation = new TileSetAnimation(explosion, 100f);
explosionAnimation.setRepeat(1);
explosionAnimation.setOnFinished(new AnimationEventHandler() {
@Override
public void handleEvent(AnimationEvent event) {
Sprite target = event.getTarget();
target.getParent().removeSprite(target);
getParent().removeSprite(EnemySprite.this);
}
});

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

В видео вы также видите DebugLayer. В настоящее время он отслеживает некоторые данные производительности, в основном FPS, и если время между двумя импульсами слишком велико. Я также добавил пулю в верхнюю часть экрана, чтобы визуально определять анимацию заикания. Вы можете смело игнорировать это …

Так что теперь у нас есть почти все, что нужно для игры типа Tower Defense. В следующей части этого урока мы добавим индикаторы урона врагам, а также HUD со счетом и контролем, чтобы начать следующую волну.

Ссылка: Tower Defense в JavaFX (4) от нашего партнера JCG Тони Эппла в блоге Eppleton .