Что за бред, подумаете вы. Что за бессмысленный набор слов? Но, увы или ура — кому как, сегодня это уже не бред, а реальность. Речь в статье пойдет о написанной мной для Инстаграм игре, в которой в качестве контроллера используется ваше лицо, а точнее, не только оно, но иногда и вся голова. Перемещение по игровому полю производится посредством поворота и наклона головы, а выбор действия — открытием рта либо двойным «кликом» (морганием) глазами. Double blink, наверно, стоило бы его назвать…

О дивный новый мир! Вероятно, хипстеры будущего займутся разработкой скриптов для генерации красивого разноцветного дыма за своими легковыми космическими капсулами или написанием шейдеров для придания интересных визуальных эффектов черным вратам персонального телепорта, типа падающих снежинок (Web 100.0) или фрактального тумана (WebGL 100.0) или… Нет, пожалуй, у меня не хватит фантазии представить себе, какой ерундой можно было бы заняться в будущем: я пытаюсь мыслить слишком логично. Но лет 30 назад мы точно, ни в каком сне, ни при каких условиях не смогли бы даже подумать, что сегодня мы будем создавать маски для социальной сети под названием Инстаграм. И более того — что внутри этих масок появятся полноценные игры. Мне стало страшно и интересно. В общем, страшно интересно. И, как вы уже поняли, я не удержался и тоже написал такую игру. Разумеется, для того, чтобы поиграть в нее, у вас должен быть смартфон на Android или iOS с фронтальной камерой и установленным приложением Инстаграм.

Эта игра — «Крестики-нолики». Вообще, это одна из первых игр, которую я создал в своей жизни, а, может быть, даже самая первая — я сейчас уже точно не вспомню. Но я писал ее еще на Паскале, затем перетащил тот старый алгоритм в Delphi. После этого игра, конечно, появилась также и в вебе, на моем сайте: ну а куда же на своей персональной страничке без своих персональных крестиков-ноликов… И вот теперь пришла пора запихнуть эту эпичную игру еще и в Инстаграм. В общем, если бы я был в этой игре крестиком или ноликом (скорее, наверно, ноликом), то я бы мог много поведать о своей долгой, интересной жизни…

Что еще за маски такие?


Для начала — краткий исторический экскурс. Маски в Инстаграм появились благодаря белорусской команде, разработавшей приложение для селфи MSQRD. После того, как это приложение купил Facebook, команда уже внутри корпорации продолжила работу по интеграции своей технологии в продукты Facebook. Так появилась платформа, предлагающая функции дополненной реальности, Spark AR. В августе 2019-го платформа вышла из статуса беты и стала доступна для всех пользователей, которые теперь могут создавать контент дополненной реальности для Facebook и Instagram.

Маски были задуманы как фильтры для придания забавных эффектов своей физиономии с последующим размещением селфи в социальной сети. Что касается игр в масках, то я нашел их не так много. Есть Flying Face, аналог Flappy Bird, где по экрану летит птичка, и вам нужно не дать ей врезаться в препятствия, моргая в нужные моменты. Еще видел игру, где нужно ловить ртом падающую сверху вниз еду. Шутер, типа Invaders. И 3D раннер, где персонаж бежит по улице города и перепрыгивает через препятствия. Возможно, есть и еще какие-то. В целом, тема довольно новая.

Процесс создания контента происходит в среде разработки Spark AR Studio, которую можно бесплатно скачать для Windows. Также, стоит скачать и приложение Spark AR Player для тестирования создаваемых вами эффектов на мобильных устройствах Andrioid или iOS, для которых, эти эффекты, собственно говоря, и предназначены. Соединение с компьютером происходит по USB кабелю, по крайней мере, в моем случае с Android смартфоном. После загрузки маски через компонент Facebook /sparkarhub и прохождения модерации ваша маска (или игра) станет доступна для всех пользователей Инстаграм из Интернета. Маску можно будет получить по прямой ссылке или со страницы ее создателя в Инстаграм.

Разобраться в том, как работать в Spark AR Studio совсем не сложно. Я не буду подробно останавливаться на этом, тем более, что в Интернете полно уроков на данную тему. Отмечу только, что существуют два пути разработки более-менее интерактивного приложения, предполагающего нечто большее, чем просто наложение на лицо каких-то статических спрайтов: это создание так называемых патчей и написание скриптов. Первый способ визуально напоминает работу с материалами в 3D редакторе, а второй — это просто программирование на Javascript с использованием специализированного api. Я выбрал второй вариант. Впрочем, никто не запрещает совмещать оба способа: из скриптов есть возможность доступа ко всему, что творится в патчах.

Графика


Итак, для начала неплохо бы создать некоторые графические элементы, которые будут использоваться в игре. Открываем любимую программу 3D моделирования и творим. Я использую Blender. В принципе, можно создать просто отдельные спрайты, а затем задать им некую логику поведения средствами Spark AR и не использовать 3D редактор вообще. Но я решил сделать объемные фигурки крестиков и ноликов, поэтому без подобного редактора было уже не обойтись.

Первым делом нужно создать 2D текстуры. Объединим все текстуры в один атлас.


Текстурный атлас

Теперь о 3D моделях. В качестве игрового 3D пространства используется плоскость из двух треугольников, на которые натягивается разметка поля (вот тот большой сетчатый фрагмент из текстурного атласа). Сам атлас — с прозрачным альфа-каналом. В поле будет вставляться 9 квадратиков синего цвета, тоже состоящих из двух треугольников каждый. Также, помимо девяти синих, 3D модель будет содержать в тех же координатах по 9 зеленых и красных квадратиков, которые при старте игры будут скрыты. Смысл в том, что в нормальном состоянии цвет всех квадратов поля — синий, а зеленый и красный используются в качестве курсора для выбора позиции, в которую требуется установить соответственно крестик или нолик. Кроме того, зеленый и красный цвета используются для подсветки выигравшего ряда из трех клеток в конце каждого раунда. Достаточно было бы, конечно, использовать 9 синих и всего по 3 зеленых и красных квадратика, но тогда пришлось бы, помимо отображения / скрытия, задать им еще и некую логику перемещения. Мне показалось, что просто скрывать и отображать их без какого-либо оперирования координатами в пространстве будет легче, да и 3D модель это нагрузит несущественно.


Игровое поле

В игре позиции трех квадратиков в каждой из девяти групп, разумеется совпадают. Здесь я просто в одной из клеток рассредоточил их по вертикали, чтобы наглядно продемонстрировать, из чего состоит 3D модель в целом. Обратите внимание, что квадратикам заданы имена (на рисунке в списке справа) определенным образом: от b11 до b33, от g11 до g33 и от r11 до r33, где каждая цифра изменяется в диапазоне от 1 до 3. Иными словами, буква обозначает цвет, а цифры — строку и столбец. Это нужно для доступа к квадратикам (мешам, в терминологии 3D) по именам из скрипта, чтобы было удобно скрывать и отображать их.

Затем я смоделировал сами фигурки крестиков и ноликов, расположив их также парами в каждой клетке. (Для наглядности приподнял один из ноликов.) Не смотрите на то, что их текстура пока такая «веселенькая». Я это сделал для удобства наложения UV развертки. Затем в текстурном атласе я заменил текстуры фигур на более спокойные, которые вы можете наблюдать в самой игре.


Игровые фигуры

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

Экспортируем все 3D модели (все три, то есть, поле, фигуры и стрелки) в формат FBX, который требует Spark AR Studio.

Импорт в Spark AR Studio


Сначала запустим Spark AR Studio и импортируем туда 3D модели. Они появятся в списке Assets слева внизу. Заодно создадим для своего лица тематическую маску с боевой раскраской в виде «татуировок» крестика и нолика на лице. О том, как создать текстурированную маску, можно узнать из тысяч уроков в глобальной сети или из официальной документации по платформе, не буду заострять на этом внимание. Вкратце: скачиваем «болванку», открываем ее в любимом графическом редакторе, рисуем что-нибудь свое поверх, сохраняем, импортируем картинку в Spark AR Studio, настраиваем параметры материала и текстуры.


Перетащим из Assets наши модели в список Scene выше. Причем, нам нужно, чтобы они рендерились в определенном порядке: сначала поле (field.fbx), затем модели фигур (fieldxo.fbx) и, наконец, срелки (arrows.fbx). Кроме того, на самом нижнем слое будет еще faceTracker с faceMesh — это маска для вашего лица. Вероятно, можно закинуть модели на сцену в любом порядке, однако, я намереваюсь отключить для всех материалов (кроме фигурок) сортировку по глубине — Depth Test и Write to Depth Buffer (в свойствах материалов), поэтому пусть они рендерятся в заданном мной вручную порядке: маска — поле — фигурки — стрелки.

В материалах каждого из слоев зададим Blend Mode = Alpha, чтобы объекты стали прозрачными в тех частях, где это задано в текстуре. А текстурам зададим Filtering = Medium для сглаживания. (При выборе из списка слева материала или текстуры в правой части окна будут отображаться все доступные для них свойства.)

Наконец, добавим в Assets (кликнув внизу слева +Add Asset) элемент Script. И помимо 3D моделей, в списке появится script.js, по клику на который откроется ваш внешний текстовый редактор скриптов. При сохранении в редакторе происходит рестарт вашей программы в Spark AR Studio.

Программирование


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

const Scene = require('Scene');
const FaceTracking = require('FaceTracking');
const Time = require('Time');
const Audio = require('Audio');
const Animation = require('Animation');

Реализовать крестики-нолики на Javascript можно по-разному. В данной статье речь не о реализации данной игры на данном языке программирования, а об управлении при помощи лица. Опишу ключевые моменты, специфичные для взаимодействия с системой Spark AR, чтобы был понятен принцип.

Доступ к именованному фрагменту 3D модели (мешу), например, к синему квадратику в первой строке, втором столбце игрового поля:

Scene.root.find('b12');

(Вспомним, b12 — так мы именовали данный меш в 3D редакторе.)

Доступ к крестику в данной позиции:

Scene.root.find('x12');

Сокрытие нолика (o), постановка крестика (x) и окрашивание в зеленый цвет (g) ячейки поля '32' (третья строка второй столбец):

// figure
Scene.root.find('o32').hidden = true;
Scene.root.find('x32').hidden = false;
// background
Scene.root.find('b32').hidden = true;
Scene.root.find('g32').hidden = false;
Scene.root.find('r32').hidden = true;



Зеленый крестик с зеленой подсветкой в поле '32'

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

//vert: fov+ back-
FaceTracking.face(0).cameraTransform.rotationX.monitor().subscribe(function(event) {
	var v = event.newValue;
	...
});

//hor: rt+ dn-
FaceTracking.face(0).cameraTransform.rotationY.monitor().subscribe(function(event) {
	var v = event.newValue;
	...
});

Здесь все очень просто. Берем из события значения поворота или наклона головы (event.newValue) и проверяем, выходят ли они за определенный диапазон. Если да — то смещаем курсор (зеленый квадратик) визуально в нужном направлении. Отрицательное значение поворота головы соответствует ее повороту влево, положительное — вправо. Аналогично — для наклона головы вперед-назад для движения по строкам. Весь диапазон — это от -1 до +1. Таким образом можно реализовать перемещение курсора и…

И тут меня осенило


Я вдруг понял, что управлять курсором на поле тем способом, который я выбрал, неудобно: нужно поворачивать голову для перемещения на одну клетку, затем возвращать голову в исходное положение, затем снова поворачивать ее для следующего шага и так далее. А почему бы просто не использовать дробные значения поворота головы в стороны и наклона вперед-назад для того, чтобы сразу сфокусироваться на нужной клетке поля? То есть, как бы указывать курсору его позицию взглядом, а, точнее, направлением головы. А то первый способ представляет собой слишком усердную разминку для шеи. Физкультура — это, конечно, хорошо, но не до такой же степени. Естественно, будем запоминать текущую позицию где-нибудь в ap.cur={x:1,y:1}, чтобы стирать курсор в предыдущей позиции и сохранять координаты новой позиции, необходимой впоследствии для «клика». Кстати, визуальные стрелки для указания направления движения курсора теперь не нужны: одну 3D модель (arrows.fbx) выбрасываем из проекта. Также, можно стереть изображение стрелки и с текстуры.

«Дабл блинк»?


А теперь мы добрались до самого интересного. Двойной клик глазами. Признайтесь, вы хотели сделать это с того момента, как узнали о нем в начале статьи? Теперь вы будете рассказывать своим знакомым о том, что мышь уже устарела и что сегодня для клика используется двойное моргание! Double blink. Почему двойное? Все просто: одинарное человек делает непроизвольно периодически, поэтому навешивать какие-либо события на одиночный блинк было бы садизмом.


В Spark AR существует события leftEye.openness и rightEye.openness, подписавшись на которые, можно получить величину открытия каждого глаза в event.newValue в диапазоне от 0 до 1. Будем считать глаз закрытым при значении менее 0.1. И вызовем функцию обработки «блинка» ap.blink1() (ap — это неймспейс, мне просто так удобнее).

//leftEye
FaceTracking.face(0).leftEye.openness.monitor().subscribe(function(event) {
	if  (event.newValue < 0.1) {
		ap.blink1();
	};
});

//leftEye
FaceTracking.face(0).rightEye.openness.monitor().subscribe(function(event) {
	if  (event.newValue < 0.1) {
		ap.blink1();
	};
});

В самой функции обработки моргания определим минимальный интервал между морганиями 200 миллисекунд для того, чтобы эта функция не срабатывала сразу дважды при моргании обоими глазами, так как ее вызов повешен на каждый глаз. Думаю, понятно. Также, определим максимальное время двойного моргания — 1 сек. Если укладываемся в этот диапазон, то сработает функция action(), которая отобразит крестик в текущей позиции. Заодно повесим action() и на событие открытия рта mouth.openness — чтобы у игрока был выбор, ставить ли крестик по двойному морганию или по открытию рта.

blink1:function () {
	var tmNew = Date.now();
	if  (tmNew - ap.blinkStart > 200) {
		ap.blinkCount++;
		if  (ap.blinkCount > 1) {
			//action
			if (tmNew - ap.blinkStart < 1000) {
				ap.action();
			};
			ap.blinkCount = 0;
			ap.blinkStart = 0;
		} else {
			ap.blinkStart = tmNew;
		};
	};
},

action:function() {
	if  (ap.fProc == false) {
		ap.fProc = true;
		xo.putx();
	};
},

//mouth
FaceTracking.face(0).mouth.openness.monitor().subscribe(function(event) {
	if  (event.newValue > 0.2) {
		ap.action();
	};
});

Звуки


Их я нашел в разделах роялти-фри на различных сайтах звуковых эффектов. Важная деталь: Spark AR требует звуковые файлы определенного формата, а именно, M4A, моно, 44.1 КГц, 16 бит. Онлайн-конвертеры из wav и mp3 в m4a в ассортименте присутствуют в глобальной сети. Я добавил звук к четырем событиям: ходу, выигрышу крестиков, выигрышу ноликов и ничьей. Общий объем всех звуков после конвертации составил 105 Кб.

Для использования звуков в скрипте надо подключить в скрипте аудиомодуль, добавить на сцену нужное количество динамиков по количеству звуков (у меня получилось Speaker0, Speaker1, Speaker2, Speaker3), добавить в Assets модуль Playback Controllers, создать в нем соответственно количеству звуков нужное число подпунктов audioPlaybackController и привязать к каждому динамику свой звук.


После этого проиграть звук из скрипта можно будет следующим образом (на примере звука клика):

var audioClick = Audio.getPlaybackController('audioPlaybackControllerClick');
audioClick.reset();
audioClick.setPlaying(true);

Финальный билд


Для создания билдов под все поддерживаемые мобильные устройства (Android, iOs) надо просто нажать File -> Export. Объем игры под каждую мобильную платформу со всеми 3D моделями и текстурами в моем случае составил порядка 1 Мб. Затем нужно зайти через браузер на Spark AR Hub в Facebook /sparkarhub и загрузить туда экспортированный файл. Также требуется заполнить описание, ключевые слова для поиска и загрузить демонстрационное видео. Причем, оно должно быть снято на телефон через камеру Инстаграм. Для этого в хабе существует раздел «Предпросмотр камеры», содержащий ссылку. Эту ссылку можно открыть на мобильном устройстве. Запустится приложение Инстаграм и в нем откроется ваша маска (игра). Ссылку можно отправить для тестирования также всем своим друзьям, у которых, конечно же, должен быть смартфон с установленным приложением Инстаграм. До завершения процесса модерации маска не будет видна всем пользователям и они пока не смогут добавлять ее в список своих эффектов, она будет видна лишь по этой ссылке.

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

Через три дня игру одобрили, предварительно переместив ее в раздел «Camera Styles». Честно говоря, я не помню, в какой раздел я помещал ее изначально (а раздел, или, иначе говоря, категорию, нужно было выбрать обязательно). Это не очевидно, поскольку не существует раздела игр для Инстаграм. Но, ладно, теперь будем знать, куда нужно помещать новые игры. Если игры будут появляться и в дальнейшем, то, вероятно, Инстаграму стоит добавить новый раздел.

Итог


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

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