Теплым летним вечером посетила мысль, которая, наверняка, посещает многих: хочу сделать свою игру! Энергии было через край, поэтому работа пошла с огоньком.
![Racing game](https://habrastorage.org/r/w1560/files/be3/a0e/324/be3a0e3247ae4481a522bfa866e56ee4.png)
Итогом стал небольшой прототип браузерного 2d платформера с физикой.
Под катом — руководство для новичков от новичка по созданию такой игры. Если вы — опытный игродел, заходите делиться ценными советами!
Классический JavaScript — Для простоты я постарался воспользоваться самым базовым синтаксисом языка. Так же в проекте нет сборщика: каждый файл подключается как есть. Благодаря этому, надеюсь, проект будет понятен широкому кругу разработчиков.
PixiJS — Мне понравился этот движок 2d графики. Каких-либо замечаний по его работе не возникло. Плюс в наличии — хорошая документация.
PhysicsJS — Одной из причин проекта было желание попробовать в деле готовый физический движок. Выбор пал на PhysicsJS. В процессе разработки иногда не хватало документации, приходилось открывать его исходники. Но свою работу он выполнил, физика тел выглядит вполне реалистично.
JQuery — Возможности библиотеки используются минимально и ее можно спокойно убрать при желании. Но лично мне JQuery нравится, я с удовольствием его использую для работы с HTML.
Максимально 60 раз в секунду браузер вызывает метод перерисовки экрана.
При каждой перерисовке идет обновление физической модели и последовательное рисование слоев игры: карты, игровой машины, призовых звезд.
Если между перерисовками экрана пользователь нажимал на кнопки управления — то модель получает об этом информацию, которая будет учтена при следующей перерисовке.
Можно изобразить этот процесс в виде схемы:
![Процесс игры](https://habrastorage.org/r/w1560/files/b72/f27/863/b72f27863bbc4bceb9368e721bc73fd7.png)
1. Обновление модели.
2. Вызов PhysicsJS для расчета физики.
3. Последовательный вызов слоев на перерисовку.
4. Опрос обновленной модели и перерисовка с помощью PixiJS.
Коллизии — физический движок дает удобное API определения коллизий. Не нужно самому вспоминать математику.
Но иногда коллизии не нужны... — например, когда собираешь призовые звезды. Мне кажется логичным включить в физический движок тип объектов, которые фиксируют факт столкновения с ними, но при этом не взаимодействуют с другими объектами (объекты-призраки). К сожалению, я не нашел в PhysicsJS такой возможности. В итоге, даже если удалять призовую звезду после коллизии, то движок уже изменил скорость игрока, замедлив его.
![](https://habrastorage.org/files/d37/cff/e66/d37cffe6652c4931b98f16c84e4a0b24.gif)
Уверен, есть более красивое решение, но я сделал так: после факта коллизии возвращаем игроку его характеристики до столкновения, благо PhysicsJS позволяет так себя обманывать.
Результат — сбор звезд не нарушает скорости игрока.
![](https://habrastorage.org/files/477/634/f47/477634f476c84b98a6306a4823f6b038.gif)
Разные модели у движков — физический движок делает поворот объекта вокруг его центра масс, а графический движок по умолчанию разворачивает по левому верхнему углу объекта. Если этот факт не учитывать, то результат будет довольно забавным.
![](https://habrastorage.org/files/65c/0d1/1ee/65c0d11ee77940b2839488d497ca71c7.gif)
Кстати, что-то похожее я наблюдаю в анимации разворота автомобиля в Uber клиенте на Android: там точка поворота так же находится в левом верхнем углу, а не по центру автомобиля. Думаю, это баг, который им лень поправить.
Решением является рисование автомобиля относительно его центра, а не левого верхнего угла.
Теперь все выглядит как надо
![](https://habrastorage.org/files/d35/c56/844/d35c56844ae9435a8600030bf1d7bd22.gif)
Показ кнопок управления на мобильном устройстве — сделал реверанс в сторону прогресса: показываю большие кнопки управления для мобильных устройств. Главное, не забыть, что зажатие кнопки делается touch событиями, и что нужно запретить появление выделения текста от этого долгого нажатия через css стиль.
![](https://habrastorage.org/r/w780q1/files/abb/ac6/bec/abbac6becd1e4e779c3a227e5635c222.jpg)
Расход батареи телефона от работы игры заметно увеличивается. Думаю, виноват физический движок, который активно нагружает процессор работой.
В целом, процесс создания игры не показался мне очень сложным. Используя готовые движки для графики и физики, можно значительно упростить разработку, почти не думая о математике. Насколько это эффективный подход — вопрос для отдельной статьи. Спасибо всем за внимание и надеюсь, мои наработки помогут вам создать что-нибудь свое, если вы давно собирались это сделать! Удачи!
[Исходники на GitHub]
![Racing game](https://habrastorage.org/files/be3/a0e/324/be3a0e3247ae4481a522bfa866e56ee4.png)
Итогом стал небольшой прототип браузерного 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();
});
Можно изобразить этот процесс в виде схемы:
![Процесс игры](https://habrastorage.org/files/b72/f27/863/b72f27863bbc4bceb9368e721bc73fd7.png)
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 такой возможности. В итоге, даже если удалять призовую звезду после коллизии, то движок уже изменил скорость игрока, замедлив его.
![](https://habrastorage.org/files/d37/cff/e66/d37cffe6652c4931b98f16c84e4a0b24.gif)
Уверен, есть более красивое решение, но я сделал так: после факта коллизии возвращаем игроку его характеристики до столкновения, благо 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;
}
Результат — сбор звезд не нарушает скорости игрока.
![](https://habrastorage.org/files/477/634/f47/477634f476c84b98a6306a4823f6b038.gif)
Разные модели у движков — физический движок делает поворот объекта вокруг его центра масс, а графический движок по умолчанию разворачивает по левому верхнему углу объекта. Если этот факт не учитывать, то результат будет довольно забавным.
![](https://habrastorage.org/files/65c/0d1/1ee/65c0d11ee77940b2839488d497ca71c7.gif)
Кстати, что-то похожее я наблюдаю в анимации разворота автомобиля в Uber клиенте на Android: там точка поворота так же находится в левом верхнем углу, а не по центру автомобиля. Думаю, это баг, который им лень поправить.
Пример Uber-подобной анимации
![](https://habrastorage.org/files/0ae/87e/17f/0ae87e17f16445639e3fee1d9026ef2b.gif)
Решением является рисование автомобиля относительно его центра, а не левого верхнего угла.
Код
//render.car.PlayerCar
function paintCabin(g, model){
//...
g.drawRect(model.x - model.w/2, model.y - model.h/2, model.w, model.h);
//...
}
Теперь все выглядит как надо
![](https://habrastorage.org/files/d35/c56/844/d35c56844ae9435a8600030bf1d7bd22.gif)
Показ кнопок управления на мобильном устройстве — сделал реверанс в сторону прогресса: показываю большие кнопки управления для мобильных устройств. Главное, не забыть, что зажатие кнопки делается 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;
}
![](https://habrastorage.org/files/abb/ac6/bec/abbac6becd1e4e779c3a227e5635c222.jpg)
Расход батареи телефона от работы игры заметно увеличивается. Думаю, виноват физический движок, который активно нагружает процессор работой.
Выводы
В целом, процесс создания игры не показался мне очень сложным. Используя готовые движки для графики и физики, можно значительно упростить разработку, почти не думая о математике. Насколько это эффективный подход — вопрос для отдельной статьи. Спасибо всем за внимание и надеюсь, мои наработки помогут вам создать что-нибудь свое, если вы давно собирались это сделать! Удачи!
[Исходники на GitHub]