HTML5 Phaser ECMAScript 6

Часть 13: Позиционирование элементов aling, alignTo, alignIn

Впервой версии Game.js все элементы интерфейса были расположены на фиксированных координатах. А мне хотелось по возможности динамичности! Я начал думать о создании класса на базе Phaser.Group, у которого была бы возможность располагать все child (помогите с переводом этого термина) равномерно, как например flex в css3.

Вот тут я чуть не изобрел велосипед. Опыт заставил перепроверить проверить доку на ключевое слово «align», и о чудо, на этот раз мне удалось найти сразу три варианта: align, alignIn и alignTo!

ВАЖНО: на первом месте в выдаче гугла ссылка на документацию 2.4.4, а в ней у Phaser.Group данных методов нет, они появились позже. В доке для 2.6.2 версии все ОК. По этому всегда смотрите документацию от последней версии (или той, которую используете)

Первым методом align() оказался как раз то, что мне нужно было. Достаточно ему передать размер таблицы (в моем случае 4х1), размер каждой ячейки и как каждый из child-ов группы должен в этой ячейке позиционироваться (всего 9 вариантов) и Phaser сделает все за вас =) Но неожиданным бонусом оказались методы alignIn() и alignTo(), которые дают возможность позиционировать один элемент относительно другого. Вы представляете как все упрощается?

Вот как я поступил: во первых позиционируем группу с главными кнопками относительно самой сцены this.game.world, после разделительную черту относительно главной группы и наконец, группу с вспомогательными кнопками относительно главной группы. Но тут возникла сложность. В вспомогательной группе всего 3 кнопки, а в главной 4. Что бы вспомогательные кнопки начали позиционирование со второй ячейки, я добавил первую невидимую кнопку в вспомогательную группу.

    this.main_button_list = this.game.add.group();
    this.button_learn = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_learn', this.on_button_learn_click, this));
    this.button_listen = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_listen', this.on_button_listen_click, this));
    this.button_write = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_write', this.on_button_write_click, this));
    this.button_game = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_game', this.on_button_game_click, this));
    this.main_button_list.align(4, 1, 420/4, this.main_button_list.height, Phaser.CENTER);
    this.main_button_list.alignIn(this.game.world, Phaser.BOTTOM_CENTER, 0, -20-20-20-this.main_button_list.height);

На тот, момент мне казалось, что создав невидимую кнопку. я как бы забронирую место (увеличу ширину группы). Оказалось, размер группы всегда пересчитывается и равняется минимальному размеру прямоугольника, в который может уместиться все элементы группы. Соответственно позиционирование по центру давало сбой:

попытка исправить позиционирование за счет добавления невидимой кнопки

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

правильное позиционирование за счет Phaser.BOTTOM_RIGHT

Как оказалось класс Phaser.Button больше подходит под эти задачи. Такой кнопке легко задать изображения состояния: нажата, отпущена, кликнута и т.д. А также можно задавать аудио, которое будет автоматом проигрываться.

И наконец я смог перейти к анимации. У меня было желание, что бы при нажатии на кнопку навыка, вспомогательная группа кнопок исчезала вправо, а слева появлялась панель с подсказкой (в мобильной версии у меня не много места, что бы подписывать кнпоку) и кнопкой запуска: Learn English (последняя версия)

Сейчас анимация реализована очень грубо (для теста). Все очень просто, группе вспомогательных кнопок меняем x координату на ширину размера сцены this.game.world.width, а панельке наоборот x координату из отрицательной — в положительную. Привожу содержимое Game.js файла:

import Skill from '../prefabs/Skill.js';
 
export default class Game extends Phaser.State {
 preload() {
 }
 
 create() {
 this.game.add.sprite(0, 0, 'menu_bg');
 
 this.menu_logo = this.game.add.sprite(this.game.world.centerX, 75, 'menu_logo');
 this.menu_logo.anchor.setTo(0.5);
 
 this.menu_skill_circle = this.game.add.sprite(this.game.world.centerX, 375, 'menu_skill_circle');
 this.menu_skill_circle.anchor.setTo(0.5);
 
 this.menu_avatar = this.game.add.sprite(this.menu_skill_circle.centerX, this.menu_skill_circle.centerY, 'menu_avatar');
 this.menu_avatar.anchor.setTo(0.5);
 
 this.skill_learn = new Skill(this.game, 191, 153, 'skill_learn_bg', 'skill_progress', 'skill_level', 'skill_icon_learn');
 this.game.add.group(this.skill_learn);
 
 this.skill_listen = new Skill(this.game, 62, 375, 'skill_listen_bg', 'skill_progress', 'skill_level', 'skill_icon_listen');
 this.game.add.group(this.skill_listen);
 
 this.skill_write = new Skill(this.game, 323, 375, 'skill_write_bg', 'skill_progress', 'skill_level', 'skill_icon_write');
 this.game.add.group(this.skill_write);
 
 this.main_button_list = this.game.add.group();
 this.button_learn = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_learn', this.on_button_learn_click, this));
 this.button_listen = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_listen', this.on_button_listen_click, this));
 this.button_write = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_write', this.on_button_write_click, this));
 this.button_game = this.main_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_game', this.on_button_game_click, this));
 this.main_button_list.align(4, 1, 420/4, this.main_button_list.height, Phaser.CENTER);
 this.main_button_list.alignIn(this.game.world, Phaser.BOTTOM_CENTER, 0, -20-20-20-this.main_button_list.height);
 
 this.menu_button_divider = this.game.add.sprite(0, 0, 'menu_button_divider');
 this.menu_button_divider.alignTo(this.main_button_list, Phaser.BOTTOM_CENTER, 0, 20);
 
 this.optional_button_list = this.game.add.group();
 this.button_null = this.optional_button_list.addChild(new Phaser.Button(this.game, 0, 0, null, null, this));
 this.button_achievement = this.optional_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_achievement', this.on_button_achievement_click, this));
 this.button_top = this.optional_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_top', this.on_button_top_click, this));
 this.button_settings = this.optional_button_list.addChild(new Phaser.Button(this.game, 0, 0, 'button_settings', this.on_button_settings_click, this));
 this.optional_button_list.align(4, 1, 420/4, this.optional_button_list.height, Phaser.CENTER);
 this.optional_button_list.alignTo(this.main_button_list, Phaser.BOTTOM_RIGHT, 0, 20+20);
 
 this.start_panel = this.game.add.group();
 var button_x = this.main_button_list.width - this.game.cache.getImage('button_start').width
 this.button_start = this.start_panel.addChild(new Phaser.Button(this.game, button_x, 0, 'button_start', this.on_button_start_click, this));
 var label_width = this.main_button_list.width - this.button_start.width - 24;
 var style = {font: 'bold 20px Arial', align: 'left', fill: '#F8C573', boundsAlignH: "left", boundsAlignV: "center", wordWrap: true, wordWrapWidth: label_width};
 this.label = this.start_panel.addChild(new Phaser.Text(this.game, 0, this.start_panel.centerY, 'уроки по изучению новых слов', style));
 this.label.anchor.setTo(0, 0.5);
 this.start_panel.alignIn(this.optional_button_list, Phaser.RIGHT_CENTER);
 this.start_panel.x = -this.world.width;
 
 this.progress_timer = this.game.time.create(false);
 this.progress_timer.loop(2000, this.update_progress, this);
 this.progress_timer.start();
 }
 
 update() {
 }
 
 on_button_learn_click() {
 console.log('button learn click');
 // TODO: replace fix 49 number with dynamic one
 if (this.start_panel.x != 49) {
 this.game.add.tween(this.start_panel).to({x: 49}, 1500, Phaser.Easing.Exponential.Out, true);
 this.game.add.tween(this.optional_button_list).to({x: this.game.world.width}, 1500, Phaser.Easing.Exponential.Out, true);
 } else {
 this.game.add.tween(this.start_panel).to({x: -this.world.width}, 1500, Phaser.Easing.Exponential.Out, true);
 this.game.add.tween(this.optional_button_list).to({x: 29}, 1500, Phaser.Easing.Exponential.Out, true);
 }
 }
 
 on_button_listen_click() {
 console.log('button listen click');
 }
 
 on_button_write_click() {
 console.log('button write click');
 }
 
 on_button_game_click() {
 console.log('button game click');
 }
 
 on_button_achievement_click() {
 console.log('button achievement click');
 }
 
 on_button_top_click() {
 console.log('button top click');
 }
 
 on_button_settings_click() {
 console.log('button settings click');
 }
 
 on_button_start_click() {
 console.log('button start click');
 }
 
 update_progress() {
 this.skill_learn.set_progress(this.game.rnd.integerInRange(1, 16));
 this.skill_listen.set_progress(this.game.rnd.integerInRange(1, 16));
 this.skill_write.set_progress(this.game.rnd.integerInRange(1, 16));
 }
}

Что мне не нравиться в этом подходе? Фиксированные числа! Я считаю анимация должна происходит по одному методу и не должна требовать от меня никаких чисел. Пока в мыслях создать группу по ширине самой сцены (а как это сделать?), тогда исходной x координатой всегда будет 0! Но это уже в следующем посте.

2 thoughts on “Часть 13: Позиционирование элементов aling, alignTo, alignIn

  1. Советую — потрать час-другой на просмотр всех примеров на сайте phaser, особенно кода
    Я так увидел этот самый align и еще кое-что
    Но нужного мне функционала так и не нашел. Может ты в курсе — в Phaser есть готовый компонент для прогресс-бара или его вручную создавать?

    Теоретически, не так сложно самому сделать — но вдруг я велосипед изобрету )

  2. Примеров там действительно много, но какой из них содержит решение моей проблемы? =) Я спецом не написал в этом посте, но на самом деле align не совсем то, что мне надо. У него нет возможности распределить элементы по всей ширине. Допустим у меня ширина под кнопки 400 точек, если создать таблицу из 4 клеток и задать каждой клетке по 100 точек, а ширина кнопки допустим 50, ты уже догадался какова будет ширина первой и последней кнопкой? Не 400!!! а всего 350… будешь ты позиционировать по левому краю, центру или правому. И решения данной задачи просто нет, так как align создан для других целей, не для интерфейса.

    На счет прогресса — я не думаю, что будут готовые решения, из за простоты задачи. Прогресс ведь у тебя одна фотка верно? Тогда все сводиться к нескольким строкам кода… ты читал как пост про мой Skill класс? Там как раз об crop и анимировании прогресса (у тебя задача проще, так как он слева на право. а не снизу вверх как у меня). Если что я могу попробовать сделать пример.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *