Теплым летним вечером посетила мысль, которая, наверняка, посещает многих: хочу сделать свою игру! Энергии было через край, поэтому работа пошла с огоньком.
Итогом стал небольшой прототип браузерного 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]
Поделиться с друзьями
Комментарии (4)
semenyakinVS
30.09.2016 11:57+1Уверен, есть более красивое решение, но я сделал так: после факта коллизии возвращаем игроку его характеристики до столкновения, благо PhysicsJS позволяет так себя обманывать
Ужас какой… Неужели прямо совсем-совсем нет API для триггеров по коллизии без воздействия на состояние физики?
vlreshet
30.09.2016 12:31Имхо, здесь работают коллизии, но вменяемой физикой и не пахнет. Всё как-будто резиновое, «машина» отскакивает и крутится как только ей вздумается
GreatRash
Phaser в миллиард раз удобней чем Pixi (при том, что это надстройка над Pixi). И из коробки предлагает несколько физических движков (Ninja, P2, Box2d). Заодно он предлагает систему плагинов (например плагин для UI: slick-ui.com).
CrazyNiger
Некорректно сравнивать Phaser и Pixi. Phaser – это игровой фрэймворк, а Pixi – библиотека для рендеринга (без разницы чего, игры – лишь одно из применений).
И в третьей версии Phaser'а разработчики решили отказаться от Pixi и пилят свой рендер.