Подготовка персонажа для Blend4Web

На сайте разработчиков движка опубликовано множество материалов, в том числе и по этой теме. Все «разжевано» и на русском языке. Но вот казус — задача оказалась не простой, даже для человека имеющего солидный опыт работы с Blender и искренне считающего себя game developer`ом. Некоторые этапы вызывали критические приступы “зависания”, происходящих от недопонимания особенностей работы с движком и, откровенно говоря, слабого знания JavaScript.

Немаловажным фактором была попытка переноса опыта работы с Unity (c#) на абсолютно иную платформу. И это было серьезной ошибкой. Blend4Web — инструмент заточенный исключительно для создания приложений WebGL, что дает солидное преимущество перед кроссплатформенным Unity, но и накладывает некоторые ограничения. В связи с прекращением поддержки веб-плеера и слабой работоспособностью экспортера Unity WebGL, платформа Blend4Web выглядит очень интересной. Поэтому, хочешь — не хочешь, а разбираться приходится.

Этот урок — компиляция собственного опыта, подсказок разработчиков b4w, официальной документации. Рассчитан, в первую очередь, на начинающих и основан на моем игровом проекте.
Игровой персонаж, в моем понимании — это объект способный перемещаться, “думать” и взаимодействовать с другими объектами. Реализацию AI я оставлю для следующей статьи, а пока разговор пойдет о базовых действиях.

Blend4Web основан на Blender, поэтому все настройки моделей и сцен выполняются в этом редакторе, что выгодно отличает b4w от других решений.

Игровой уровень можно создать разными способами:

  • Статичный. Все модели, объекты заранее располагаются на базовой сцене. При этом используются стандартные функции Blender для подключения объектов из сторонних blend-файлов (Link). Таким образом, весь уровень целиком загружается в память, а разработчик имеет возможность доступа к объектами с помощью API Blend4Web. Это простой способ, но у него есть недостаток — долгая загрузка, что критично для браузерных игр.
  • Динамичный. Имеется основная сцена, а все необходимые объекты являются самостоятельными файлами и подгружаются с помощью API Blend4Web.


По задумке, в игре есть рыбки — злые и не очень, быстрые и медленные, большие и маленькие. Если их жестко разместить на уровне, то реиграбельность будет не на высоте. Куда интереснее, если они будут генерироваться в разных местах, качестве и количестве. Да и сетевой трафик экономится. Поэтому я однозначно выбрал второй вариант загрузки.

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

Получается, что от персонажей требуется два главных действия:

  • Следовать по маршруту.
  • Отклоняться от траектории для выполнения задания или при встрече с другим объектом.


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

Шаг 1. Настройка модели в Blender


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

Файлы сцен (blend) должны располагаться в директории sdk\blender\project.
Файлы ресурсов, равно как и экспортированные JSON из сцен, размещаются в sdk\deploy\assets\project.
Скрипты, HTML, CSS в папке sdk\apps_dev\project.

Движок b4w работает не с оригинальными файлами blend, а с форматом JSON. Экспорт выполняется из меню Blender: File | Export | Blend4Web (JSON). Учтите, что экспортер перегоняет всю сцену Blender. Поэтому нужно удалить из нее не задействованные в игре объекты, например, высокополигональный оригинал. Конечно, API позволяет выбирать из сцены нужные объекты, но загружаться по сети будет весь файл целиком.

Вторая особенность — это использование модификаторов. Нет необходимости применять их и, тем самым, отсекать себе возможность корректировать модель. Просто поставьте галочку в опции Apply Modifiers или даже лучше в Apply Scale and Modifiers. Эти параметры находятся в панели Object выделенного объекта.

f913a81531b645fb965f236cd39ea705.jpg

Важно давать человекопонятные названия материалам, объектам, текстурам. Ведь в дальнейшем доступ из кода к ним выполняется с использованием имен. Годятся латиница, знак “подчеркивание” и цифры. Стоит избегать стандартных для Blender автоматических названий, где используются точки.

Простейший способ проверки настроек модели — это просмотр ее в браузере при помощи специальной утилиты. Программа может запускаться автоматически после экспорта JSON, если включите опцию Run in Viewer (слева на панели B4W Export JSON). Учтите, что опция доступна только при сохранении проекта в корректной для SDK папке sdk\blender\.

Просмотрщик позволяет покрутить, рассмотреть модель со всех сторон, корректировать параметры сцены, а самое главное — сигнализирует о возможных проблемах с JSON. Маленький светофорчик в углу экрана (см. рис.) предупреждает о состоянии “пациента”. Красный и желтый — значит что-то не в порядке и стоит просмотреть логи в консоли браузера.

2115b00becf245f2b14eaf3c26cd1881.jpg

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

9f39e7186ec64cdab0254444fb16d813.jpg

Для этого я добавил в сцену новый примитив Cube. Сделал его корневым (parent) по отношению к модели, а также сгруппировал всё в одно целое. Если вы не знаете, как это сделать — вот пошаговый алгоритм:

  • Выделить модель, затем коллайдер. Нажать клавиши CTRL+P и в появившемся меню выбрать Object.
  • Для группировки выделенных объектов нажать CTRL+G.


После этих действий коллайдер с именем Fish стал корневым (см. рис. выше). Именно он будет участвовать в физических действиях, перемещаться по сцене, а моделька получается, всего лишь “пассажир”.

Итак, все физические настройки выполняются в отношении куба-коллайдера. Первое что нужно сделать — это отключить визуализацию в рендере движка, ведь нужна только его форма, а не вид. Поставьте галочку в опции Do Not Render в настройках, как на рисунке выше. Второе — включить опцию Force Dynamic Object. Это необходимо для работоспособности некоторых нужных функций API. А вот все остальные параметры находятся в панели Physics.

fec20dbf7bdd4d5b80ddd2f5704df743.jpg

В действительности физику объекта в Blend4Web очень легко активировать. Достаточно установить галочку в Object Physics и модель будет взаимодействовать с окружающим миром. Тип взаимодействия определяется параметром Type. На рисунке выбран Dynamic и это не случайно, ведь модель рыбы подвижная. Для неподвижных элементов сцены, таких как стены, лестницы и иные препятствия нужно устанавливать тип Static.

Вид коллайдера выбирается в закладке Collision Bounds. На рисунке установлен Box, как наиболее подходящая форма для параллелепипеда. Разумеется для своей модели вы можете выбрать любой из имеющихся физических примитивов.

И последнее — опция Charaster. По названию уже понятно, что она активирует. Да, действительно, разработчики Blend4Web позаботились о пользователях и добавили быстрые настройки основных параметров движения персонажа: ходьба, прыжок, бег, шаг и т.д. Конечно, к рыбкам это особо не применимо, но без включения этой опции не будут работать некоторые функции API.

В принципе, все. Можно экспортировать сцену и переходить к программированию.

Шаг 2. Программный код.


В одной из статей я уже рассказывал об особенностях создания приложений Blend4Web. Поэтому повторяться не стану, а перейду сразу к теме урока.

Как вы знаете — функции Blend4Web API сгруппированы по отдельным модулям и эти модули необходимо объявлять перед использованием:

//регистрируем текущий скрипт, ответственный за AI рыб
b4w.register("game_fish", function(exports, require) {

//объявляем модули
var m_scenes     = b4w.require("scenes");
var m_cfg  = b4w.require("config");
var m_data = b4w.require("data");
var m_trans = b4w.require ("transform");
var m_phy = b4w.require ("physics");
var m_obj = b4w.require ("objects");
…
var APP_ASSETS_PATH = m_cfg.get_std_assets_path() + "waterhunter/";

exports.new_fishes = function() {
//что-то делаем
}
}


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

//объявляем модуль AI рыб
var m_fish     = b4w.require("game_fish");
…
//вызываем функцию
m_fish.new_fishes();

Первое, что должен сделать модуль — это загрузить файл с моделью.

exports.new_fishes = function() {
//что-то делаем
m_data.load(APP_ASSETS_PATH +”fish.json”, fish_loaded_cb,null,true,true);
}


За загрузку файлов JSON отвечает функция m_data.load. Работа с ней имеет несколько тонкостей, но сначала нужно разъяснить, что такое иерархия сцен с точки зрения движка.

Все сцены в приложении имеют определенный порядковый номер, который присваивается при загрузке файлов. Самый нижний, базовый уровень — это первая загруженная сцена и её номер 0. Все остальные получат очередные цифры 1,2,3...n.

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

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

Теперь вернемся к загрузке:

m_data.load(APP_ASSETS_PATH +”fish.json”, fish_loaded_cb,null,true,true);


Первое значение, которое передается в функцию — название файла (”fish.json”). Причем желательно использовать константу для указания директории. Дело в том, что после финальной сборки проекта, разбросанные файлы по папкам SDK группируются в одном месте. Действие происходит автоматически и, чтобы не было проблем с потерянными файлами, нужна эта константа. Предварительно она объявляется в области переменных:

var APP_ASSETS_PATH = m_cfg.get_std_assets_path() + "waterhunter/";


Второе значение (fish_loaded_cb) — это название функции, которая будет вызвана после завершения загрузки. Есть несколько нюансов о которых нужно знать…

Фактическая загрузка файла выполняется асинхронно и первоначальная функция load возвращает управление основному потоку еще ДО завершения загрузки. К сожалению, в официальной документации SDK этот момент не указан, что привело к баснословной потери моего времени при попытке выяснить, почему не работает код.

Третье значение (указано null) — название функции, передающей данные о процессе загрузки. Полезно для создания прелоадеров. Сейчас мне не нужна. Прочесть о создании прелоадеров можно в этой статье.

Четвертое значение (установлено true) в SDK движка указано, как wait_complete_loading. Это, собственно и сбило меня ранее с толку. В действительности, этот параметр отвечает только за вызов fish_loaded_cb. Если установлено true, то callback выполнится после загрузки всех данных файла. False — досрочный вызов функции, после загрузки ключевых объектов (например, звуки такими не являются).

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

Итак, объект загрузился в сцену. Теперь всё действие переходит к функции:

function fish_loaded_cb (data_id) {
    //data_id - присвоенный номер загруженной сцене
//что-то делаем
}


Здесь предполагается уже некоторый код, связанный с логикой объекта — это тема следующей статьи. Но кое-что я все же добавлю.

Основное управление движения объектом сконцентрировано в двух модулях движка: transform, physics.

Transform содержит базовые функции для перемещения, вращения, масштабирования объектов. Причем это происходит мгновенно, за смену кадра. Таким образом, движение возможно только при использовании постоянно генерируемого события. Замечу, что API Blend4Web предлагает свою реализацию событий.

Physics — это модуль, предлагающий большое количество функций, связанных с физикой. Обратите внимание, что некоторые из них, требуют активации настроек Charaster в Blender (см. выше). Это, в первую очередь, касается управления движением персонажа. Так как действие основано на физике, то необходимости в использовании событий нет.

Рассмотрим несложный пример движения персонажа вперед:

function fish_loaded_cb (data_id) {
    // ищем объект с указанным именем и номером сцены data_id
var obj_fish = m_scenes.get_object_by_name ("fish", data_id);

//двигаем персонаж вперед
m_phy.set_character_move_dir(obj_fish,1, 0);
}

© Habrahabr.ru