Создание вашей первой игры Phaser 3 на современном Javascript. Часть 4.

В третьей части мы рассмотрели столкновения между платформами и игроком и добавили элементы управления с клавиатуры. Если вы еще этого не сделали, то перейдите к третьей части и выполните указания в ней.

Мы закончили третью часть созданием персонажа, который может двигаться и прыгать по платформам.

Теперь нам нужно создать несколько звезд.

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.

В следующей, заключительной части, мы добавим бомбы, чтобы было немного интереснее играть!

Оригинал.