25 октября 2019 25.10.19 3 4300

Своя 3D игра на Javascript: избавляемся от паразитных таймаутов

+13

Не так давно я опубликовал на Хабре две статьи (одна, вторая) о ходе разработки моей игры Percy Lancaster 3D. В них я достаточно подробно описал трудности, с которыми столкнулся в процессе. Главными из них были запекание теней и скелетная анимация. Эти проблемы я успешно решил.

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

И вот я решил запустить игру на своем планшете 2014-го года на Windows 8.1. И результат меня, мягко говоря, не порадовал. Во-первых, удручала низкая скорость работы. А, во-вторых, в игре отсутствовало какое-либо управление для тач-скрина, и можно было только подключить клавиатуру, либо геймпад — благо, usb разъем в планшете присутствует.

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

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

В Three.js может быть только две карты (map): текстура и light map. То есть, нельзя на текстуру наложить отдельно light map и shadow map. Одни должны быть совмещены. Совместить — не проблема, проблема — в том, что в отсутствии освещения общая яркость в итоге становится низкой. Можно просто увеличить внутреннее свечение всех стен и наложить на них совмещенный light map + shadow map. Тогда увеличенная яркость будет имитировать освещение, а тени, как обычно, будут создаваться заполненными участками накладываемого мэпа. Но тогда требуется запекание абсолютно по всем текстурам. Потому что на те текстуры, что не освещены, должны будут накладываться мэпы с полупрозрачностью, иначе объекты будут слишком яркими. Однако, как от этого будет меняться их цвет и как его корректировать, пока непонятно. Я пока над этим думаю. И экспериментирую.

Еще одним решением проблемы производительности стало избавление от setInterval. Это такая функция в Javascript, которая позволяет задать периодически выполняемые события:

setInterval( function() { … }, 100)

Горение факелов и анимация воды у меня были поставлены на выполнение каждые 100 миллисекунд. Опрос геймпада — туда же. На большом компьютере проблем не было, но на планшете появились мелкие очень неприятные подергивания. Я предположил, что браузер выполняет setInterval-ы в первое свободное время между другими функциями, но не в те моменты, в которые это было бы лучше для плавности движения. Вся анимация становится рваной.

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

Таким образом я вообще избавился от setInterval. И картинка стала двигаться намного плавнее. Более того, несмотря на что, что на ПК в Chrome до этого все работало нормально, в Firefox все равно наблюдались фризы. Но я не парился, потому что пока игра рассчитана только на сборку под Windows на Webkit (Chrome). Но как только я убрал интервалы, то даже в Firefox все заработало идеально. Совет. Никогда не используйте setInterval в Javascript, если у вас параллельно работает рендер 60 кадров в секунду…

Решение проблемы управления на планшете.

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

В Javascript есть события touchstart и touchend. Их я и использовал. Пока есть какие-то баги, но вот так это работает:

 

Поиграть можно на Стиме или скачать с Itch.
Домашняя страничка игры.


Лучшие комментарии

Игра сначала выйдет в Стиме в виде exe приложения под Windows. А уже позже я ее запущу в онлайн виде. Там еще требуется сделать регистрацию и протестировать игру под Android, iOs, MacOS, Linux под все браузеры…
А ты потом не станешь переносить игру в exe приложение?
Читай также