Декабрь 2015

Том 30, номер 13

Разработка игр - Babylon.js: создание базовой игры для Web

Раанан Вебер

Продукты и технологии:

Visual Studio 2015 Community Edition, Babylon.js

В статье рассматриваются:

  • программирование простой игры для Web с помощью Babylon.js и WebGL;
  • создание мешей и добавление текстур к ним;
  • добавление камер и источников света;
  • поддержка простого взаимодействия пользователя с игрой.

Babylon.js — это простой в использовании движок трехмерной (3D) графики на базе WebGL, в основном предназначенный для разработки игр. В нем есть средства для создания, отображения и наложения текстур на меши в пространстве, а также для добавления источников света и камер. Поскольку он ориентирован главным образом на игры, Babylon.js имеет некоторые дополнительные функции, не требующиеся в обычном движке 3D-графики. Он поддерживает обнаружение коллизий, гравитацию в рамках сцены, ориентированные на игры камеры (например, фиксированную камеру [follow-camera], которая отслеживает движущийся объект), а также изначальную поддержку Oculus Rift и других устройств виртуальной реальности (virtual reality, VR). Он имеет систему плагинов для движка физики, поддержку аудио, диспетчер действий на основе пользовательского ввода и многое другое. Я исследую все эти средства в данном обучающем курсе.

Об этом обучающем курсе

В этом пособии я разработаю простую игру в боулинг. Я создам дорожку для боулинга, добавлю 10 кеглей и шар для боулинга и предоставлю возможность бросать шар. Безусловно, в таком виде моя игра ни в коей мере не готова к выпуску, но она покажет, как можно разрабатывать игру, используя средства Babylon.js. Я буду умышленно избегать применения любых внешних инструментов во время разработки. Объекты, камеры, текстуры и все остальное будут создаваться исключительно в JavaScript-коде.

Используемая мной версия Babylon.js в этом обучающем курсе — 2.1 (самый новый стабильный выпуск). Дэвид Катуэ (David Catuhe) и группа разработки Babylon пытаются, насколько это возможно, сохранять новые версии инфраструктуры обратно совместимыми, поэтому я могу лишь предполагать, что этот обучающий курс будет корректен при выходе будущих выпусков, по крайней мере до выхода следующей основной версии. Кроме того, я буду использовать Visual Studio 2015 Community Edition, но вы можете работать с любой IDE, которая вам нравится.

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

Приступаем к работе: новые проект Babylon.js

Простой проект Babylon.js является статическим веб-сайтом. Поскольку я использую Visual Studio, я задействую локальный IIS-сервер, встроенный в Visual Studio, для хостинга этих статических файлов. В Visual Studio нет шаблона для статического веб-сайта, поэтому нужен другой подход.

Сначала создайте новое пустое решение. Откройте Visual Studio и перейдите в File | New | Project. Выберите Other Project Types в секции слева, а затем Blank Solution, как показано на рис. 1. Присвойте решению какое-то имя (я назвал его BabylonBowling) и щелкните OK.

Создание нового пустого решения в Visual Studio
Рис. 1. Создание нового пустого решения в Visual Studio

Чтобы создать новый статический веб-сайт, сначала надо создать новый каталог для его хостинга. Щелкните пустое решение правой кнопкой мыши и выберите Open Folder в File Explorer, как показано на рис. 2.

Открытие папки решения в File Explorer
Рис. 2. Открытие папки решения в File Explorer

Создайте новый каталог для проекта с именем BabylonBowling и закройте File Explorer.

Щелкните решение правой кнопкой мыши и выберите Add | Existing Website. Укажите только что созданную папку (убедитесь, что вы выбрали папку, которую только что создали, а не папку решения) и щелкните Open. Теперь у вас должен быть пустой веб-сайт в качестве единственного проекта в этом решении.

После создания проекта нужно добавить инфраструктуру и ее зависимости. Делается это несколькими способами. Самый простой — использовать NuGet Package Manager.

Щелкните правой кнопкой мыши проект и выберите Manage NuGet packages. Щелкните в поле поиска (или нажмите комбинацию клавиш Ctrl+E) и введите babylon. Вы увидите окно, подобное представленному на рис. 3. Выберите BabylonJS. Убедитесь, что в поле Version указано Latest stable 2.1.0, и щелкните Install. Щелкните OK в окне Preview, которое всплывает после этого (если оно всплывает), и Babylon.js будет установлен в ваш пустой проект вместе с демонстрационной сценой.

Добавление Babylon.js через NuGet Package Manager
Рис. 3. Добавление Babylon.js через NuGet Package Manager

Те, кто используют npm в качестве диспетчера пакетов, могут установить Babylon.js командой:

npm install babylonjs

После установки пакета в папке scripts должны появиться следующие файлы:

  • babylon.js — минимизированная (minified) версия Babylon.js;
  • babylon.max.js — отладочная версия Babylon.js;
  • Oimo.js — движок физики Oimo JS, который будет использоваться во второй части этой обучающего курса;
  • poly2tri.js — дополнительная библиотека разбиения на треугольники (triangulation) (github.com/r3mi/poly2tri.js);
  • hand-minified.js — полизаполнение (polyfill) свойства pointer-events; этот файл отсутствует при использовании npm, поэтому установите его командой:
npm install handjs

NuGet-установщик также создает файлы index.html и index.js.

Ошибка в NuGet-пакете добавляет ненужную строку в web.config. Пока эту ошибку не исправили, дважды щелкните этот файл и удалите строку, выделенную на рис. 4 (в моем решении это строка номер 9).

Удаление выделенной строки из web.config
Рис. 4. Удаление выделенной строки из web.config

Теперь можно протестировать проект. Откройте его в своем браузере по умолчанию комбинацией клавиш Ctrl+Shift+W или щелкнув кнопку Run на верхней навигационной панели (navbar). Если вы видите трехмерный космический корабль, как на рис. 5, ваш проект готов.

Космический корабль по умолчанию в Babylon
Рис. 5. Космический корабль по умолчанию в Babylon

Для тех, кто использует другие IDE: просто следуйте обучающему курсу «Creating Basic Scene» на странице Babylon.js Documentation, чтобы получить текущее состояние. Тем не менее, я рекомендую устанавливать зависимости с помощью npm, если вы не используете NuGet. Этот обучающий курс можно найти по ссылке bit.ly/1MXT6WP.

Чтобы начать с нуля, удалите функцию createScene в index.js.

Строительные блоки

Первые два элемента, которые мы обсудим, — это engine и scene.

Объект engine отвечает за взаимодействие с низкоуровневым WebGL API (WebGL 1 основан на OpenGL ES2 и имеет очень похожий синтаксис). Вместо того чтобы заставлять вас писать низкоуровневый WebGL-код, engine предоставляет более высокоуровневый и простой в понимании API. Он также прозрачен для разработчика. Кроме как для начальной подготовки проекта, я вообще не буду напрямую использовать engine. С помощью engine можно добраться до более низкоуровневой функциональности, но я не стану вдаваться в эту тему.

Для инициализации engine требует двух параметров. Первый — это холст, на котором будет выполняться рисование. Холст (canvas) является HTML-элементом, уже содержащимся в index.html. Второй параметр — состояние сглаживания (anti-aliasing) (включено или выключено).

Откройте пустой на данный момент index.js и добавьте следующие строки кода:

function init() {
  var engine = initEngine();
}
function initEngine() {
  // Получаем элемент canvas из index.html
  var canvas = document.getElementById("renderCanvas");
  // Инициализируем движок трехмерной графики BABYLON
  var engine = new BABYLON.Engine(canvas, true);
  return engine;
}

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

Чтобы понять, что представляет сцена, возьмем веб-сайт с разными страницами. Одна веб-страница содержит текст, изображения, слушатели событий и все остальные ресурсы, необходимые для рендеринга этой страницы. Загрузка другой страницы приводит к загрузке других ресурсов.

Как и в случае одной веб-страницы, сцена хранит необходимые ресурсы для отображения одной трехмерной «страницы».

Как и в случае одной веб-страницы, сцена хранит необходимые ресурсы для отображения одной трехмерной «страницы». Эта сцена может быть очень большой и наполненной ресурсами — мешами, камерами, источниками света, пользовательскими действиями и др. Но для перехода с одной страницы на другую рекомендуется новая сцена. Сцена также отвечает за рендеринг своих ресурсов и передачу необходимой информации движку. Игра в боулинг требует лишь одной сцены. Но, если бы я планировал добавить новые уровни или бонусную игру, я создал бы из, используя новые сцены.

Инициализация scene требует только созданного мной engine. Добавьте в index.js следующее:

function createScene(engine) {
  var scene = new BABYLON.Scene(engine);
  // Регистрируем цикл рендеринга
  // для повторного рендеринга сцены
  engine.runRenderLoop(function () {
      scene.render();
  });
}

Сначала я создаю объект scene. Следующие строки — это взаимодействие с объектом engine. Они сообщают engine выполнять рендеринг этой конкретной сцены при каждом выполнении цикла рендеринга.

Добавьте следующую строку кода в конец функции init, чтобы реально создать scene:

var scene = createScene(engine);

Еще две вещи, прежде чем я продолжу. Когда вы изменяете размер окна браузера, изменяется и размер холста. Чтобы сохранить перспективу сцены, engine тоже должен изменить внутренние ширину и высоту.

Добавление следующих строк в функцию initEngine непосредственно перед return engine сохранит сцену в перспективе:

// Следим за событиями изменения размера для браузера/холста
window.addEventListener("resize", function () {
  engine.resize();
});

И второе: изменяем index.html на использование новой функции init. Откройте index.html и найдите тег script, содержащий вызов createScene. Замените createScene на init, сохраните index.html и закройте его.

Объект scene имеет чрезвычайно продвинутый уровень отладки, который позволяет вам отлаживать сцену, для которой выполняется рендеринг. Он показывает количество визуализируемых мешей и текущее число кадров в секунду (frames per second, FPS). Этот уровень дает возможность отключать такие средства, как наложение текстур и тени, и тем самым облегчает нахождение потерянных мешей. Включить уровень отладки очень просто:

scene.debugLayer.show();

Сделайте это и вы сможете сами отлаживать свою сцену.

Теперь у меня есть engine и scene, и я готов приступить к добавлению камеры, освещения и мешей, чтобы создать сцену Bowling Alley.

Камеры

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

  • Свободная камера (free camera) Камера для шутера от первого лица. Ее можно свободно перемещать по сцене, используя клавиши-стрелки на клавиатуре, а направление можно задавать с помощью мыши. Также можно включить необязательные гравитацию и обнаружение коллизий.
  • Камера дугового поворота (arc rotate camera) Используется для поворота вокруг конкретной цели. С помощью событий от мыши, клавиатуры или сенсорных событий, пользователь может рассматривать объект со всех направлений.
  • Сенсорная камера (touch camera) Свободная камера, использующая в качестве ввода сенсорные события. Подходит для любых мобильных платформ.
  • Фиксированная камера (follow camera) Автоматически следует за конкретной целью (т. е. фиксируется на ней).

Babylon.js обеспечивает «родную» поддержку WebVR и камеры с аппаратной ориентацией, а значит, вы можете легко манипулировать такими устройствами, как Oculus Rift или Google Cardboard.

Новая концепция, введенная в версии 2.1, делает каждый тип камер готовым к 3D. То есть каждый тип камер может быть настроен на соответствие стереоскопическому изображению в стиле Oculus Rift и красно-синим очкам (объемному стереоскопическому изображению [anaglyph 3D]). На рис. 6 показана сцена с космическим кораблем, визуализированная с помощью объемной стереоскопической свободной камеры (anaglyph-free camera), а на рис. 7 — та же сцена, визуализированная стереоскопической камерой (stereoscopic camera).

Объемная стереоскопическая камера
Рис. 6. Объемная стереоскопическая камера

Стереоскопическая камера
Рис. 7. Стереоскопическая камера

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

Сначала добавлю свободную камеру. Функция createFreeCamera принимает scene как единственную переменную:

function createFreeCamera(scene) {
  var camera = new BABYLON.FreeCamera(
    "player camera", BABYLON.Vector3.Zero(), scene);
  return camera;
}

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

И конечно, не забудьте добавить в конец функции init следующий код:

// Создаем основную камеру игрока
var camera = createFreeCamera(scene);
// Подключаем управление через пользовательский ввод с холста
camera.attachControl(engine.getRenderingCanvas());
// Указываем эту камеру основной активной
scene.activeCamera = camera;

Функция attachControl регистрирует «родные» JavaScript-слушатели событий, необходимые для конкретной камеры (например, для приема ввода от мыши, с клавиатуры и сенсорного ввода). Назначение активной камеры в сцене сообщает последней, что эта камера должна использоваться для визуализации.

Вторую камеру я добавлю во второй части после реализации броска шара.

Создание дорожки

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

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

Babylon.js обеспечивает «родную» поддержку WebVR и камеры с аппаратной ориентацией, а значит, вы можете легко манипулировать такими устройствами, как Oculus Rift или Google Cardboard.

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

Двухмерный план сцены с дорожкой для боулинга
Рис. 8. Двухмерный план сцены с дорожкой для боулинга

Front view Вертикальная проекция
Floor Пол
Gutter Желоб
Lane Дорожка
Top view Горизонтальная проекция

Я создаю глобальный пол для сцены:

function createFloor(scene) {
  var floor = BABYLON.Mesh.CreateGround("floor", 100, 100, 1, scene, false);
  return floor;
}

Этот код создает простой пол размером 100×100 метров. Все меши, созданные с помощью внутренних функций Babylon.js, размещаются в позиции 0,0,0 сцены и центрируются. Три важные переменные преобразуют меши и правильно позиционируют их в сцене:

  • mesh.position — вектор позиции меша в пространстве;
  • mesh.scaling — масштабный множитель меша по каждой из осей;
  • mesh.rotation — углы Эйлера (в радианах) для поворота по каждой оси; если вам удобнее работать с кватернионами (как и мне), вы можете заменить mesh.rotation на mesh.rotationQuaternion.

После добавления этой функции в мою функцию init и запуска сцены я заметил интересную вещь: я никак не могу увидеть добавленный мной пол! Причина в том, что камера и пол были созданы в одной и той же точке пространства (0,0,0). Поскольку пол совершенно плоский, он появится на экране только при виде сверху или снизу. Вернувшись к инициализации камеры в функции createCamera, я изменил вторую переменную, которая является начальной позицией камеры, на новый вектор — теперь камера будет на 1.7 единиц (в данном случае — метров) выше пола:

var camera = new BABYLON.FreeCamera(
  "cam", new BABYLON.Vector3(0,1.7,0), scene);

Если вы сейчас запустите сцену, то увидите пол, вытянутый на половину экрана, как показано на рис. 9.

Добавление пола в сцену
Рис. 9. Добавление пола в сцену

Попробуйте перемещаться с помощью клавиш-стрелок и мыши, чтобы прочувствовать управление свободной камерой.

Вы заметите, что пол совершенно черный. Я забыл об освещении! Добавим новую функцию, createLight, которую я потом расширю:

function createLight(scene) {
  var light = new BABYLON.DirectionalLight(
    "dir01", new BABYLON.Vector3(-0.5, -1, -0.5), scene);
  // Задаем позицию, чтобы впоследствии включить тени
  light.position = new BABYLON.Vector3(20, 40, 20);
  return light;
}

Не забудьте вставить в init эту строку:

var light = createLight(scene);

В Babylon.js четыре типа освещения.

  • Полусферическое Рассеянный свет (ambient light), в котором предопределены фоновый цвет (ground color) (пиксели внизу), цвет неба (пиксели вверху) и отражаемый цвет (specular color).
  • Точечное (point) Свет, излучаемый из одной точки во всех направлениях подобно солнцу.
  • Узконаправленное (spot) Как и предполагает название, это свет из одной точки с конкретным направлением и радиусом излучения. Этот свет может создавать тени.
  • Направленное (directional) Свет, излучаемый в конкретном направлении откуда угодно. Солнце может быть точечным источником света, но направленное освещение лучше имитирует солнечный свет. Этот свет тоже может создавать тени, и именно его я использовал в своем примере.
Экземпляр является точной копией меша, кроме его преобразования в пространстве.

Теперь пол будет белым. Babylon.js назначает белый материал по умолчанию каждому мешу, которому не назначен какой-либо материал.

Сама дорожка будет прямоугольником, лежащим на только что созданном полу:

function createLane(scene) {
  var lane = BABYLON.Mesh.CreateBox("lane", 1, scene, false);
  lane.scaling = new BABYLON.Vector3(
    laneWidth, laneHeight, totalLaneLength);
  // Новая позиция из-за центрирования меша
  lane.position.y = laneHeight / 2;
  lane.position.z = totalLaneLength / 2;
  return lane;
}

И вновь не забудьте добавить эту функцию в init.

Как видите, здесь использован масштабный множитель: я создал прямоугольник с размерами 1×1×1 и изменил его масштаб в соответствии с предопределенными ранее константами.

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

Кегли и шар для боулинга

Теперь не хватает кеглей и шара. Чтобы создать кегли, я использую цилиндр, всего один, который будет многократно копироваться (10 раз, если быть точным). В Babylon.js такие копии (multiplications) называются экземплярами. Экземпляр является точной копией меша, кроме его преобразования в пространстве. Это означает, что геометрию и текстуру экземпляра нельзя изменять, но позицию, масштаб и угол поворота — можно. Лично я никогда не использую исходный объект в сцене; если мне нужно 10 кеглей, я создаю один, отключаю его и создаю 10 экземпляров этого кегля в 10 предопределенных позициях:

function createPins(scene) {
  // Создание главного кегля
  var mainPin = BABYLON.Mesh.CreateCylinder("pin",
    pinHeight, pinDiameter / 2, pinDiameter, 6, 1, scene);
  // Отключаем его, поэтому он больше не будет показываться
  mainPin.setEnabled(false);
  return pinPositions.map(function (positionInSpace, idx) {
    var pin = new BABYLON.InstancedMesh("pin-" + idx, mainPin);
    pin.position = positionInSpace;
    return pin;
  });
}

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

Следующий на очереди — шар для боулинга. Это простая сфера с тремя отверстиями для пальцев. Чтобы создать сферу, я вызову функцию CreateSphere из Babylon.js:

var sphere = BABYLON.Mesh.CreateSphere("sphere", 12, 0.22, scene);

Теперь нужно просверлить отверстия. Для этого я воспользуюсь функциональностью, называемой конструктивной блочной геометрией (constructive solid geometry, CSG), интегрированной в Babylon.js, которая позволяет мне добавлять и вычитать меши из существующих мешей или, что еще лучше, добавлять или вычитать геометрические элементы (geometries) один из другого. Суть этого в следующем: если два меша пересекаются, можно «удалить» один из другого и получить измененный меш. В моем случае нужно создать три круглых отверстия в сфере. Здесь отлично подойдет цилиндр.

Сначала я создаю цилиндр, который будет использован для первого отверстия:

var cylinder = BABYLON.Mesh.CreateCylinder(
  "cylinder", 0.15, 0.02, 0.02, 8, 1, scene, false);

Затем я изменяю позицию цилиндра так, чтобы он пересекался со сферой:

cylinder.position.y += 0.15;

Далее я создаю CSG-объекты и использую их для вычитания цилиндра из сферы:

var sphereCSG = BABYLON.CSG.FromMesh(sphere);
var cylinderCSG = BABYLON.CSG.FromMesh(cylinder);
sphereCSG.subtractInPlace(cylinderCSG);
var ball = sphereCSG.toMesh("test", sphere.material,
  scene, false);

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

Создание шара для боулинга
Рис. 10. Создание шара для боулинга

Декали являются способом «рисования» поверх меша с уже наложенной текстурой.

Рис. 11 и игровая площадка на babylonjs-playground.com/#BIG0J показывают весь код, используемый для создания шара с нуля.

Рис. 11. Создание шара для боулинга, используя CSG

// Исходная сфера, из которой будет сделан шар
var sphere = BABYLON.Mesh.CreateSphere("sphere",
  10.0, 10.0, scene);
sphere.material = new BABYLON.StandardMaterial(
  "sphereMat", scene);

// Для корректного поворота цилиндров
var box1 = BABYLON.Mesh.CreateBox("parentBox", 1, scene);
var box2 = box1.clone("parentBox");
var box3 = box1.clone("parentBox");

// Задаем угол поворота для каждого
// родительского прямоугольника (parent box)
box2.rotate(BABYLON.Axis.X, 0.3);
box3.rotate(BABYLON.Axis.Z, 0.3);
box1.rotate(new BABYLON.Vector3(0.5, 0, 0.5), -0.2);

[box1, box2, box3].forEach(function (boxMesh) {
  // Вычисляем мировую матрицу так, чтобы CSG
  // обеспечила корректный поворот
  boxMesh.computeWorldMatrix(true);
  // Make the boxes invisible
  boxMesh.isVisible = false;
});

// Создаем три цилиндра
var cylinder1 = BABYLON.Mesh.CreateCylinder(
  "cylinder", 4, 1, 1, 30, 1, scene, false);
cylinder1.position.y += 4;
cylinder1.parent = box1;
var cylinder2 = cylinder1.clone("cylinder", box2);
var cylinder3 = cylinder1.clone("cylinder", box3);

// Создаем CSG-объект сферы
var sphereCSG = BABYLON.CSG.FromMesh(sphere);

// Вычитаем все цилиндры из CSG-объекта сферы
[cylinder1, cylinder2, cylinder3].forEach(
  function (cylinderMesh) {
  sphereCSG.subtractInPlace(BABYLON.CSG.FromMesh(
    cylinderMesh));
});

// Создаем новый меш из CSG-сферы
var ball = sphereCSG.toMesh(
  "bowling ball", sphere.material, scene, false);

Текстуры

Все созданные мной меши имеют белый материал по умолчанию. Чтобы сцена выглядела привлекательнее, я должен добавить другие материалы. Стандартный в Babylon.js материал (шейдер по умолчанию) имеет уйму определений, с которыми стоит поэкспериментировать, но я не стану обсуждать это здесь. (Чтобы узнать больше о шейдере по умолчанию в Babylon.js, опробуйте BabylonJS Material Editor на materialeditor.raananweber.com.)  Однако мы рассмотрим, как я накладывал текстуры на дорожку и шар для боулинга.

Для наложения текстуры на шар для боулинга я задействую другую чудесную функциональность Babylon.js — процедурные текстуры (procedural textures). Они не являются стандартными, использующими двухмерные изображения. Это программно создаваемые текстуры, которые генерируются графическим (а не обычным) процессором (GPU) что весьма положительно сказывается на скорости работы сцены. В Babylon много типов процедурных текстур — дерево, кирпичи, огонь, облака, трава и др. Я воспользуюсь текстурой мрамора.

Добавление следующих строк после создания меша шара сделает его шаром из зеленого мрамора, как на рис. 12:

var marbleMaterial = new BABYLON.StandardMaterial(
  "ball", scene);
var marbleTexture = new BABYLON.MarbleProceduralTexture(
  "marble", 512, scene);
marbleTexture.numberOfTilesHeight = 2;
marbleTexture.numberOfTilesWidth = 2;
marbleMaterial.ambientTexture = marbleTexture;
// Присваиваем размытому цвету (diffuse color)
// нужный цвет шара (зеленый)
marbleMaterial.diffuseColor = BABYLON.Color3.Green();
ball.material = marbleMaterial;

Текстура мрамора, примененная к шару

Рис. 12. Текстура мрамора, примененная к шару

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

Кроме того, можно применить декали, новую функциональность в Babylon.js 2.1. Декали (decals) являются способом «рисования» поверх меша с уже наложенной текстурой. Их можно использовать, например, для имитации следов выстрелов на стене или, как в моем случае, для добавления дополнительного декора на дорожку для боулинга. Декали — это меши и поэтому текстурируются с помощью стандартных материалов. На рис. 13 показано, как я добавляю контрольную разметку, а на рис. 14 — как выглядит дорожка после добавления декалей, а также как выглядят пол и желобки после использования процедурных текстур (кирпичей и травы) в качестве материалов.

Рис. 13. Добавление декали контрольной разметки

// Задаем позицию декали
var foulLinePosition = new BABYLON.Vector3(
  0, laneHeight, foulLaneDistance);
var decalSize = new BABYLON.Vector3(1,1,1);

// Создаем декаль (имя, меш, к которому она добавляется,
// позиция, вектор ориентации и размер)
var foulLine = BABYLON.Mesh.CreateDecal("foulLine", lane,
  foulLinePosition, BABYLON.Vector3.Up(), decalSize);

// Задаем группу рендеринга, чтобы рендеринг декали
// происходил после рендеринга дорожки
foulLine.renderingGroupId = 1;

// Задаем материал
var foulMaterial = new BABYLON.StandardMaterial(
  "foulMat", scene);
foulMaterial.diffuseTexture =
  new BABYLON.Texture("Assets/dots2-w-line.png", scene);
foulMaterial.diffuseTexture.hasAlpha = true;
foulLine.material = foulMaterial;

// Если диспетчер действий не инициализирован, создаем новый
scene.actionManager = new BABYLON.ActionManager(scene);

// Регистрируем действие для генерации нового цвета
// всякий раз, когда я нажимаю клавишу "C"
scene.actionManager.registerAction(
  new BABYLON.ExecuteCodeAction({
  trigger: BABYLON.ActionManager.OnKeyUpTrigger, parameter:
  "c" },
  // Функция, выполняемая при каждом нажатии клавиши "C"
  function () {
    ball.material.diffuseColor = new BABYLON.Color3(
      Math.random(), Math.random(), Math.random());
  }
));

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

Рис. 14. Дорожка с добавленными декалями

Пользовательский ввод — диспетчер действий в Babylon.js

Как полнофункциональный игровой движок Babylon.js предлагает простой способ для взаимодействия с пользовательским вводом. Скажем, я хочу изменять цвет шара, нажимая клавишу «C». Всякий раз, когда я нажимаю ее, шару присваивается случайный цвет:

// Если диспетчер действий не инициализирован, создаем новый
scene.actionManager = new BABYLON.ActionManager(scene);
// Регистрируем действие для генерации нового цвета
// всякий раз, когда я нажимаю клавишу "C"
scene.actionManager.registerAction(
  new BABYLON.ExecuteCodeAction({ trigger:
  BABYLON.ActionManager.OnKeyUpTrigger, parameter: "c" },
  // Функция, выполняемая при каждом нажатии клавиши "C"
  function () {
    ball.material.diffuseColor = new BABYLON.Color3(
      Math.random(), Math.random(), Math.random());
  }
));

Action Manager (диспетчер действий) в Babylon.js — мощное средство для управления действиями в соответствии с триггерами. Триггером может быть перемещение или щелчки мышью, пересечения мешей и клавиатурный ввод. Существует много триггеров и действий, из которых есть, что выбрать. Загляните на сайт Babylon.js Tutorial (bit.ly/1MEkNRo), чтобы увидеть их все.

Я намерен использовать Action Manager для управления шаром и сброса сцены. Эти действия я добавлю во второй части обучающего курса.

Применение внешних ресурсов

Я создавал необходимые мне меши, используя внутренние функции Babylon.js. По большей части этого будет недостаточно. Babylon.js предлагает множество мешей — от сфер и прямоугольников до сложных лент, но создавать сложные модели вроде людей, оружия для вашей игры Doom-for-the-Web или космический корабль для вашего клона Space Invaders очень трудно.

Action Manager в Babylon.js — мощное средство для управления действиями в соответствии с триггерами.

Babylon.js предоставляет плагины-экспортеры для многих известных инструментов работы с трехмерной графикой, таких как Blender и 3D-Studio. Вы также можете экспортировать свои модели из чудесной Clara.io и скачать файл .babylon.

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

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

Что дальше?

Я пропустил много средств, интегрированных в Babylon.js. В нем огромное количество средств, и я настоятельно рекомендую проверить игровую площадку (babylonjs-playground.com), страницу Documentation (doc.babylonjs.com) и основную страницу Babylon.js (babylonjs.com), чтобы получить представление о бесконечных возможностях этой инфраструктуры. Если у вас возникли затруднения с любой частью этого обучающего курса, свяжитесь со мной или любым пользователем проекта Babylon.js на самом активном форуме Babylon.js HTML5 Game Devs (bit.ly/1GmUyzq).

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


Раанан Вебер (Raanan Weber) — IT-консультант, специалист по комплексной разработке, муж и отец. В свободное время старается внести свой вклад в совершенствование Babylon.js и других проектов с открытым исходным кодом. Читайте его блог blog.raananweber.com.

Выражаю благодарность за рецензирование статьи эксперту Microsoft Дэвиду Катуэ (David Catuhe).