Создание СУХОГО дизайна с помощью Phaser 3

При создании игры с несколькими уровнями может показаться, что вы переписываете один и тот же код снова и снова. Не проще ли написать один шаблон для своего уровня и вставлять в него меняющиеся данные? Это руководство поможет вам создать СУХОЙ дизайн, который будет выглядеть чище и будет легче писать код, избегая этого повторения.

Различные фазы Phaser 3

Когда игра создается с помощью Phaser 3, она проходит три основных этапа. Первый шаг — предварительная загрузка. Это раздел, в котором в игровой мир загружаются любые активы, чтобы мы могли их использовать. Ничего из того, что есть в предзагрузке, в игре пока не видно. Только сейчас они доступны для использования в нашей игровой среде. Следующий шаг — создание. Именно здесь мы берем любые активы, которые мы добавили на этапе предварительной загрузки, и помещаем их в физическую среду нашей игры, чтобы пользователи могли взаимодействовать с ними. Теперь на экране появляются спрайты, теперь воспроизводятся звуки, и большинство функций, которые мы создаем, попадут в этот раздел. Завершающим этапом является обновление. Обновление интересно тем, что повторяется и повторяется часто. Если вам интересно, как часто запускается обновление, закиньте лог консоли в этот раздел и посмотрите, как быстро увеличивается количество логов консоли. (Не делайте этого слишком долго, если только вы не хотите серьезно замедлить работу вашей системы.) Это важно учитывать, потому что мы хотим быть осторожными при использовании этого раздела. Если он повторяется так быстро, и даже простой журнал консоли так сильно замедляет работу, важно убедиться, что мы добавляем в обновление только то, что необходимо. Обычно в этот раздел попадают только обновления перемещений игроков.

Если вы сомневаетесь, расставьте все по своим местам

Теперь, когда мы знаем различные части нашей сцены, у нас есть область для экспериментов. Первый вопрос, который мы должны себе задать: «Что меняется на каждом уровне, а что остается неизменным?» Скорее всего, некоторые части нашей игры останутся прежними. Это может немного сбивать с толку, если каждый раз, когда игрок начинает новый уровень, он обнаруживает, что управляет новым персонажем (если вы хотите сбить с толку, не стесняйтесь делать это). Функции, которые мы будем использовать для каждого уровня, движения и логика работы нашей игры. Все эти вещи, как правило, одинаковы для каждого уровня. Вещи, которые могут отличаться от уровня к уровню, — это дизайн карты, расположение активов, внешний вид врагов. Логика того, как каждый предмет взаимодействует со сценой и как игрок взаимодействует с предметом, остается прежней, но небольшие аспекты меняются между уровнями, что делает их уникальными. Итак, как мы это сделаем? Для карт и спрайтов все, что нам нужно сделать, это передать разные источники для каждого изображения в предварительной загрузке. Например, при загрузке карты тайлов мы обычно пишем код, который выглядит примерно так:

this.load.tilemapTiledJSON(“map”,‘assets/backgrounds/jsonFiles/levelOne.json’)

Вместо этого, что, если мы создадим массив, содержащий исходный код для всех наших карт, и передадим в загрузку определенный индекс этого массива?

const tileMaps = [
‘assets/backgrounds/jsonFiles/levelOne.json’,
‘assets/backgrounds/jsonFiles/levelTwo.json’,
‘assets/backgrounds/jsonFiles/levelThree.json’
];
this.load.tilemapTiledJSON(“map”, tileMaps[this.game.level])

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

this.player = new Player(this, 160, 190, “player”)

Мы могли бы передать ему статическую позицию x и y, чтобы наш игрок начинал с одного и того же места на каждом уровне. Или мы могли бы создать массив объектов для каждого уровня, состоящий из ключей x и y. И пройти это вместо этого.

const playerStartPosition = [
{ x: 11, y: 6 },
{ x: 15, y: 5 },
{ x: 14, y: 5 }
];
this.player = new Player(this, playerStartPosition[this.game.level].x, playerStartPosition[this.game.level].y, “player”)

Установите Phasers для повторного запуска

Это все хорошо, но на данный момент каждый из этих разделов запускается только один раз (за исключением, конечно, нашего милого друга, обновления), так как же нам заставить нашу сцену перезагружаться для каждого уровня? И как мы сообщаем нашему массиву, какие индексы он должен смотреть? Все, что нам нужно сделать, это создать функцию, которая запускается, когда мы выходим из уровня. Есть несколько способов сделать это, но один из них — создать невидимую зону (наш «выход»), которая, когда игрок достигает ее, запускает нашу функцию выхода. К счастью, в Phaser есть игровой объект под названием Zone и функция перекрытия, которую мы можем запустить, когда объект игрока встречает выход.

this.exit = this.physics.add.group({classType: Phaser.GameObjects.Zone});
this.exit.create( 32, //x position 32, //y position 32, //height,
32 //width);
this.physics.add.overlap(this.player, this.exit, 
this.exitLevel, //our exit function null, this);

Итак, для чего нам нужна наша функция выхода? Нам нужно, чтобы он сказал нашей игре перейти к следующему индексу нашего массива. Вы могли заметить, что в некоторых местах кода выше мы передаем переменную с именем this.game.level в качестве позиции индекса. Вот как мы можем сказать нашей игре, когда переходить к следующим индексам в массиве! Для начала в конструкторе этой сцены мы можем создать глобальную переменную с именем this.game.level и установить ее равной 0. Причина, по которой мы установили ее как this.game.level вместо this.level, заключается в том, что она доступна. для всех наших сцен, на тот случай, если мы захотим использовать его и в других сценах. И причина, по которой мы устанавливаем его равным 0 вместо 1, заключается в том, что первый индекс массива равен нулю. Теперь, когда игрок касается выхода, мы хотим, чтобы наш уровень увеличился на 1, чтобы мы перешли к следующему индексу нашего массива. Итак, в нашу функцию выхода мы поместим:

this.game.level++

Хорошо, но нам все еще нужно, чтобы наша игра перезагрузилась. К счастью, в Phaser 3 была функция и для этого! Все, что нам нужно поместить в нашу функцию, это:

this.scene.restart()

И бум! Все три шага нашей сцены (предварительная загрузка, создание и обновление) выполняются снова, но теперь ей передается наша информация из индекса 1 наших массивов! Ура! Следует иметь в виду одно важное замечание: Phaser 3 любит помогать нам, имея некоторое хранилище, которое он называет кешем, где он хранит информацию, которую мы уже предварительно загрузили, например аудио. Таким образом, вместо того, чтобы смотреть на нашу предварительную загрузку, он просто берет для нас информацию из кеша. Очень удобно, если мы каждый раз делаем одну и ту же музыку, но не очень удобно, если мы хотим проверить предварительную загрузку для нового источника. Это легко исправить. Все, что нам нужно сделать, это удалить элементы из кеша в нашей функции выхода, прежде чем мы вызовем перезагрузку. Например:

this.music.destroy();
this.cache.audio.remove("background")

Это хорошая идея — занести в консоль журнал this.cache, чтобы увидеть, какие активы могут там скрываться, и удалить их из кеша перед перезапуском. Если сомневаетесь, войдите в консоль!

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

Удачного кодирования!