Как создать игру в браузере на джава скрипте
HTML5 вместе с CSS3 и JavaScript дают разработчику широкие возможности создания игр с использованием 3D, анимации, Canvas, математики, цветов, звука, WebGL. Одно из наиболее очевидных преимуществ HTML5 заключается в его независимости и от платформы, и в общем случае от аппаратной начинки.
При детальном рассмотрении можно выявить предоставляемые движками дополнительные возможности: упрощение некоторых часто встречающихся задач или подгрузка ресурсов, оформленный ввод, физика, звук, bitmap’ы (таких, конечно же, немного). Есть и довольно слабо оформленные движки, а есть и те, которые предоставляют в пользование разработчику редактор 2D уровней и инструменты отладки.
Предполагается, что большинство движков служат для сокращения временных затрат на разработку полноценной игры. Однако многие разработчики предпочитают создавать свой проект полностью с нуля, чтобы лучше представлять его устройство. Существует немного JavaScript-HTML5 движков, которые действительно чего-то стоят, однако и у них может быть один большой недостаток: они более не поддерживаются или близки к прекращению поддержки. Поэтому, выбирая движок, остановите свой выбор на тех продуктах, поддержка которых будет длиться достаточно продолжительное время.
Итак, вот, собственно, сами движки.
Crafty
Легкий модульный игровой движок, включающий множество функций: анимацию, управление событиями, перерисовку регионов, отслеживание пересечений и столкновений, спрайтовую графику и многое другое. Поддерживает все браузеры, в т.ч. IE9. Никаких дополнительных усилий прилагать не требуется.
Quintus
Quintus – игровой HTML5-движок, разработанный, чтобы быть модульным и легковесным, с четким JavaScript-подобным интерфейсом. Для того, чтобы реализовать основные особенности ООП-игрового движка в HTML5-движке, в Quintus в некотором отношении схож с jQuery, а также поддерживает плагины, управление событиями и гибкую модель наследования, чтобы упростить повторное использование реализованных функций.
gameQuery
Простой в использовании плагин jQuery, упрощающий разработку игры за счет использования реализованных игровых компонентов. Благодаря особенностям реализации совместим со множеством браузеров, в т.ч. их мобильными версиями.
Идеально подойдет для реализации 2D спрайтовых аркад в ретро-стиле и головоломок вроде Судоку. Он имеет готовый к использованию самозапускающийся игровой цикл. Поддерживаются мышь и клавиатура. Отлично документирован, и главным недостатком можно считать только отсутствие поддержки звуков.
lycheeJS
Игровая библиотека JavaScript, которая предлагает готовое решение для проектирования и реализации HTML5 Canvas и WebGL или нативных OpenGL игр внутри браузера или стационарных платформ. Оптимизирован для Google Chrome.
Enchant.js
Фреймворк Enchant.js для HTML5+JavaScript игр был разработан в 2011 году, распространяется с открытым исходным кодом (MIT лицензия) и потому бесплатен.
The Render Engine
Кросс-браузерный опенсорсный движок, написанный полностью на JavаScript. Созданный с нуля для того, чтобы быть максимально гибким, он имеет обширный API и использует самые новые фичи современных браузеров. Этот фреймворк предназначен, чтобы делать все за вас: ваша идея – его реализация с помощью самых часто используемых инструментов.
GameJS
Большая библиотека на верхнем уровне HTML Canvas. В добавок к функциям рисования в ней имеется растущий ассортимент полезных для разработки игр модулей. Большинство имеющегося API основан на популярной PyGame.
CSS Game Engine
Для формирования страницы используются JavaScript и CSS. Вместе они работают достаточно уверенно и слаженно. Разработан для новичков, обучающихся азам программирования видеоигр. Вам будет проще, если у вас уже есть какие-то навыки работы с CSS.
ClanFX
clanfx основан на JavaScript и CSS и использует плиточную графику. Работает на данный момент в Firefox, Epiphany и Opera. Среди реализованных фич: анимированные спрайты, эффекты заклинаний, постройки, плитки/текстуры и базовый искусственный интеллект.
gTile
Браузерный движок на чистом JavaScript и DHTML. В gTile плиточная графика была выбрана за ее простоту и доступность. Упор в реализации был сделан на высокий уровень интерактивности и поведении игровых объектов. Меньшее внимание было уделено графике. А потому движок подойдет больше для создания текстовых РПГ, а графических возможностей должно хватить для изображения локаций.
Графический JS движок с открытым исходным кодом (GPLv3). Легкий в использовании синтаксис предназначен для того, чтобы сделать фреймворк быстрым и расширяемым.
2D игровая библиотека, основанная на HTML5. Использует и Canvas, и средства DOM.
Cocos2D
Портированный с iPhone графический 2D HTML5-движок на JavaScript. Позволяет быстро создавать 2D игры и графические приложения, которые могут работать на всех современных устройствах без установки дополнительных плагинов.
CopperLicht
WebGL библиотека и JavaScript 3D движок для создания браузерных игр и 3D приложений. Использует WebGL Canvas, поддерживаемый современными браузерами и способный поддерживать рендеринг 3D моделей, используя аппаратное ускорение без плагинов.
Этот HTML/JavaScript движок – реинкарнация набора инструментов для разработки олдскульных RPG (но с более привлекательной графикой). И все только с помощью HTML и JS. Никаких плагинов. Никакого Flash.
LimeJS
HTML5 движок для разработки игр с поддержкой сенсорного ввода. LimeJS создан с использованием Closure Library, созданной Google, и в нем уже реализованы классы и функции для отслеживания времени, событий, обработки форм и анимации. Также фреймворк поддерживает спрайтовые листы (т.е. все используемые изображения могут быть помещены в один файл).
Phaser
Ещё один фреймворк для создания мобильных и десктопных игр на HTML5 с применением Canvas и WebGL. Бесплатный и с открытым исходным кодом. Есть быстрые гайды для старта на JavaScript и TypeScript.
Современная вычислительная техника позволяет создавать классные компьютерные игры! И сейчас, достаточно популярны игры с 3d-графикой, так как, играя в них, ты окунаешься в вымышленный мир и теряешь всякую связь с реальностью. Развитие интернета и браузерных технологий сделало возможным запускать головоломки и стрелялки в любимом Хроме, Мозилле или еще в чем-то там (про Эксплорер помолчим) в онлайн-режиме, без загрузки. Так вот, здесь я расскажу о том, как создать простую трехмерную браузерную игру.
Выбор жанра, сюжета и стилистики игры является достаточно интересной задачей, и от решения этих вопросов может зависеть успех игры. Кроме этого, свои нюансы вносит и выбор технологии, на основе которой будет создаваться продукт. Моя цель – показать элементарные основы этого увлекательного процесса, поэтому я буду делать 3-мерный лабиринт с незамысловатым оформлением. Более того, я это сделаю на чистом коде без использования библиотек и движков, типа three.js (хотя большие проекты лучше делать все-таки на нем), чтобы показать, как можно создать движок для своих нужд. Полностью самописная игра может быть оригинальной, а потому интересной. В общем, оба подхода имеют свои плюсы и минусы.
Я полагаю, если вы читаете эту статью, то вам интересна тема создания игр для гугл Хром, а, значит, понимаете, как работает связка html-css-javaScript, поэтому не буду останавливаться на основах, а сразу приступлю к разработке. В html5 и css3, которые поддерживают все современные браузеры (Эксплорер не в счет), есть возможность расположения блоков в 3-мерном пространстве. Также есть элемент , в котором можно рисовать линии и графические примитивы. Большинство браузерных движков используют , так как на нем можно сделать больше вещей, да и производительность на нем выше. Но для простых вещей вполне можно использовать методы transform-3d, которые будут занимать меньше кода.
1. Инструменты для разработки
Я использую для проверки сайтов и игр только 2 браузера: Chrome и Mozilla. Все остальные браузеры (кроме того самого Эксплорера) построены на движке первого, поэтому использовать их я не вижу смысла, ибо результаты точно такие же, как и в Chrome. Для написания кода достаточно Notepad++.
2. Как реализуется трехмерное пространство в html?
Посмотрим на систему координат блока:
По умолчанию, дочерний блок имеет координаты (left и top) 0 пикселей по x и 0 пикселей по y. Смещение (translate), также 0 пикселей по всем трем осям. Покажем это на примере, для чего создадим новую папку. В нем создадим файлы index.html, style.css и script.js. Откроем index.html и запишем туда следующее:
В файле style.css зададим стили для элементов “container” и “world”.
Сохраним. Откроем index.html c помощью Chrome, получим:
Попробуем применить translate3d к элементу “world”:
Как вы поняли, я перешел в полноэкранный режим. Теперь зададим смещение по оси Z:
transform:translate3d(200px,100px,-1000px);
Если вы снова откроете html-файл в браузере, то никаких изменений вы не увидите. Чтобы увидеть изменения, нужно задать перспективу для объекта “container”:
Квадрат отдалился от нас. Как работает перспектива в html? Взглянем на картинку:
d – расстояние от пользователя до объекта, а z – его координата. Отрицательный z (в html это translateZ) означает, что мы отдалили объект, а положительный – наоборот. Значение perspective определяет величину d. Если же свойство perspective не задано, то значение d принимается за бесконечность, а в этом случае объект визуально не изменяется для пользователя с изменением z. В нашем случае мы задали d = 600px. По умолчанию, точка взгляда перспективы находится в центре элемента, однако ее можно изменить путем задания свойства perspective-origin: .
Теперь повернем “world” вокруг какой-нибудь оси. В сss можно использовать 2 способа вращения. Первый – вращение вокруг осей x,y и z. Для этого используются transform-свойства rotateX(), rotateY() и rotateZ(). Второй – вращение вокруг заданной оси с помощью свойства rotate3d(). Мы будем использовать первый способ, так как он больше подходит для наших задач. Обратите внимание, что оси вращения выходят из центра прямоугольника!
Точка, относительно которой происходят трансформации, может быть изменена путем задания свойства translate-origin: . Итак, зададим вращение “world” по оси x:
Заметно смещение против часовой стрелки. Если же мы добавим rotateY(), то получим смещение уже по оси Y. Важно заметить, что при вращении блока оси вращения также поворачиваются. Вы также можете поэкспериментировать с различными значениями вращения.
Теперь внутри блока “world” создадим еще один блок, для этого добавим тег в html-файл:
В style.css добавим стили к этому блоку:
То есть, элементы внутри блока “world” будут трансформироваться в составе этого блока. Попробуем повернуть “square1” по оси y, добавив к нему стиль вращения:
transform: rotateY(30deg);
«Где вращение?» — спросите вы? На самом деле именно так выглядит проекция блока “square1” на плоскость, образуемую элементом “world”. Но нам нужна не проекция, а настоящее вращение. Чтобы все элементы внутри “world” стали объемными, необходимо применить к нему свойство transform-style:preserve-3d. После подстановки свойства внутрь списка стилей “world” проверим изменения:
3. Создаем движение в трехмерном мире
Для того, чтобы пользователь мог по этому миру передвигаться, нужно задать обработчики нажатия клавиш и перемещения мыши. Управление будет стандартным, какое присутствует в большинстве 3д-шутеров. Клавишами W, S, A, D мы будем перемещаться вперед, назад, влево, вправо, пробелом мы будем прыгать (проще говоря – перемещаться вверх), а мышью мы будем менять направление взгляда. Для этого откроем пока еще пустой файл script.js. Сначала впишем туда такие переменные:
Изначально клавиши не нажаты. Если мы нажмем клавишу, то значение определенной переменной изменится на 1. Если отпустим ее, то она снова станет 0. Реализуем это посредством добавления обработчиков нажатия и отжатия клавиш:
Номер 32 – код пробела. Как видите, тут появилась переменная onGround, указывающая на то, находимся ли мы на земле. Пока разрешим движение вверх, добавив после переменных press… переменную onGround:
Итак, мы добавили алгоритм нажатия и отжатия. Теперь необходимо добавить само передвижение. Что, собственно, мы передвигаем. Представим, что у нас есть объект, который мы двинаем. Назовем его “pawn”. Как и принято у нормальных разработчиков, для него мы создадим отдельный класс “Player”. Классы в javaScript создаются, как ни странно, с помощью функций:
Вставим этот код в script.js в самом начале файла. В конце же файла создадим объект данного типа:
Распишем, что означают эти переменные. x, y, z – это начальные координаты игрока, rx, ry – углы его поворота относительно осей x и y в градусах. Последняя записанная строка означает, что мы создаем объект “pawn” типа “player” (специально пишу тип, а не класс, так как классы в javascript означают несколько другие вещи) с нулевыми начальными координатами. Когда мы двигаем объект, координата мира изменяться не должна, а должна изменяться координата «pawn». Это с точки зрения переменных. А с точки зрения пользователя, игрок находится на одном месте, а вот мир двигается. Таким образом, нужно заставить программу изменять координаты игрока, обрабатывать эти изменения и двигать, в конце концов, мир. На деле это проще, чем кажется.
Итак, после загрузки документа в браузер мы запустим функцию, которая перерисовывает мир. Напишем функцию перерисовки:
В новых браузерах world будет соответствовать элементу с однако надежнее ее присвоить перед функцией update() с помощью следующей конструкции:
Мы будем изменять положение мира каждые 10 мс (100 обновлений в секунду), для чего запустим бесконечный цикл:
Запустим игру. Ура, теперь мы можем двигаться! Однако мир вылазит за пределы рамок элемента «container». Чтобы этого не происходило, зададим css-свойство для него в style.css. Добавим строку overflow:hidden; и посмотрим на изменения. Теперь мир остается в пределах контейнера.
Вполне возможно, что вы не всегда понимаете, куда нужно записывать те или иные строчки кода, поэтому сейчас я вам представлю файлы, которые, как я полагаю, у вас должны получиться:
Если у вас что-то по-другому, обязательно поправьте!
Мы научились двигать персонажа, однако мы еще не умеем поворачивать его! Поворот персонажа, конечно же, будет осуществляться с помощью мыши. Для мыши к переменным состояния клавиш press… мы добавим переменные состояния движения мыши:
А после обработчиков нажатия-отжатия вставим обработчик движения:
В функцию update добавим поворот:
Обратите внимание на то, что движение мыши по оси y вращает pawn по оси x и наоборот. Если мы посмотрим на результат, то ужаснемся от увиденного. Дело в том, что если смещения нет, то MouseX и MouseY остаются прежними, а не приравниваются к нулю. Значит, после каждой итерации update смещения миши должно обнуляться:
Уже лучше, мы избавились от инерции вращения, однако вращение происходит все равно странно! Чтобы понять, что все-таки происходит, добавим div-элемент «pawn» внутрь «container»:
Зададим ему стили в style.css:
Проверим результат. Теперь все ровно! Единственное — синий квадрат остается впереди, но пока оставим это. Чтобы сделать игру от первого лица, а не от третьего, нужно приблизить мир к нам на значение perspective. Сделаем это в script.js в функции update():
Теперь можно делать игру от первого лица. Скроем pawn добавив строку в style.css:
Отлично. Сразу скажу, что ориентироваться в мире с одним квадратом крайне тяжело, поэтому создадим площадку. Добавим в «world» блок «square2»:
А в style.css добавим стили для него:
Теперь все четко. Ну… не совсем. Когда мы нажимаем по клавишам, мы движемся строго по осям X и Z. А мы хотим сделать движение по направлению взгляда. Сделаем следующее: в самом начале файла script.js добавим 2 переменные:
Градус — это pi/180 от радиана. Нам придется применить синусы и косинусы, которые считаются от радиан. Что нужно сделать? Взгляните на рисунок:
Когда наш взгляд направлен под углом и мы хотим пойти вперед, то изменятся обе координаты: X и Z. В случае перемещения в сторону тригонометрические функции просто поменяются местами, а перед образовавшимся синусом изменится знак. Изменим уравнения смещений в update():
Внимательно просмотрите все файлы полностью! Если у вас что-то оказалось не так, то потом обязательно буду ошибки, из-за которых вы сломаете голову!
С движением мы почти разобрались. Но осталось неудобство: курсор мыши может двигаться только в пределах экрана. В трехмерных шутерах можно вращать мышью сколь угодно долго и сколь угодно далеко. Сделаем также: при нажатии на экран игры (на “container”) курсор будет пропадать, и мы сможем вращать мышью без ограничений на размер экрана. Активируем захват мыши при нажатии на экран, для чего перед обработчиками нажатия клавиш поставим обработчик нажатия мыши на “container”:
Теперь совсем другое дело. Однако лучше вообще сделать так, чтобы вращение производилось только тогда, когда курсор захвачен. Введем новую переменную после переменных нажатия клавиш press…
Добавим обработчик изменения состояния захвата курсора (захвачен или нет) перед обработчиком захвата курсора (извините за тавтологию):
А в update() добавим условие вращения “pawn”:
А сам захват мыши при клике по контейнеру разрешим только тогда, когда курсор еще не захвачен:
С движением мы полностью разобрались. Перейдем к генерации мира
4. Загрузка карты
Мир в нашем случае удобнее всего представить в виде множества прямоугольников, имеющих разное местоположение, поворот, размеры и цвет. Вместо цвета также можно использовать текстуры. На самом деле, все современные трехмерные миры в играх – это набор треугольников и прямоугольников, которые называют полигонами. В крутых играх их количество может достигать десятков тысяч в одном только кадре. У нас же их будет около сотни, так как браузер сам по себе имеет невысокую графическую производительность. В предыдущих пунктах мы вставляли блоки “div” внутрь “world”. Но если таких блоков много (сотни), то вставлять каждый из них в контейнер очень утомительно. Да и уровней может быть много. Поэтому пусть эти прямоугольники вставляет javaScript, а не мы. Для него же мы будем создавать специальный массив.
Откроем index.html и удалим из блока “world” все внутренние блоки:
Теперь создадим массив прямоугольников (запихнем его, примеру, между конструктором player и переменными press… в script.js):
Можно было это сделать в виде конструктора, но пока обойдемся чисто массивом, так как запуск цикла расстановки прямоугольников проще реализовать именно через массивы, а не через конструкторы. Я же поясню, что означают цифры в нем. Массив map содержит одномерные массивы из 9 переменных: [. ]. Я думаю, вы понимаете, что первые три числа – это координаты центра прямоугольника, вторые три числа – углы поворота в градусах (относительно того же центра), затем два числа – его размеры и последнее число – фон. Причем фон может быть сплошным цветом, градиентом или фотографией. Последнее очень удобно использовать в качестве текстур.
Массив мы записали, теперь запишем функцию, которая переделает этот массив в собственно прямоугольники:
Поясню, что происходит: мы создаем новую переменную, которая указывает на только что созданный элемент. Ему мы присваиваем id и css-класс (именно это и имеется ввиду под словом класс в языке javaScript), задаем ширину с высотой, фон и трансформацию. Примечательно, что в трансформации помимо координат центра прямоугольника мы указываем смещение на 600 и 400 и половины размеров для того, чтобы центр прямоугольника точно оказался в точке с нужными координатами. Запустим генератор мира перед таймером:
Теперь мы видим площадку с розовыми стенами и серым полом. Как видите, создание карты технически несложно реализовать. А в результате ваш код в трех файлах должен получиться примерно таким:
Если все хорошо, переходим к следующему пункту.
5. Столкновения игрока с объектами мира
Мы создали технику движения, генератор мира из массива. Мы можем передвигаться по миру, который может быть красивым. Однако наш игрок еще никак не взаимодействует с ним. Чтобы это взаимодействие происходило, нам необходимо проверять, сталкивается ли игрок с каким-нибудь прямоугольником или нет? То есть, мы будем проверять наличие коллизий. Для начала вставим пустую функцию:
А вызывать ее будем в update():
Как это происходит? Представим себе, что игрок – это шар с радиусом r. И он движется в сторону прямоугольника:
Очевидно, что если расстояние от шара до плоскости прямоугольника больше r, то коллизии точно не происходит. Чтобы узнать это расстояние, можно перевести координаты игрока в систему координат прямоугольника. Напишем функцию перевода из мировой системы в систему прямоугольника:
И обратную функцию:
Вставим эти функции после функции update(). Я не буду объяснять, как это работает, потому что мне не хочется рассказывать курс аналитической геометрии. Скажу, что есть такие формулы перевода координат при вращении и мы просто ими воспользовались. С точки зрения прямоугольника наш игрок расположен вот так:
В этом случае условие коллизии становится таким: если после смещения шара на величину v (v – это вектор) координата z между –r и r, а координаты x и y лежат в пределах прямоугольника или отстоят от него на величину, не большую r, то объявляется коллизия. В этом случае координата игрока по z после смещения будет составлять r или – r (в зависимости от того, с какой стороны придет игрок). В соответствии с этим, смещение игрока изменяется. Мы специально вызываем коллизию перед тем, как в update() координаты игрока будут обновлены, чтобы вовремя изменить смещение. Таким образом, шар никогда не пересечется с прямоугольником, как бывает в других алгоритмах коллизии. Хотя физически игрок будет представлять собой, скорее, случае куб, мы не будем обращать на это внимание. Итак, реализуем это в javaScript:
x0,y0 и z0 – начальные координаты игрока в системе координат прямоугольника (без поворотов. x1,y1 и z1 – координаты игрока после смещения без учета коллизии. point0, point0, point1 и point2 – начальный радиус-вектор, радиус-вектор после смещения без коллизии и радиус-вектор с коллизией соответственно. map[i][3] и другие, если вы помните, это углы поворота прямоугольника. Заметим, что в условии мы к размерам прямоугольника прибавляем не 100, а 98. Это костыль, зачем, подумайте сами. Запустите игру и вы увидите довольно качественные столкновения.
Как видим, все эти действия происходят в цикле for для всех прямоугольников. При их большом количестве такая операция становится очень дорогой, так как тут и так есть 3 вызова функций преобразований координат, которые тоже производят достаточно много математических операций. Очевидно, что если прямоугольники находятся очень далеко от игрока, то коллизию считать не имеет смысла. Добавим это условие:
Итак, с коллизиями мы разобрались. Мы спокойно можем взбираться и по наклонным поверхностям, а возникновение багов возможно только на медленных системах, если, конечно, возможно. По сути, вся основная техническая часть на этом закончилась. Нам осталось лишь добавить частные вещи, такие как гравитация, вещи, меню, звуки, красивую графику. Но это достаточно легко сделать, а к самому движку, который мы только что сделали, это отношения не имеет. Поэтому об этом я расскажу в следующей части. А сейчас проверьте то, что у вас получилось с моим кодом:
Здравствуйте! Я предлагаю вам со мной создать небольшую казуальную игру на нескольких человек за одним компьютером на Javascript Canvas.
В статье я пошагово разобрала процесс создания такой игры при помощи MooTools и LibCanvas, останавливаясь на каждом мелком действии, объясняя причины и логику добавления нового и рефакторинга существующего кода.
p.s. К сожалению, Хабр обрезает большие раскрашенные статьи где-то на шестидесятитысячном символе, потому я была вынуждена вынести пару участков кода из статьи на pastebin. Если вы хотите прочитать статью, не бегая по ссылках в поисках кода — можно воспользоваться зеркалом.
Правила
Управляем игроком (Player), который должен поймать наживку (Bait) — и при этом увернуться от появляющихся «хищников» (Barrier).
Цель игры — поймать максимальное количество наживок, не соприкоснувшись с хищниками.
При соприкосновении с одним из хищников все они(хищники) пропадают, а очки — обнуляются, что, фактически, равносильно началу игры с нуля.
HTML файл
Создаем начальный файл, на котором будет наше канвас-приложение. Я воспользовалась файлами, размещенными на сайте libcanvas, но это — не обязательно. JavaScript-файлы добавлялись в процессе создания игры, но я не хочу больше возвращаться к этому файлу, потому объявлю их сразу.
[[ Код ./index.html на pastebin ]]
Создаем проект
Для начала создадим сам проект. Нам нужно сделать это только тогда, когда документ будет готов — воспользуемся предоставленным mootools событием " domready "
Также создадим объект LibCanvas.Canvas2D, который поможет нам с анимацией.
window . addEvent ( 'domready' , function () // с помощью Мутулз выберем первый елемент канвас на странице
var elem = $$( 'canvas' )[ 0 ];
// На его основе создадим елемент LibCanvas
var libcanvas = new LibCanvas . Canvas2D ( elem );
// Перерисовка будет осуществлятся каждый кадр, несмотря на наличие или отсутствие изменений
libcanvas . autoUpdate = true ;
// Будем стремится к 60 fps
libcanvas . fps = 60 ;
// Стартуем наше приложение
libcanvas . start ();
>);
Добавляем пользователя
Добавим новый объект — Player, который будет управлятся мышью — его координаты будут равны координатам курсора мыши.
Этот объект будет выглядеть как кружочек определенного цвета и размера (указанные в свойстве)
[[ Код ./js/Player.js на pastebin ]]
Добавим в ./js/start.js перед libcanvas.start(); :
var player = new Player (). setZIndex ( 30 );
libcanvas . addElement ( player );
= Шаг 1 =
Можно заметить, что результат не совсем такой, как мы ожидали, потому-что после каждого из кадров холст не очищается автоматически.
Необходимо добавить очищающий и заливающий черным препроцессор в ./js/start.js
= Шаг 2 =
Добавляем наживку
[[ Код ./js/Bait.js на pastebin ]]
Добавим в ./js/start.js:
// Возьмем индекс поменьше, чтобы наживка рисовалась под игроком
var bait = new Bait (). setZIndex ( 20 );
libcanvas . addElement ( bait );
Рефакторинг — создаем родительский класс
У нас очень похожие классы Bait и Player. Давайте создадим класс GameObject, от которого у нас они будут наследоваться.
Для начала вынесем createPosition из конструктора класса Player:
./js/Player.js
var Player = new Class( // .
initialize : function () // ..
this . shape = new LibCanvas . Shapes . Circle ( center : this . createPosition ()
// .
>,
createPosition : function () return this . libcanvas . mouse . point ;
>,
Теперь создадим класс GameObject
[[ Код ./js/GameObject.js на pastebin ]]
После этого можно облегчить другие классы:
var Bait = new Class( Extends : GameObject ,
var Player = new Class( Extends : GameObject ,
createPosition : function () return this . libcanvas . mouse . point ;
>,
draw : function () if ( this . libcanvas . mouse . inCanvas ) this . parent ();
>
>
>);
Смотрим, ничего ли не сломалось:
= Шаг 3 =
Ура! Все везде работает, а код стал значительно легче.
Дружим игрока с наживкой
У нас на экране все двигается, но реально никакой реакции на наши действия нету.
Давайте начнем с того, что подружим наживку и игрока — при набегании на неё, наживка должна перемещаться в другое случайное место.
Для этого создадим отдельный от рендеринга таймаут, который будет проверять соприкасание.
Пишем в конец ./js/start.js:
Теперь надо реализовать метод isCatched в ./js/Bait.js:
isCatched : function ( player ) if ( player . shape . intersect ( this . shape )) this . move ();
return true ;
>
return false ;
>,
move : function () // перемещаем в случайное место
this . shape . center = this . createPosition ();
>
= Шаг 4 =
Почти отлично, но мы видим, что перемещение грубовато и раздражающе. Лучше было бы, если бы наживка плавно убегала.
Для этого можно воспользоваться одним из поведений ЛибКанвас. Достаточно добавить одну строчку и слегка поменять метод move:
Теперь надо реализовать метод isCatched в ./js/Bait.js:
var Bait = new Class( Extends : GameObject ,
Implements : [ LibCanvas . Behaviors . Moveable ],
move : function () // быстро (800), но плавно перемещаем в случайное место
this . moveTo ( this . createPosition (), 800 );
>
>);
Очень просто, правда? А результат мне нравится намного больше:
= Шаг 5 =
Добавим хищников
var Barrier = new Class( Extends : GameObject ,
initialize : function () this . parent ();
this . speed = new LibCanvas . Point (
$random ( 2 , 5 ), $random ( 2 , 5 )
);
// Через раз летим влево, а не вправо
$random ( 0 , 1 ) && ( this . speed . x *= - 1 );
// Через раз летим вверх, а не вниз
$random ( 0 , 1 ) && ( this . speed . y *= - 1 );
>,
move : function () this . shape . center . move ( this . speed );
return this ;
>,
intersect : function ( player ) return ( player . shape . intersect ( this . shape ));
>
>);
Также чуть изменим ./js/start.js, чтобы при ловле наживки появлялись хищники:
bait . isCatched ( player );
// меняем на
if ( bait . isCatched ( player )) player . createBarrier ();
>
player . checkBarriers ();
Реализуем добавление барьеров для игрока, ./js/Player.js и двигаем их все каждую проверку:
createBarrier : function () var barrier = new Barrier (). setZIndex ( 10 );
this . barriers . push ( barrier );
// Надо не забыть добавить его в наш объект libcanvas, чтобы хищник рендерился
this . libcanvas . addElement ( barrier );
return barrier ;
>,
checkBarriers : function () for (var i = this . barriers . length ; i --;) if ( this . barriers [ i ]. move (). intersect ( this )) this .die();
return true ;
>
>
return false ;
>,
= Шаг 6 =
Отлично, появилось движение в игре. Но мы видим три проблемы:
1. Хищники улетают за игровое поле — надо сделать «отбивание от стен».
2. Иногда наживка успевает схватиться дважды, пока улетает — надо сделать небольшой таймаут «неуязвимости».
3. Не обработан случай смерти.
Хищники отбиваются от стен, наживка получает небольшое время «неуязвимости»
Реализовать отбивание от стен проще простого. Слегка меняем метод move класса Barrier, в файле ./js/Barrier.js:
[[ Код Barrier.move на pastebin ]]
[[ Код Bait.makeInvulnerable на pastebin ]]
= Шаг 7 =
Реализуем смерть и подсчёт очков
Т.к. очки — это сколько раз поймана наживка и она равная количеству хищников на экране — сделать подсчёт очков очень легко:
Чуть расширим метод draw в классе Player, файл ./js/Player.js:
draw : function () // .
this . libcanvas . ctx . text ( text : 'Score : ' + this . barriers . length ,
to : [ 20 , 10 , 200 , 40 ],
color : this . color
>);
>,
// Т.к. очки - это всего-лишь количество хищников - при смерти достаточно удалить всех хищников
die : function () for (var i = this . barriers . length ; i --;) this . libcanvas . rmElement ( this . barriers [ i ]);
>
this . barriers = [];
>
Одиночная игра — закончена!
= Шаг 8 — одиночная игра =
Движение с клавиатуры
speed : 8 ,
checkMovement : function () var pos = this . shape . center ;
if ( this . libcanvas . getKey ( 'left' )) pos . x -= this . speed ;
if ( this . libcanvas . getKey ( 'right' )) pos . x += this . speed ;
if ( this . libcanvas . getKey ( 'up' )) pos . y -= this . speed ;
if ( this . libcanvas . getKey ( 'down' )) pos . y += this . speed ;
>,
= Шаг 9 =
Неприятный нюанс — игрок заползает за экран и там может заблудиться.
Давайте ограничим его передвижение и немножко отрефакторим код, вынеся получение состояния клавиши в отдельный метод
isMoveTo : function ( dir ) return this . libcanvas . getKey ( dir );
>,
checkMovement : function () var pos = this . shape . center ;
var full = this . getFull ();
if ( this . isMoveTo ( 'left' ) && pos . x > 0 ) pos . x -= this . speed ;
if ( this . isMoveTo ( 'right' ) && pos . x < full . width ) pos . x += this . speed ;
if ( this . isMoveTo ( 'up' ) && pos . y > 0 ) pos . y -= this . speed ;
if ( this . isMoveTo ( 'down' ) && pos . y < full . height ) pos . y += this . speed ;
>,
Также слегка изменим метод isMoveTo — чтобы можно было с легкостью изменять клавиши для управления игроком:
control : up : 'up' ,
down : 'down' ,
left : 'left' ,
right : 'right'
>,
isMoveTo : function ( dir ) return this . libcanvas . getKey ( this . control [ dir ]);
>,
= Шаг 10 =
Вводим второго игрока
Изменяем файл ./js/start.js:
var player = new Player (). setZIndex ( 30 );
libcanvas . addElement ( player );
var players = [];
( 2 ). times (function ( i ) var player = new Player (). setZIndex ( 30 + i );
libcanvas . addElement ( player );
players . push ( player );
>);
Содержимое таймера оборачиваем в players . each (function ( player ) < /* * */ >);
= Шаг 11 =
Осталось сделать небольшие поправки:
1. Сдвинуть счёт второго игрока ниже счёта первого игрока.
2. Раскрасить хищников разных игроков в разные цвета.
3. Ради статистики ввести «Рекорд» — какой максимальный счёт каким игроком был достигнут.
Вносим соответствующие изменения в ./js/Player.js:
// Красим хищников в соответствующий игроку цвет:
createBarrier : function () // .
barrier . color = this . barrierColor || this . color ;
// .
>,
// Реализуем подсчет максимального рекорда
maxScore : 0 ,
die : function () this . maxScore = Math . max ( this . maxScore , this . barriers . length );
// .
>,
index : 0 ,
draw : function () this . parent ();
this . libcanvas . ctx . text ( // Выводим максимальный рекорд:
text : 'Score : ' + this . barriers . length + ' (' + this . maxScore + ')' ,
// Смещаем очки игрока на 20 пикселей вниз зависимо от его индекса:
to : [ 20 , 10 + 20 * this . index , 200 , 40 ],
color : this . color
>);
>
>);
Вносим коррективы в ./js/start.js:
( 2 ). times (function ( i ) var player = new Player (). setZIndex ( 30 + i );
player . index = i ;
// .
>);
Поздравляем, игра сделана!
= Шаг 12 — игра на двоих =
Добавляем третьего и четвертого игрока
При желании очень просто добавить третьего и четвертого игрока:
На JS можно создавать сложные и простые игры любых жанров. Мы расскажем как создать 2D игру на JavaScript и HTML5 всего за 20 минут.
Для создания веб игр на языке JavaScript используется технология Canvas , которая позволяет выполнять JavaScript код в HTML5 документе. Вы можете более детально ознакомиться с этой технологией посмотрев видео ниже:
На html странице прописывается лишь тег канвас, а также подключение JS файла, в котором будет происходить обработка всей функциональности. К примеру, HTML файл может выглядеть следующим образом:
В JS файле необходимо найти нужный канвас по id и указать способ работы с ним.
Добавление изображений и аудио
Далее необходимо загрузить все изображения, а также аудио файлы, которые будут использоваться в игре. Для этого используйте класс Image и Audio соответсвенно. Ниже вы можете скачать все необходимые картинки, а также аудиофайлы к игре.
Код добавления изображений и аудио в игру:
Рисование объектов
Чтобы нарисовать объекты, а также добавить функционал к игре необходимо прописать функцию, которая будет постоянно вызываться. Такую функцию вы можете назвать как вам будет угодно. Главное, вам нужно вызвать эту функцию из вне её хотя бы один раз, а внутри неё прописать метод requestAnimationFrame , который будет вызывать функцию постоянно.
Весь код игры стоит помещать в этот метод, ведь в нем он будет постоянно обрабатываться и игра будет выглядеть живой и анимированной.
Чтобы отследить нажатие игрока на какую-либо клавишу, необходимо использовать отслеживание событий - addEventListener . К примеру, чтобы отследить нажатие на любую клавишу на клавиатуре надо прописать следующий код:
Видео урок
Это были лишь небольшие азы перед созданием самой игры. Предлагаем вам ознакомиться с небольшим видео уроком, в ходе которого вы создадите небольшую 2D игру на чистом JavaScript'е.
Весь JS код игры
Ниже вы можете посмотреть на полностью весь код JavaScript файла, который был создан в ходе видео урока выше:
Больше интересных новостей
Библиотека Deeplearn.js для машинного обучения
Языки и сферы их применения. Какой язык программирования выбрать?
Как создать 3D модель (объект) для игры?
Google отказался от работы над полностью автономной машиной
Змейка - классическая игра, которую мы знаем еще с давних времен. Мы представляем вам статью, в ходе которой мы создадим полноценную игру «Змейка» на чистом JavaScript и HTML5.
Для создания веб игр на языке JavaScript используется технология Canvas , которая позволяет выполнять JavaScript код в HTML5 документе. Вы можете более детально ознакомиться с этой технологией посмотрев видео ниже:
HTML страница может содержать классическую разметку, в которую необходимо вписать canvas для отображения игры внутри него. Пример кода:
Внутри этого файла мы подключаем скрипт "game.js", который будет описывать весь функционал нашей игры.
JavaScript файл
Внутри JavaScript файла добавьте выборку канваса, а также укажите контекст игры.
Добавление изображений и аудио
Далее необходимо загрузить все основные изображения, которые будут использоваться в игре. Для этого используйте класс Image . Ниже вы можете скачать все необходимые картинки к игре.
Код добавления изображений и аудио в игру:
Рисование объектов
Чтобы нарисовать объекты, а также добавить функционал к игре необходимо прописать функцию, которая будет постоянно вызываться. Такую функцию вы можете назвать как вам будет угодно. Чтобы функция работала постоянно, вы можете запустите её выполнение через setInterval() .
Весь код игры стоит помещать в этот метод, ведь в нем он будет постоянно обрабатываться и игра будет выглядеть живой и анимированной.
Чтобы отследить нажатие игрока на какую-либо клавишу, необходимо использовать отслеживание событий - addEventListener . К примеру, чтобы отследить нажатие на любую клавишу на клавиатуре надо прописать следующий код:
Видео урок
Это были лишь небольшие азы перед созданием самой игры. Предлагаем вам ознакомиться с большим видео уроком, в ходе которого вы создадите 2D игру "Змейка" на чистом JavaScript'е.
Полезные ссылки из видео:
Весь JS код игры
Ниже вы можете посмотреть на полностью весь код JavaScript файла, который был создан в ходе видео урока выше:
Больше интересных новостей
7 полезных GitHub-проектов для всех программистов JavaScript
Пишем телеграмм бота на Python / Бот по выбору языка программирования
ТОП 10: Подборка хобби для программиста
Лучшие тренды и технологии в программировании
Читайте также: