Кросс-платформенная разработка мобильных игр на Cocos Creator — Урок 4: Создание платформ

Всем привет и добро пожаловать на 4 урок нашего курса!
В этом уроке мы займемся работой с платформами. И нашей главной целью будет являться автоматическое программное создание платформ!

И цели сегодняшнего урока следующие:
Создать компонент Platform, ответственный за инициализацию платформы
Понять что такое префаб и создать префабы тайла и префаб самой платформы
Создать компонент Platforms и сгенерировать в нем заданное число платформ с заданными характеристиками
Заставить все платформы двигаться справа-налево

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

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

Создадим новый компонент Platform и этот скрипт будет отвечать за логику конкретной платформы. Прикрепим данный скрипт непосредственно к пустой пока ноде Platform.
И договоримся, что за генерацию платформы у нас будет отвечать наш собственный метод init.

   init(tilesCount, x, y) {

у которого будет 3 параметра:
Число тайлов для генерации, координаты x и y;
Вызовем этот метод из метода start, который как мы знаем вызывается перед активацией компонента, так что платформа должна быть сгенерирована автоматически с нужными нам параметрами.

Давайте посмотрим на текущие координаты созданной на экране платформы и определимся, в каких координатах должна быть новая платформа.
А теперь перейдем к реализации метода init.
В методе init нам потребуется выполнить следующие 4 действия:

init(tilesCount, x, y) {
   // поместить ноду в нужную позицию
   // создать нужно число тайлов
   // обновить размер ноды
   // обновить размер коллайдера
},

Идем по порядку.
Поместить ноду в указанные координаты проще простого и делается это в 1 действие:

   this.node.setPosition(cc.v2(x, y));

Мы используем x y из параметров метода. Мы можем создать нужное число тайлов в цикле:

   // создать нужно число тайлов
   for (let i = 0; i < tilesCount; i++) {
   }

Как именно создается тайл?
Здесь мы подходим к очень важному понятию в работе с Cocos Creator — префабы.
Что такое тайл платформы?
Давайте вернемся в редактор и посмотрим на текущую платформу, которую мы сделали вручную из тайлов. Как вы видите, тайл — это простая нода. один из компонентов которой является спрайтом.
Значит, чтобы создать тайл, мы должны создать ноду с компонентом спрайта, у которого будет нужное значение.
Тогда как создать ноду в creator? Мы могли бы создать ноду программно, например так:

const node = new cc.Node();

И тогда нам пришлось бы вручную добавить ноде нужный компонент:

node.addComponent(cc.Sprite)

и кроме того, проставить правильные спрайт компоненту:

node.getComponent(cc.Sprite).spriteFrame = ?

Но мы можем эти действия упростить. Вместо того, чтобы создавать пустую ноду, программно добавлять ей компонент спрайт, программно устанавливать этому компоненту нужное изображение, мы просто инициализируем ноду с уже предустановленными значениями. Такая нода и является префабом. Давайте я продемонстрирую, как создать префаб.
Итак, у нас есть некоторая нода. Данная нода platform имеет комопнент cc.Sprite, который представлен тайлом платформы.
Если мы зажмем кнопку мыши на этой ноде и перенесем ее в панель assets, то увидим, как Cocos Creator из данной ноды сделает префаб.
Если мы теперь 2 раза кликнем по созданному префабу, то мы узнаем, как он устроен.
Это та-же самая нода, но теперь она не привязана ни к какой сцене. У этой ноды абсолютно все те-же самые свойства. у нее тот-же компонент спрайта с той-же картинкой, но теперь этой ноды нет на сцене и у нее нет никакой родительской ноды.

При этом префаб — это некоторый шаблон реальной ноды, которая может быть на сцене. Это значит, что когда мы захотим инициализировать данный префаб и превратить его в ноду, мы по сути построим новую ноду по чертежу. Но сам чертеж никуда не денется. И префаб останется доступен нам. А это значит, что мы можем инициализировать столько нод из данного префаба, сколько нам угодно и все они будут точными копиями самого префаба, но уже реальными объектами на сцене со своей родительской нодой.

Давайте теперь так и сделаем.

Во-первых нам необходимо знать, из какого именно чертежа конструировать ноду. Мы можем прямо в собственном кастомном свойстве компонента Platform указать данный префаб:

       tile: {
           default: null,
           type: cc.Prefab,
       },

Теперь в редакторе перетащим префаб в данное свойство и теперь у нас есть прямой доступ к нужному префабу.
Теперь в цикле создания тайлов мы выполняем следующий код:

   // создать нужно число тайлов
   for (let i = 0; i < tilesCount; i++) {
       // инициализировать ноду из префаба
       const tile = cc.instantiate(this.tile);
       //  сделать созданную ноду дочерней для ноды данного комопнента
       this.node.addChild(tile);
       // установить в правильную позицию
       tile.setPosition(i * tileSize, 0);
   }

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

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

   // обновить размер ноды
   this.node.width = tileSize * tilesCount;
   this.node.height = tileSize;

   // обновить размер коллайдера
   let collider = this.node.getComponent(cc.PhysicsBoxCollider);
   collider.size.width = this.node.width;
   collider.size.height = this.node.height;

Для удобства установим точку отсчета длины для самой ноды в ее левый нижний угол. С учетом того, что anchor point ноды мы установим в левый нижний угол, нам также нужно сместить точку отсчета и для коллайдера. Делаем это с помощью свойства offset

  collider.offset.x = this.node.width / 2;

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

   collider.apply();  

Теперь проверим, что у нас получилось. Мы ожидаем, что данная пустая нода platform будет представлена в виде платформы из 3 тайлов на экране в координатах.
И так и есть.

А теперь перейдем к динамическому созданию таких нод. Мы разумеется не будем предварительно вручную создавать под каждую новую ноду свою пустую ноду platform.

Вместо этого создадим одну ноду под названием platforms и поместим ее рядом с нодой героя. Эта нода будет ответственна за создание всех платформ в игре. И создаваемые данной нодой платформы будут являтся дочерними для самой ноды platforms. Таким образом platforms — это некоторый пул платформ, генерируемых динамически.

Создадим компонент Platforms и прикрепим его к ноде platforms.
В этом скрипте мы реализуем логику динамического создания платформ.
В данном конкретном уроке мы создадим нужное число платформ из предопределенного конфига. А в следующем уроке мы будем создавать платформы динамически со случайными характеристиками в нужное время.

Договоримся, что за создание платформы будет отвечать метод createPlatform.
Это метод будет принимать конфигурационный объект с параметрами создаваемой платформы.
Вызовем этот метод из метода start:

       this.createPlatform({
           tilesCount: 3,
           x: -280,
           y: -200
       });

Реализуем метод createPlatform.
Давайте определимся, что мы хотим от этого метода.
Мы хотим, чтобы этот метод создавал нужную нам ноду с нужными параметрами. Очень похоже на использование префаба, которое мы только что рассматривали.
И действительно, давайте возьмем текущую пустую ноду platform и превратим ее саму в префаб. Перенесем ее в assets в папку prefabs.

Создадим кастомное свойство

       platform: {
           default: null,
           type: cc.Prefab,
       },

Которое будет указывать на нужный префаб. Прямо укажем нужный префаб в качестве значения свойства данного компонента в редакторе.
И теперь мы можем инициализировать данный префаб уже знакомым нам способом:

   createPlatform(data) {
           const node = cc.instantiate(this.platform);
           this.node.addChild(node);
   },

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

           const platform = node.getComponent('Platform');

И мы знаем, что у этого компонента есть метод init. Значит мы можем его вызвать:

       platform.init(data.tilesCount, data.x, data.y);

Тогда нам больше не требуется вызывать init в методе start компонента Platform. Вместо этого мы будем вызывать init извне данного класса, а именно при создании объектов в Platforms и передать нужные параметры оттуда.

Давайте добавим еще платформ и проверим, что у нас получилось.

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

Ну а теперь давайте решим эту задачку вместе.
Раз мы хотим двигать платформу, то логику движения мы должны поместить в компонент, который за платформу отвечает. У нас это наш собственный компонент — Platform.
А теперь ответим на все перечисленные вопросы.
Если мы будем изменять координату x ноды из свойства this.node.x, мы сможем изменять положение платформы на оси x
Мы знаем, что метод update в компоненте вызывается многократно и вызывается перед отрисовкой каждого нового кадра на экране. Значит мы можем в методе update изменять координату x ноды платфомы на незначительное значение

   update (dt) {
       this.node.x -= 150 * dt;
   },

Параметр dt здесь описывает время, которое прошло со момента прошлого вызова update и служит для сглаживания анимации движения.
Мы изменяем значение позиции по оси x в отрицательную сторону потому что чем левее нода на сцене, тем меньшее ее координат x.

Что-ж, выполним финальную проверку. Протестируем текущую версию игры.

Подведем итоги.
На этом уроке мы разобрали, что такое префаб.
Мы использовали префабы в 2 местах:
для динамической генерации тайлов в платформе и для динамической генерации самих платформ, состоящих из тайлов
Мы создали уровень из конфига, сгенерировав заранее заданное число платформ с заданными характеристиками

Такой подход может быть использован, если вы хотите сделать не бесконечную игру, а игру с заранее заскриптованными уровнями, что также часто является целью разработчика.

И наконец, мы задали платформам движение по оси x в левую сторону для создания иллюзии бега героя

На следующем уроке мы займемся полноценной случайной бесконечной генерацией платформ в случайных позициях, а не по заранее созданному конфигу, как это работает в данный момент.

Leave a reply:

Your email address will not be published.

Site Footer