В третьей части мы рассмотрели столкновения между платформами и игроком и добавили элементы управления с клавиатуры. Если вы еще этого не сделали, то перейдите к третьей части и выполните указания в ней.
Мы закончили третью часть созданием персонажа, который может двигаться и прыгать по платформам.
Теперь нам нужно создать несколько звезд.
import Phaser from 'phaser'
const GROUND_KEY = 'ground'
const DUDE_KEY = 'dude'
//добавляем константу для звезд
const STAR_KEY = 'star'
export default class GameScene extends Phaser.Scene
{
constructor()
{
super('game-scene');
this.player = undefined;
this.cursors = undefined;
}
preload()
{
this.load.image('sky', 'assets/sky.png');
this.load.image(GROUND_KEY, 'assets/platform.png');
// заменяем константой строковый литерал
this.load.image(STAR_KEY, 'assets/star.png');
this.load.image('bomb', 'assets/bomb.png');
this.load.spritesheet(DUDE_KEY,
'assets/dude.png',
{ frameWidth: 32, frameHeight: 48 }
);
}
create()
{
this.add.image(400, 300, 'sky');
const platforms = this.createPlatforms();
this.player = this.createPlayer();
// добавляем константу для хранения звезд
const stars = this.createStars();
this.physics.add.collider(this.player, platforms);
// добавляем коллайдер для звезд и платформ
this.physics.add.collider(stars, platforms);
this.cursors = this.input.keyboard.createCursorKeys();
}
// ...
// Создаем звезды
createStars()
{
const stars = this.physics.add.group({
key: STAR_KEY,
repeat: 11,
setXY: { x: 12, y: 0, stepX: 70 }
});
stars.children.iterate((child) => {
child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
});
return stars;
}
}
Как видите, мы используем лучшие практики, описанные в предыдущих частях! Сначала константа STAR_KEY
используется для предварительной загрузки ресурса со звездой, а затем для создания группы равномерно расположенных звезд в методе createStars()
.
Наш метод createStars()
возвращает группу физических объектов, которая в дальнейшем используется в методе create()
для добавления коллайдера, проверяющего столкновения звезд и платформ.
Мы сохранили логику, аналогичную официальному руководству Phaser. Просмотрите его, чтобы узнать более подробно узнать как в методе createStars()
создаются 12 звезд .
Откройте окно браузера и перейдите по адресу http://localhost:8000/ - должны появиться звезды, падающие с неба и приземляющиеся на землю и платформы.
Теперь займемся сбором звезд.
import Phaser from 'phaser'
//...
export default class GameScene extends Phaser.Scene
{
//...
create()
{
this.add.image(400, 300, 'sky');
const platforms = this.createPlatforms();
this.player = this.createPlayer();
const stars = this.createStars();
this.physics.add.collider(this.player, platforms);
this.physics.add.collider(stars, platforms);
// проверяем пересечение между персонажем и любой звездой в группе.
this.physics.add.overlap(this.player, stars, this.collectStar, null, this);
this.cursors = this.input.keyboard.createCursorKeys();
}
// отключаем физическое тело звезды
collectStar(player, star)
{
star.disableBody(true, true);
}
//...
}
Сбор звезд обрабатывается путем обнаружения пересечения между звездами и игроком. Мы это делаем в строке 20. В этой строке мы видим третий параметр - this.collectStar
. Это метод, который Phaser вызывает при обнаружении коллизии. Последний параметр в этой строке - this
. Он определяет область, из которой метод collectStar()
будет вызываться.
В методе collectStar(player, star)
мы просто отключаем физическое тело. Два логических параметра со значением true
, передаваемые в disableBody() говорят Phaser, чтобы отключить и скрыть GameObject
.
Вернувшись в браузер, вы сможете перемещать игрока по звездам и заставлять их исчезать!
Мы хотим выводить счет игрока, когда он собирает звезды, поэтому давайте создадим класс ScoreLabel
в src/ui/ScoreLabel.js
.
import Phaser from 'phaser'
const formatScore = (score) => `Score: ${score}`;
export default class ScoreLabel extends Phaser.GameObjects.Text
{
constructor(scene, x, y, score, style)
{
super(scene, x, y, formatScore(score), style);
this.score = score;
}
setScore(score)
{
this.score = score;
this.updateScoreText();
}
add(points)
{
this.setScore(this.score + points);
}
updateScoreText()
{
this.setText(formatScore(this.score));
}
}
Конечно, нет никакой необходимости создавать отдельный класс для нашей метки со счетом. В официальном руководстве Phaser вообще просто добавляется объект Text
на сцену.
Мы решили сохранить всю логику обновления результатов в одном месте. Благодаря этому наш класс GameScene
будет меньше и его будет легче читать.
Инкапсуляция общей логики для повторного использования - лучшая практика. Поскольку мы создали класс ScoreLabel
, мы можем использовать его снова в другой cцене без дублирования кода - не повторяйтесь!
Имея на руках классScoreLabel
мы можем добавить его в наш GameScene
.
import Phaser from 'phaser'
// добавляем импорт класса
import ScoreLabel from '../ui/ScoreLabel'
//константы
//...
export default class GameScene extends Phaser.Scene
{
constructor()
{
super('game-scene');
this.player = undefined;
this.cursors = undefined;
// добавляем свойство для хранения метки
this.scoreLabel = undefined;
}
//...
create()
{
this.add.image(400, 300, 'sky');
const platforms = this.createPlatforms();
this.player = this.createPlayer();
const stars = this.createStars();
// Создаем метку по координатам (16;16) с нулевым счетом
this.scoreLabel = this.createScoreLabel(16, 16, 0);
this.physics.add.collider(this.player, platforms);
this.physics.add.collider(stars, platforms);
this.physics.add.overlap(this.player, stars, this.collectStar, null, this);
this.cursors = this.input.keyboard.createCursorKeys();
}
collectStar(player, star)
{
star.disableBody(true, true);
// добавляем 10 очков к счету
this.scoreLabel.add(10);
}
// ...
//Метод создающий метку
createScoreLabel(x, y, score)
{
const style = { fontSize: '32px', fill: '#000' }
const label = new ScoreLabel(this, x, y, score, style);
this.add.existing(label);
return label;
}
}
Наша логика подсчета очков отличается от официального руководства Phaser .
Поскольку у нас есть отдельный класс ScoreLabel
, нам нужно создать его новый экземпляр в 50 строке в методе createScoreLabel(x, y, score)
, а затем добавляем объект метки на сцену вызовом метода this.add.existing()
.
В официальном руководстве Phaser вместо этого используется фабрика this.add.text()
. В обоих случаях создается объект Text
.
Одно заметное отличие состоит в том, что мы передаем в ScoreLabel
число, а не форматированную строку. Это еще одно применение принципа DRY, которое заключается в том, что нам не нужно выполнять одно и то же форматирование текста в более чем в одном месте. Это происходит только в методе updateScoreText()
класса ScoreLabel
.
Мы можем увидеть это в действии в строке 44, где мы добавляем 10 очков к счету каждый раз, когда собираем звезду.
Откройте окно браузера и перейдите по адресу http://localhost:8000/, там вы сможете получить 120 баллов, собрав все звезды.
Мы преобразовали в современный JavaScript 8 и 9 части из официального руководства по игре Создание вашей первой игры в Phaser 3.
В следующей, заключительной части, мы добавим бомбы, чтобы было немного интереснее играть!