Перейти к основному содержанию

Создание игр: платформер

20 февраля 2019 г.

Создание игр: платформер

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

Скриншот финальной игры

Вот что мы сделаем:

Создание проекта

На главном экране перейдите во вкладку "Создать новый". В поле "Имя" введите название вашего проекта – например, «Платформер». Затем выберите папку, в которой ct.js будет хранить проект, например, в папке «Документы», и нажмите кнопку «Создать».

Импорт текстур

Нам понадобятся некоторые ассеты из пакета упрощенного платформера от Кенни. Эти ассеты уже включены в ct.js и красиво названы; вы можете найти их во встроенной галерее.

Перейдите во вкладку "Ассеты", нажмите на кнопку "Новый ассет", затем нажмите на "Встроенная галерея ассетов". Найдите пак "Kenney's Simplified Platformer" от Кенни и импортируйте необходимые текстуры, показанные на картинке. Затем закройте галерею; текстуры появятся в списке всех ассетов.

Как будете готовы, нажмите на ассет "PlatformChar_Walk1".

Редактирование кадров анимации

Эта текстура персонажа — изображение, состоящее из двух кадров анимации. Её можно условно представить в виде таблицы размером в одну строку и два столбца. Ct.js разделяет анимацию на кадры именно так, и нам здесь нужно в поле "Колонок:" поставить значение 2, а в поле "Строк:" — 1. Поля "Ширина" и "Высота" покажут размер одной ячейки. Они определяются автоматически при задании количества строк и колонок, но их можно изменять по необходимости.

Редактирование текстуры

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

Сначала нужно переместить ось вращения в нижнюю центральную точку. Это можно сделать двумя способами:

  1. просто перетащить её с помощью мышки (она отмечена на текстуре квадратиком);
  2. вручную ввести координаты в поле "Ось вращения".

Объяснение

Система координат в игровых движках отличается от системы координат, которая используется в геометрии. Например, в Ct.js ось Y перевёрнута и "растёт" при движении сверху вниз. Пиксели отсчитываются с верхнего левого угла.
Чтобы найти координаты точки внизу посередине, мы отступим от левого верхнего угла половину ширины кадра вправо и его целую высоту вниз. Поскольку у нас спрайт размером 86x80 пикселей, нам нужно взять 43 пикселя по горизонтали и 80 по вертикали. Первое значение точки обычно является её горизонтальным компонентом, или её X-компонентом, а второе — Y-компонентом.

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

Редактирование текстуры

Вы можете покрыть как тело с руками, так и выбрать только тело.

Нажмите кнопку "Применить" в правом нижнем углу.

Теперь нам нужно настроить маски столкновения для "PlatformChar_Jump" и "PlatformChar_Idle". Мы можем быстро сделать это, скопировав маску столкновения у "PlatformChar_Walk1" и вставив её в "PlatformChar_Jump" и "PlatformChar_Idle"! Убедитесь, что вы также смещаете ось на 43x80 для каждого из них.

Копирование маски текстуры
Вставка маски текстуры

Совет

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

Теперь давайте настроим маски столкновения наших кристаллов и бонусов-сердец. Для простоты можем сделать их в форме кругов. Откройте текстуру "PlatformPack_Item09" (Зелёный кристалл), установите его маску столкновения как "Круг", затем нажмите кнопку "По центру", чтобы ось автоматически настроилась на нужные значения, и настройте радиус формы столкновения.

Сделайте то же для текстуры "PlatformPack_Item17" (Сердце).

Редактирование кристаллов
Редактирование сердец

Последняя текстура, которую нам нужно изменить, это "PlatformPack_Tile43" (Шипы). Нам не нужно смещать её ось, потому что тогда она будет неправильно выравниваться на уровне, но мы всё ещё должны настроить её коллизию. Установите верхний отступ маски столкновений на отрицательное значение, чтобы персонаж в будущем сталкивался только с нижней частью тектуры, там, где нарисованы шипы.

Редактирование шипов

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

Создание робота-персонажа и платформ

Откройте вкладку "Ассеты" и нажмите кнопку "Новый ассет". В меню выберите "Шаблон". Назовите шаблон "Робот", установите спрайт PlatformChar_Idle и сохраните.

Редактирование шаблона

Совет

Шаблоны используются как основа для создания идентичных копий. Мы заполняем уровни (также называемые комнатами) копиями, и именно они взаимодействуют друг с другом на экране, но каждая копия всегда создаётся на основе определённого шаблона.

Создайте дополнительные шаблоны аналогичным образом (все текстуры скал):

Добавление комнаты

Нажмите "Новый ассет" ещё раз и выберите в меню «Комната». Назовите её «Уровень_01» и в панели «Свойства комнаты» с иконкой шестерёнки слева установите размер камеры 1024x576.

Редактирование комнаты

Затем нарисуйте свой уровень! Выберите инструмент «Добавить копии», щёлкните по шаблону слева и нарисуйте их мышью в большой области справа. Не забудьте о роботе!

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

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

Уровень платформерa Comigo

Теперь давайте изменим цвет фона. Нажмите инструмент «Свойства комнаты» снова и установите цвет фона на #D0F4F7.

Если мы сохраним проект сейчас и нажмём кнопку «Пуск» в верхней части экрана, мы увидим небольшую часть нашего уровня, нарисованного в окне отладки. Пока ничего не двигается, но это уже хорошее начало!

Окно отладки с размещенными копиями и фоном

Добавление модулей для клавиатуры и столкновений

Нам понадобится слушать события клавиатуры и обнаруживать столкновения между Роботом и землёй. Для этих суперспособностей нам понадобятся Котомоды! Нажмите на вкладку "Проект", а затем на вкладку "Котомоды" слева. Нажмите на модуль клавиатура в разделе доступных модулей, чтобы у него появился зелёный флажок и маленькая вращающаяся круглая точечка (возможно, он уже включен!). Сделайте то же самое с модулем place.

Включение модуля в ct.js

У каждого модуля есть своя документация во вкладке справа "Доки и заметки". Мы позже рассмотрим её подробнее.

Добавление действий для событий клавиатуры

Действия позволяют слушать (обрабатывать) события с клавиатуры, мыши, игрового контроллера и т.д. Более подробно о них можно прочитать здесь. С их помощью мы создадим слушатели для клавиш WASD и стрелок.

Совет

Несмотря на названия "слушатель" и "слушать", эти слова никак не связаны с восприятием звука человеком в контексте программирования. Для начала разберёмся, что такое "событие". Событие — это сигнал от игры о том, что что-то произошло. Например, нажатие кнопки, выход из игры, или смерть игрока. Слушатель или обработчик — это объект, который принимает уведомление о событии, то есть слушает событие. Например, кристалл может "слушать" событие столкновения с персонажем и, например, исчезать при получении уведомления о начале этого события.

Перейдите во вкладку Проект, а затем нажмите вкладку "Действия и методы ввода" слева.

Затем создайте схему ввода, как показано на следующем изображении. Для этого сначала нажмите кнопку "Создать с нуля", чтобы не использовать шаблон. Затем нажмите кнопку "Добавить действие", дайте ему название и добавьте методы ввода в правой колонке. Вы можете использовать функцию поиска, чтобы быстро добавлять необходимые клавиши.

Схема сопоставления ввода для простой платформерной игры в ct.js

Совет

Хоть эта схема может быть упрощена до всего двух действий (см. примеры на странице действий), у нас будет два отдельных действия для движения влево и вправо, чтобы не усложнять туториал.

Код коллизии и движения

Теперь перейдите во вкладку "Ассеты" в верхней части экрана и откройте шаблон Камни. В правом столбце заполните поле "Группа столкновений" значением Solid (пер. твёрдый, непроницаемый):

Добавление группы коллизий к шаблону

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

Добавьте ту же строку в шаблоны Земля и Земляная_платформа.

Теперь откройте шаблон Робот. Если вы ранее прошли руководство "Создание игр: космический шутер", вы можете помнить, что мы двигали наш корабль изменяя напрямую параметры копии, либо использовали встроенне переменные, такие как this.speed или this.direction. Правда, эти способы не будут работать с платформером, даже вне ct.js! Нам нужно написать что-то более сложное. Готовьтесь! 😃

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

Пришло время создать наш первый код. Для этого откройте шаблон "Робот" и слева внизу нажмите кнопку "Добавить событие". Найдите событие "Создание", затем добавьте следующий код:

JavaScript
this.jumpSpeed = -600;
this.gravity = 1800;

this.hspeed = 0; // Horizontal speed
this.vspeed = 0; // Vertical speed

Совет

this - это копия, которая выполняет написанный код. В нашем случае это Робот.

Теперь перейдите в событие "Начало кадра". Удалите строку this.move();, и добавьте следующий код:

JavaScript
this.movespeed = 240; // Max horizontal speed

if (actions.MoveLeft.down) {
    // If the A key or left arrow on a keyboard is down, then move to left
    this.hspeed = -this.movespeed;
} else if (actions.MoveRight.down) {
    // If the D key or right arrow on a keyboard is down, then move to right
    this.hspeed = this.movespeed;
} else {
    // Don't move horizontally if no input
    this.hspeed = 0;
}

// If there is ground underneath the Robot…
if (place.occupied(this, this.x, this.y + 1, 'Solid')) {
    // …and the W key or the spacebar is down…
    if (actions.Jump.down) {
        // …then jump!
        this.vspeed = this.jumpSpeed;
    } else {
        // Reset our vspeed. We don't want to be buried underground!
        this.vspeed = 0;
    }
}

Совет

"Начало кадра" выполняется каждый кадр для каждой копии. Движение и другая игровая логика обычно находятся здесь.

Совет

actions.YourAction.down проверяет, зажата ли в данный момент времени одна из указанных вами клавиш в данном действии. Также есть actions.YourAction.pressed (помогает отслеживать начало нажатия) и actions.YourAction.released (отслеживает конец нажатия кнопки).

place.occupied(copy, x, y, group) проверяет наличие столкновений с данной копией copy в заданных координатах x, y с другими копиями с группой group. Если группа не нужна, вы можете её не указывать. Этот метод возвращает либо false (нет столкновения), либо первую копию, которая столкнулась с copy.

Мы установили переменные hspeed (горизонтальная скорость) и vspeed (вертикальная скорость), но они ничего не делают сами по себе. Также мы не хотим застревать в текстурах или проходить сквозь стены, относящиеся к группе столкновений «Solid». К счастью, мы можем добавить эту волшебную строку в конец, чтобы персонаж правильно сталкивался с твёрдыми объектами:

JavaScript
this.moveSmart('Solid');

Совет

moveSmart — метод из модуля place, который постепенно перемещает копию пиксель за пикселем, останавливая её рядом с препятствиями. Это отлично подходит для платформеров и когда нужно точное движение вдоль ровных поверхностей.

Теперь мы можем перемещать нашего Робота!

Примечание

Ваш персонаж может игнорировать ямы шириной в одну клетку. Проверьте это. Если это происходит, вам нужно сделать коллизию Робота более худенькой (уменьшить ширину).

Настройка камеры для следования за роботом

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

Но её легко исправить. Если мы изучим документацию ct.js, мы найдем сущность camera с такими параметрами, как camera.follow, camera.borderX и camera.borderY, которые позволяют настроить следование за копией.

Откройте шаблон "Робот" и его событие создания. Добавьте следующий код в конец:

JavaScript
camera.follow = this;
camera.borderX = 450;
camera.borderY = 200;

Запустите игру и проверьте изменения. Теперь камера должна следовать за роботом.

Добавление ловушек и чекпоинтов

Мы добавим смертельные ловушки и водные преграды, а также чекпоинты, чтобы игрок после проигрыша мог продолжить игру с этих точек, а не с начала уровня.

Создайте новые шаблоны со следующими текстурами:

Создайте новую комнату и назовите её Уровень_2. Установите её размер в 1024x576 и снова сделайте фон цвета #D0F4F7. Создайте опасный уровень с шипами и прудами.

Поставьте точки чекпоинта до и/или после опасных участков. Не стесняйтесь ставить много, потому что жёстко наказывать игрока за любые ошибки — не самая хорошая идея! 😉

Второй уровень КоМиГо

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

Теперь давайте поговорим о шаблоне Чекпоинт.

Мы будем проверять столкновение с роботом, и когда они будут происходить, мы будем сохранять точку восстановления внутри копии робота. Создайте новое событие «Столкновение с шаблоном» и выберите Робота. Затем добавьте следующий код в событие:

JavaScript
other.savedX = this.x + 32;
other.savedY = this.y + 32;

:: tip
Событие «Коллизия с шаблоном» имеет специальную переменную other, которую можно использовать тольк в коде этого события. Эта переменная хранит ссылку на копию, которая столкнулась с нашим чекпоинтом — в нашем случаем, с роботом. Поглядывайте на предмет наличия подобных переменных при использовании разных событий!
:::

Здесь мы также смещаем сохранённую точку на 32x32 пикселей, потому что ось контрольной точки находится в её верхнем левом углу, а ось робота находится в средней нижней точке. Без смещения робот будет помещён чуть левее и выше желаемой центральной точки.

Также надо бы сделать контрольные точки невидимыми во время игры. Откройте раздел «Внешний вид» справа и отключите флажок «Отображать».

Сделать контрольную точку невидимой

Теперь перейдите к шаблону Шипы и установите их группу столкновений как «Deadly» (пер. «Смертельные») в правой колонке.

То же имя группы примените к шаблонам Вода и Вода_Верх.

Теперь откройте снова шаблон Робот, и добавьте новое событие «Коллизия с группой». В имени группы используйте «Deadly». Затем в коде события добавьте следующее:

JavaScript
this.x = this.savedX;
this.y = this.savedY;
this.hspeed = 0;
this.vspeed = 0;

Мы также должны написать этот код для события "Создание", чтобы по умолчанию точка возрождения находилась в месте создания, на случай, если наши способные игроки эпично облажаются:

JavaScript
this.savedX = this.x;
this.savedY = this.y;

Для тестирования конкретной комнаты откройте вкладку «Ассеты» вверху, затем щёлкните правой кнопкой мыши по нужной комнате и выберите «Сделать стартовой комнатой».

Трансформация и анимация робота

На этом этапе будет мудро добавить несколько анимаций к нашему роботу. Как вы помните, у нас есть три разных ассета под названием PlatformChar_Idle, PlatformChar_Jump и PlatformChar_Walk1.

Добавьте эту строку в код "Создание" робота:

JavaScript
this.animationSpeed = 0.2;

0.2 означает, что мы хотим воспроизводить 0.2×60 (что составляет 12) кадров в секунду. Для большей читаемости мы также можем записать это как 12/60.

Откройте код "Начало кадра" робота и измените раздел движения так, чтобы текстура, которая отображается, зависела от ввода пользователя и положения робота в пространстве:

JavaScript
this.movespeed = 240; // Max horizontal speed

if (actions.MoveLeft.down) {
    // If the A key or left arrow on a keyboard is down, then move to left
    this.hspeed = -this.movespeed;
    // Set the walking animation and transform the robot to the left
    if (this.tex !== 'PlatformChar_Walk1') {
        this.tex = 'PlatformChar_Walk1';
        this.play();
    }
    this.scale.x = -1;
} else if (actions.MoveRight.down) {
    // If the D key or right arrow on a keyboard is down, then move to right
    this.hspeed = this.movespeed;
    // Set the walking animation and transform the robot to the right
    if (this.tex !== 'PlatformChar_Walk1') {
        this.tex = 'PlatformChar_Walk1';
        this.play();
    }
    this.scale.x = 1;
} else {
    // Don't move horizontally if no input
    this.hspeed = 0;
    this.tex = 'PlatformChar_Idle';
}

// If there is ground underneath the Robot…
if (place.occupied(this, this.x, this.y + 1, 'Solid')) {
    // …and the W key or the spacebar is down…
    if (actions.Jump.down) {
        // …then jump!
        this.vspeed = this.jumpSpeed;
    } else {
        // Reset our vspeed. We don't want to be buried underground!
        this.vspeed = 0;
    }
} else {
    // If there is no ground
    // Set jumping animation!
    this.tex = 'PlatformChar_Jump';
}

this.moveSmart('Solid');

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

Робот теперь будет поворачиваться в сторону текущего направления и изменять текстуру (анимацию) в зависимости от движения. Вы только посмотрите на этого малыша!

Анимационный робот

Добавление переходов между уровнями

Задумка такая:

Создайте новый шаблон с названием "Выход". Установите ему текстуру "PlatformPack_Tile48". Затем у Робота создайте событие "Столкновение с шаблоном", выберите "Выход" и напишите следующий код:

JavaScript
// Is the next room defined?
if (rooms.current.nextRoom) {
    // Switch to the next room
    rooms.switch(rooms.current.nextRoom);
}

Совет

Здесь rooms.current указывает на текущую комнату. Функция rooms.switch выходит из текущей комнаты и переходит к другой комнате с указанным именем.

Теперь перейдите на вкладку "Ассеты", откройте уровень Уровень_1, нажмите кнопку "События" и введите следующий код в событии "Старт комнаты":

JavaScript
this.nextRoom = 'Уровень_2';

Поместите выход в комнату.

Сохраните комнату и пометьте Уровень_1 как стартовую комнату и протестируйте переход.

Самостоятельно!

Создайте дополнительные выходы, ведущие к секретным подуровням и обратно. Помните, что во встроенной галерее в этом паке есть ещё куча интересных текстур.

Подбираемые предметы: подсчёт и отображение

Добавление кристаллов

Создайте новый шаблон под названием Зелёный_кристалл и установите его спрайт. Создайте событие «Столкновение с шаблоном Робот» и напишите следующий код:

JavaScript
rooms.current.crystals ++;
this.kill = true;

Совет

this.kill = true; указывает, что текущая копия должна быть удалена из текущей комнаты. Это произойдёт после всех событий «Начало кадра», но до события «Конец кадра».

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

Но если мы продолжим добавлять больше функций в события комнат, мы скоро попадем в ловушку багов из-за того, что для каждой будущей комнаты прийдётся вручную копировать и вставлять большие фрагменты кода, а если мы захотим изменить логику, то также прийдётся и изменять его в каждой комнате. Это будет невероятно скучная работа для третьей и всех последующих комнат. (И у нас обязательно будет третья комната!)

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

Чтобы навсегда решить проблему повторяющегося кода в котэ есть специальный тип ассетов, называемый "Поведениями". Нажмите кнопку "новый ассет", в выпадающем меню выберите "Поведение" -> "Поведение для комнат", и назовите его ИнициализацияКомнаты.

Поведения для комнат имеют такие же события, как и сами комнаты. Создайте событие «Старт комнаты» и напишите следующий код:

JavaScript
rooms.current.crystals = 0;
rooms.current.crystalsTotal = templates.list['Зелёный_кристалл'].length;

Совет

templates.list['TemplateName'] возвращает массив всех копий данного шаблона в комнате. length возвращает длину массива.

Создание повторно используемого скрипта

Теперь перейдите в каждую комнату, нажмите кнопку "Свойства комнаты" в боковой панели, и затем нажмите на "Добавить поведение" и выберите только что созданное нами поведение.

Теперь это поведение подключено к комнате, и когда срабатывает событие "Старт комнаты", оно само устанавливает параметры crystals и crystalsTotal, не требуя писать такой код непосредственно в событиях комнаты.

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

К счастью, внутри ct.js есть инструмент для создания текстовых стилей. Откройте вкладку "Ассеты" и создайте новый стиль. Назовите его Счётчик_кристаллов.

В левой панели найдите раздел "Шрифт", установите размер шрифта 24, а толщину — в 600. Установите выравнивание по левому краю и высоту строки в 32 пикселя.

Настройка шрифта стиля

Затем откройте вкладку "Заливка", активируйте ее и установите цвет заполнения зеленым. Я выбрал #00A847. Другие хорошие варианты включают основные цвета кристалла, такие как #2ECC71 и #28B463.

Настройка цвета заполнения стиля

Мы также можем добавить толстую белую линию к нашему тексту, чтобы его было лучше видно на любом фоне. Откройте вкладку "Обводка", затем установите цвет линии в белый и ширину 5. Если вы не видите результат справа, попробуйте изменить цвет фона кнопкой с каплей внизу.

Настройка стиля линии

Теперь нам нужно создать два новых шаблона, Виджет_кристалл и Виджет_кристалл_текст. Первый будет отображать иконку кристалла, а второй - счётчик. Установите шаблону Виджет_кристалл текстуру зелёного кристалла, а в Виджет_кристалл_текст установите его тип на Текст вместо Анимированного Спрайта в выпадающем меню под иконкой призрачного кота. Теперь установите стиль Счётчик_кристаллов слева сверху.

Наконец, добавьте это в код события "Начало кадра" Виджет_кристалл_текст:

JavaScript
this.text = `${rooms.current.crystals} / ${rooms.current.crystalsTotal}`;

Нам понадобится создать специальную комнату для наших элементов интерфейса. Создайте новую комнату и назовите ее Слой_интерфейса. Установите её размер такой же, как и у других комнат, 1024x576. Затем добавьте только что созданный Виджет_кристалл и Виджет_кристалл_текст в верхний левый угол комнаты:

Добавление виджета кристаллов в слой интерфейса

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

Добавление виджета кристаллов в слой интерфейса

Добавление элементов интерфейса в отдельную комнату позволяет проектировать интерфейс визуально, а затем импортировать его в другие комнаты с помощью кода. Ct.js также имеет специальный флажок, который маркирует слои интерфейса. Вы сможете свободно перемещать, масштабировать и даже поворачивать камеру, а элементы интерфейса останутся правильно расположенными. Перейдите в настройки комнаты и установите флажок "Слой для графического интерфейса", чтобы Слой_интефейса был зафиксирован на экране игры.

Включение слоя как слоя интерфейса

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

JavaScript
rooms.append('Слой_интерфейса');

Вот как должен выглядеть код:

Полный код добавления слоя интерфейса в ct.js

Совет

Метод rooms.append (а также rooms.prepend) также можно использовать для повторного использования других вещей, помимо слоев интерфейса. Например, мы можем поместить все фоновые изображения в отдельный слой, а затем вызвать rooms.prepend("YourBackgroundRoom");, чтобы импортировать их. Это особенно полезно при создании сложных слоенных фонов с эффектом параллаксного сдвига.

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

Счетчик кристаллов

Добавление жизней и бонусов-сердец

Подбирание сердец в основном похоже на сбор кристаллов, но есть некоторые изменения:

Сделай сам!

Попробуй сделать всё самостоятельно! Если ты запутался, проверься с инструкцией ниже. Всё, не скролль вниз! 😃

Создайте новый шаблон под названием Сердце и назначьте соответствующий спрайт. Добавьте следующий код в событие "Столкновение с шаблоном Робот":

JavaScript
if (rooms.current.lives < 3) {
    rooms.current.lives++;
    this.kill = true;
}

Не забудьте разместить копии сердца на своих уровнях!

Мы также должны создать стиль для счётчика жизней. Процесс тот же, и подходящий цвет - #E85017. Мы даже можем дублировать существующий стиль! Назовите этот стиль Счетчик_сердец.

Нам нужны ещё два шаблона для здоровья. Создайте новый шаблон под названием Виджет_сердце, и поставьте текстуру сердца. Затем создайте Виджет_сердце_текст и установите его как Текст, а не Анимированный Спрайт. Теперь примените стиль Счетчик_сердец.

Добавьте следующий код в конец события Начало кадра шаблона Виджет_сердце_текст:

JavaScript
this.text = rooms.current.lives;

Затем добавьте оба этих новых шаблона в слой Слой_интерфейса. Не забудьте настроить выравнивание текста сердца!

Измените код респавна шаблона Робот, чтобы он терял одну жизнь при каждом респавне (в событии со столкновением с группой «Deadly»):

JavaScript
this.x = this.savedX;
this.y = this.savedY;
this.hspeed = 0;
this.vspeed = 0;
// remove one life
rooms.current.lives --;
if (rooms.current.lives <= 0) {
    // Restart a room: switch to the room of its own name
    rooms.switch(rooms.current.name);
}

И не забудьте отредактировать поведение инициализация_комнаты, чтобы инициализировать количество жизней в комнате:

JavaScript
rooms.current.crystals = 0;
rooms.current.lives = 3;
rooms.current.crystalsTotal = templates.list['Зелёный_кристалл'].length;
rooms.append('Слой_интерфейса');

Всё! Пора тестировать.

Добавление движущихся платформ

Создайте новый шаблон под названием Платформа и выберите соответствующую текстуру. Создайте новый уровень под названием Уровень_3, который состоит из длинных ловушек с шипами и движущихся платформ.

Пример третьего уровня

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

Давайте создадим и откроем шаблон деревянной платформы и инициализируем его скорость:

JavaScript
this.speed = 120;

Также, установите группу столкновений платформы как Solid в правом столбце редактора шаблонов.

Затем, добавьте следующий код в раздел "Начало кадра":

JavaScript
var robot = place.meet(this, this.x, this.y - 1, 'Робот');
if (robot) {
    robot.x += this.hspeed * u.time;
}

И логику движения в то же событие "Начало кадра":

JavaScript
if (place.occupied(this, this.x + this.hspeed * u.time, this.y, 'Solid')) {
    // Flip direction
    this.direction += 180;
}
this.move();

Выглядит просто! Может быть, даже слишком просто. А проблема в том, что если Робот коснётся левой или правой стороны платформы, он застрянет навсегда! Нам нужно сделать платформы твёрдыми только в том случае, если Робот и платформа уже не пересекаются друг с другом.

Проблема с платформами

Вот улучшенный код:

JavaScript
var robot = place.meet(this, this.x, this.y, 'Робот');
if (robot) {
    this.cgroup = undefined;
} else {
    this.cgroup = 'Solid';
    robot = place.meet(this, this.x, this.y - 1, 'Робот');
    if (robot) {
        robot.x += this.hspeed * u.time;
    }
}

if (place.occupied(this, this.x + this.hspeed * u.time, this.y, 'Робот')) {
    // Flip direction
    this.direction += 180;
}
this.move();

Что тут происходит? Во-первых, мы проверяем, не пересекается ли робот с платформой. Если пересекается, мы говорим, что платформа теперь не твёрдая с помощью строчки cgroup = undefined, чтобы робот мог пройти через платформу вместо того, чтобы застрять в ней. 'cgroup' - это поле группы столкновений, которое мы редактировали в левой колонке редактора шаблона! Если между платформой и роботом нет столкновения, платформа становится обратно твёрдой (cgroup = 'Solid'), и мы снова ищем робота, но теперь на один пиксель выше платформы. У робота идеальное попиксельное движение, поэтому одного пикселя достаточно, чтобы понять, стоит ли он на текущей платформе.

Сделай сам!

Добавь движущиеся вертикально платформы! И убедись, что они не раздавят Робота. 😉

Вот и всё!

Уф, это был довольно длинный туториал. Несмотря на это, есть ещё много возможностей для улучшения.

Вот как можно улучшить эту игру:

Заметки на полях

Посмотрите, как новые функции в вашем коде постепенно появляются в ваших уровнях! Это хороший способ представить игрокам новые механики. Добавляйте по одному нововведению за раз, но сохраняйте предыдущие механики с увеличивающейся сложностью. Это совет по проектированию уровней от Comigo 😎

Счастливого кодинга!
КоМиГо

Your primary language is en-US, do you want to switch to it?

Ваш основной язык - en-US, вы хотите переключиться на него?