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

Итогом стал небольшой прототип браузерного 2d платформера с физикой.
Под катом — руководство для новичков от новичка по созданию такой игры. Если вы — опытный игродел, заходите делиться ценными советами!
Классический JavaScript — Для простоты я постарался воспользоваться самым базовым синтаксисом языка. Так же в проекте нет сборщика: каждый файл подключается как есть. Благодаря этому, надеюсь, проект будет понятен широкому кругу разработчиков.
PixiJS — Мне понравился этот движок 2d графики. Каких-либо замечаний по его работе не возникло. Плюс в наличии — хорошая документация.
PhysicsJS — Одной из причин проекта было желание попробовать в деле готовый физический движок. Выбор пал на PhysicsJS. В процессе разработки иногда не хватало документации, приходилось открывать его исходники. Но свою работу он выполнил, физика тел выглядит вполне реалистично.
JQuery — Возможности библиотеки используются минимально и ее можно спокойно убрать при желании. Но лично мне JQuery нравится, я с удовольствием его использую для работы с HTML.
Максимально 60 раз в секунду браузер вызывает метод перерисовки экрана.
При каждой перерисовке идет обновление физической модели и последовательное рисование слоев игры: карты, игровой машины, призовых звезд.
Если между перерисовками экрана пользователь нажимал на кнопки управления — то модель получает об этом информацию, которая будет учтена при следующей перерисовке.
Можно изобразить этот процесс в виде схемы:

1. Обновление модели.
2. Вызов PhysicsJS для расчета физики.
3. Последовательный вызов слоев на перерисовку.
4. Опрос обновленной модели и перерисовка с помощью PixiJS.
Коллизии — физический движок дает удобное API определения коллизий. Не нужно самому вспоминать математику.
Но иногда коллизии не нужны... — например, когда собираешь призовые звезды. Мне кажется логичным включить в физический движок тип объектов, которые фиксируют факт столкновения с ними, но при этом не взаимодействуют с другими объектами (объекты-призраки). К сожалению, я не нашел в PhysicsJS такой возможности. В итоге, даже если удалять призовую звезду после коллизии, то движок уже изменил скорость игрока, замедлив его.

Уверен, есть более красивое решение, но я сделал так: после факта коллизии возвращаем игроку его характеристики до столкновения, благо PhysicsJS позволяет так себя обманывать.
Результат — сбор звезд не нарушает скорости игрока.

Разные модели у движков — физический движок делает поворот объекта вокруг его центра масс, а графический движок по умолчанию разворачивает по левому верхнему углу объекта. Если этот факт не учитывать, то результат будет довольно забавным.

Кстати, что-то похожее я наблюдаю в анимации разворота автомобиля в Uber клиенте на Android: там точка поворота так же находится в левом верхнем углу, а не по центру автомобиля. Думаю, это баг, который им лень поправить.
Решением является рисование автомобиля относительно его центра, а не левого верхнего угла.
Теперь все выглядит как надо

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

Расход батареи телефона от работы игры заметно увеличивается. Думаю, виноват физический движок, который активно нагружает процессор работой.
В целом, процесс создания игры не показался мне очень сложным. Используя готовые движки для графики и физики, можно значительно упростить разработку, почти не думая о математике. Насколько это эффективный подход — вопрос для отдельной статьи. Спасибо всем за внимание и надеюсь, мои наработки помогут вам создать что-нибудь свое, если вы давно собирались это сделать! Удачи!
[Исходники на GitHub]

Итогом стал небольшой прототип браузерного 2d платформера с физикой.
Под катом — руководство для новичков от новичка по созданию такой игры. Если вы — опытный игродел, заходите делиться ценными советами!
Инструменты на проекте
Классический JavaScript — Для простоты я постарался воспользоваться самым базовым синтаксисом языка. Так же в проекте нет сборщика: каждый файл подключается как есть. Благодаря этому, надеюсь, проект будет понятен широкому кругу разработчиков.
PixiJS — Мне понравился этот движок 2d графики. Каких-либо замечаний по его работе не возникло. Плюс в наличии — хорошая документация.
PhysicsJS — Одной из причин проекта было желание попробовать в деле готовый физический движок. Выбор пал на PhysicsJS. В процессе разработки иногда не хватало документации, приходилось открывать его исходники. Но свою работу он выполнил, физика тел выглядит вполне реалистично.
JQuery — Возможности библиотеки используются минимально и ее можно спокойно убрать при желании. Но лично мне JQuery нравится, я с удовольствием его использую для работы с HTML.
Архитектура приложения
Максимально 60 раз в секунду браузер вызывает метод перерисовки экрана.
Код
//render.RootStage function animate() { requestAnimationFrame(animate); //... }
При каждой перерисовке идет обновление физической модели и последовательное рисование слоев игры: карты, игровой машины, призовых звезд.
Код
//render.RootStage function animate() { requestAnimationFrame(animate); //обновление модели game.step(); //перерисовка слоев for (var i=0; i< stages.length; i++) stages[i].update(); }
Если между перерисовками экрана пользователь нажимал на кнопки управления — то модель получает об этом информацию, которая будет учтена при следующей перерисовке.
Код
//render.RootStage $("#moveRight").mousedown(function(){ game.car().startAccelerator(); }); $("#moveRight").mouseup(function(){ game.car().stopAccelerator(); });
Можно изобразить этот процесс в виде схемы:

1. Обновление модели.
2. Вызов PhysicsJS для расчета физики.
3. Последовательный вызов слоев на перерисовку.
4. Опрос обновленной модели и перерисовка с помощью PixiJS.
Особенности реализации
Коллизии — физический движок дает удобное API определения коллизий. Не нужно самому вспоминать математику.
Код
//physics.Game var world = Physics({...}); world.add([ Physics.behavior('body-collision-detection'), ... ]); world.on('collisions:detected', function(data){ for (var i = 0; i < data.collisions.length; i++) onCollision(data.collisions[i]); });
Но иногда коллизии не нужны... — например, когда собираешь призовые звезды. Мне кажется логичным включить в физический движок тип объектов, которые фиксируют факт столкновения с ними, но при этом не взаимодействуют с другими объектами (объекты-призраки). К сожалению, я не нашел в PhysicsJS такой возможности. В итоге, даже если удалять призовую звезду после коллизии, то движок уже изменил скорость игрока, замедлив его.

Уверен, есть более красивое решение, но я сделал так: после факта коллизии возвращаем игроку его характеристики до столкновения, благо PhysicsJS позволяет так себя обманывать.
Код
//model.car.Car function onCollision(otherBody, pos, norm){ if(otherBody.objType == model.ObjectType.POINT) carBody.backPrevForce(); } //physics.BodyPhysicsImpl function backPrevForce(){ var old = body.state.old; body.state.acc.set(old.acc.x, old.acc.y); body.state.vel.set(old.vel.x, old.vel.y); body.state.angular.vel = old.angular.vel; body.state.angular.acc = old.angular.acc; }
Результат — сбор звезд не нарушает скорости игрока.

Разные модели у движков — физический движок делает поворот объекта вокруг его центра масс, а графический движок по умолчанию разворачивает по левому верхнему углу объекта. Если этот факт не учитывать, то результат будет довольно забавным.

Кстати, что-то похожее я наблюдаю в анимации разворота автомобиля в Uber клиенте на Android: там точка поворота так же находится в левом верхнем углу, а не по центру автомобиля. Думаю, это баг, который им лень поправить.
Пример Uber-подобной анимации
Решением является рисование автомобиля относительно его центра, а не левого верхнего угла.
Код
//render.car.PlayerCar function paintCabin(g, model){ //... g.drawRect(model.x - model.w/2, model.y - model.h/2, model.w, model.h); //... }
Теперь все выглядит как надо

Показ кнопок управления на мобильном устройстве — сделал реверанс в сторону прогресса: показываю большие кнопки управления для мобильных устройств. Главное, не забыть, что зажатие кнопки делается touch событиями, и что нужно запретить появление выделения текста от этого долгого нажатия через css стиль.
Код
//render.RootStage $("#moveRight").on('touchstart', function(){ game.car().startAccelerator(); }); $("#moveRight").on('touchend', function(){ game.car().stopAccelerator(); }); //main.css .moveBtn { -webkit-user-select: none; -moz-user-select: none; }

Расход батареи телефона от работы игры заметно увеличивается. Думаю, виноват физический движок, который активно нагружает процессор работой.
Выводы
В целом, процесс создания игры не показался мне очень сложным. Используя готовые движки для графики и физики, можно значительно упростить разработку, почти не думая о математике. Насколько это эффективный подход — вопрос для отдельной статьи. Спасибо всем за внимание и надеюсь, мои наработки помогут вам создать что-нибудь свое, если вы давно собирались это сделать! Удачи!
[Исходники на GitHub]

