Всем привет! В этой статье я продемонстрирую процесс создания игры про грузовик-монстр с использованием Box2DCreateJS.
Если вы не знакомы с Box2DCreateJS, вы можете обратиться к этой статье, чтобы узнать больше.
В нашем сценарии у нас будет грузовик-монстр и рампа, которую мы можем использовать для прыжков и маневров. Мы будем использовать клавиатуру для управления грузовиком.
К концу статьи вот чего мы добьемся!
Прежде чем приступить к кодированию, давайте создадим первоначальную настройку проекта, как описано в этой статье. Как только наш проект будет готов, давайте начнем!
Скачать изображения игры
В этой игре мы будем использовать три изображения:
- Шасси Monster Truck: https://raw.githubusercontent.com/ivangfr/box2dcreatejs/master/images/monster-chassis.png
- Шины Monster Truck: https://raw.githubusercontent.com/ivangfr/box2dcreatejs/master/images/monster-tire.png
- Фон: https://raw.githubusercontent.com/ivangfr/box2dcreatejs/master/images/background.jpg
Загрузите и добавьте их в папку проекта images
.
Импорт Box2DCreateJS
Давайте включим несколько файлов библиотеки Box2DCreateJS в index.html
. Для этого включите следующие строки кода.
<!DOCTYPE html> <html lang="en"> <head> ... <script src="box2dcreatejs/js/Box2dWeb/Box2dWeb-2.1.a.3.js"></script> <script src="box2dcreatejs/js/CreateJS/easeljs-1.0.0.min.js"></script> <script src="box2dcreatejs/js/CreateJS/preloadjs-1.0.0.min.js"></script> <script src="box2dcreatejs/js/Box2DCreateJS/WorldManager.js"></script> <script src="box2dcreatejs/js/Box2DCreateJS/Entity.js"></script> <script src="box2dcreatejs/js/Box2DCreateJS/Render.js"></script> <script src="box2dcreatejs/js/Box2DCreateJS/LoadingIndicator.js"></script> <script src="box2dcreatejs/js/Box2DCreateJS/Link.js"></script> <script src="MyGame.js"></script> ... </head> ... </html>
Создайте и запустите WorldManager
В MyGame.js
определите переменную _worldManager
и создайте ее экземпляр внутри функции initialize
.
В этом примере мы используем свойство preLoad
для загрузки изображений перед запуском игры. После завершения предварительной загрузки вызывается функция gameLogic
. Функция gameLogic
, которая в настоящее время пуста, определена вне функции initialize
.
Важно отметить, что мы включили режим отладки (свойство enableDebug
) для _worldManager
, установив для него значение true
. Это позволит нам увидеть сущности мира и то, как они выглядят за кулисами.
this.Box2DCreateJS = this.Box2DCreateJS || {}; (function () { function MyGame() { this.initialize() } Box2DCreateJS.MyGame = MyGame let _worldManager MyGame.prototype.initialize = function () { const easeljsCanvas = document.getElementById("easeljsCanvas") const box2dCanvas = document.getElementById("box2dCanvas") _worldManager = new Box2DCreateJS.WorldManager( easeljsCanvas, box2dCanvas, { world: new box2d.b2World(new box2d.b2Vec2(0, 10), true), enableDebug: true, preLoad: { files: [ 'images/monster-chassis.png', 'images/monster-tire.png', 'images/background.jpg', ], onComplete: gameLogic } } ) } function gameLogic() { } }())
Создайте нижний предел и пределы сценария
Теперь мы приступим к созданию пола и установке ограничений для нашего сценария. Для этого мы реализуем функцию createFloorAndLimits
в MyGame.js
, а затем добавим к ней вызов в функции gameLogic
.
... function gameLogic() { createFloorAndLimits() } function createFloorAndLimits() { const staticRender = { type: 'draw', drawOpts: { bgColorStyle: 'solid', bgSolidColorOpts: { color: 'black' } } } // Floor _worldManager.createEntity({ type: 'static', x: 5000, y: 490, shape: 'box', boxOpts: { width: 10000, height: 20 }, render: staticRender }) // Left Wall _worldManager.createEntity({ type: 'static', x: 0, y: 0, shape: 'box', boxOpts: { width: 10, height: 1000 }, render: staticRender }) // Right Wall _worldManager.createEntity({ type: 'static', x: 10000, y: 0, shape: 'box', boxOpts: { width: 10, height: 1000 }, render: staticRender }) } ...
Холст EaselJS и Box2D имеет ширину 980 пикселей и высоту 500 пикселей.
На представленном ниже изображении вы можете получить приблизительное представление о размерах static
объектов по отношению к холсту.
Давайте проверим, как это до сих пор
Создайте пейзаж
Вместо того, чтобы довольствоваться простым синим фоном, давайте улучшим его, добавив изображение. Для этого нам нужно импортировать файл Landscape.js
в index.html
.
<!DOCTYPE html> <html lang="en"> <head> ... <script src="box2dcreatejs/js/Box2DCreateJS/Landscape.js"></script> ... </head> ... </html>
Затем реализуйте функцию createLandscape
и включите ее вызов в функцию gameLogic
.
... function gameLogic() { createFloorAndLimits() createLandscape() } ... function createLandscape() { _worldManager.createLandscape({ x: 5000, y: 230, shape: 'box', boxOpts: { width: 10000, height: 500 }, render: { opacity: 0.7, type: 'draw', drawOpts: { bgColorStyle: 'transparent', bgImage: 'images/background.jpg', repeatBgImage: 'repeat-x' } } }) } ...
После включения ландшафта это упрощенный вид размеров.
Проверим как получается.
Создайте грузовик-монстр
Приступим к созданию монстр-трака. Для этого мы реализуем функцию createMonsterTruck
в MyGame.js
и включим ее вызов в функцию gameLogic
.
... function gameLogic() { createFloorAndLimits() createLandscape() const monsterTruck = createMonsterTruck() } ... function createMonsterTruck() { const TRUCK_X = 360, TRUCK_Y = 250 const chassis = _worldManager.createEntity({ type: 'dynamic', x: TRUCK_X, y: TRUCK_Y, shape: 'box', boxOpts: { width: 150, height: 50 }, render: { type: 'image', imageOpts: { image: 'images/monster-chassis.png', adjustImageSize: true } }, name: 'chassis' }) const tireRender = { type: 'image', imageOpts: { image: 'images/monster-tire.png', adjustImageSize: true } } const backTire = _worldManager.createEntity({ type: 'dynamic', x: TRUCK_X - 45, y: TRUCK_Y + 45, shape: 'circle', circleOpts: { radius: 30 }, render: tireRender, bodyDefOpts: { angularVelocity: 70 }, fixtureDefOpts: { restitution: 0.2 } }) const frontTire = _worldManager.createEntity({ type: 'dynamic', x: TRUCK_X + 50, y: TRUCK_Y + 45, shape: 'circle', circleOpts: { radius: 30 }, render: tireRender, bodyDefOpts: { angularVelocity: 70 }, fixtureDefOpts: { restitution: 0.2 } }) const link1 = _worldManager.createLink({ entityA: chassis, entityB: backTire, type: 'revolute', localAnchorA: { x: -1.6, y: 1.6 } }) const link2 = _worldManager.createLink({ entityA: chassis, entityB: frontTire, type: 'revolute', localAnchorA: { x: 1.6, y: 1.6 }, }) return { chassis, backTire, frontTire } } ...
Короче говоря, грузовик-монстр состоит из трех объектов dynamic
: грузовик-монстр chassis
, который имеет форму box
, и шины грузовика-монстра, frontTire
и backTire
, которые имеют форму circle
. chassis
подключен к frontTire
и backTire
с помощью Link
.
Вот как связаны динамические объекты
Давайте проверим, как это происходит до сих пор
Установите плеер
Давайте добавим файлы Player.js
и Camera.js
в наш проект. Мы можем сделать это, включив следующие строки в ваш файл index.html
.
<!DOCTYPE html> <html lang="en"> <head> ... <script src="box2dcreatejs/js/Box2DCreateJS/Player.js"></script> <script src="box2dcreatejs/js/Box2DCreateJS/Camera.js"></script> ... </head> ... </html>
Далее мы создадим экземпляр переменной player
в MyGame.js
, указав события, которые можно с ней выполнять, например, перемещение forward
и backward
и вращение clockwise
или anticlockwise
.
Кроме того, мы установим угловую скорость 70
и -70
, чтобы вращать frontTire
, чтобы переместить игрока forward
или backward
соответственно. Кроме того, мы установим угловую скорость на chassis
, чтобы позволить игроку маневрировать, вращая его по часовой стрелке (угловая скорость 1
) или против часовой стрелки (угловая скорость -1
).
Наконец, мы включим camera
из player
, чтобы мы всегда могли держать грузовик-монстр в поле зрения.
... function gameLogic() { createFloorAndLimits() createLandscape() const monsterTruck = createMonsterTruck() const chassis = monsterTruck.chassis const frontTire = monsterTruck.frontTire const player = _worldManager.createPlayer(chassis, { camera: { adjustX: 490, xAxisOn: true }, events: { backward: () => frontTire.getB2Body().SetAngularVelocity(-70), forward: () => frontTire.getB2Body().SetAngularVelocity(70), anticlockwise: () => chassis.getB2Body().SetAngularVelocity(-1), clockwise: () => chassis.getB2Body().SetAngularVelocity(1) } }) } ...
Добавьте клавиатуру
Чтобы включить управление player
, нам нужно включить клавиатуру. Для этого нам нужно импортировать файл KeyboardHandler.js
в index.html
.
<!DOCTYPE html> <html lang="en"> <head> ... <script src="box2dcreatejs/js/Box2DCreateJS/KeyboardHandler.js"></script> ... </head> ... </html>
В MyGame.js
мы используем _worldManager
для вызова функции createKeyboardHandler
. Эта функция принимает карту клавиш клавиатуры, которые будут использоваться для элементов управления, таких как:
ArrowDown
: переместить игрока назад;ArrowUp
: переместить игрока вперед;ArrowLeft
: вращать игрока против часовой стрелки;ArrowRight
: вращать игрока по часовой стрелке
... function gameLogic() { ... const player = _worldManager.createPlayer(chassis, { ... }) _worldManager.createKeyboardHandler({ keys: { ArrowDown: { onkeydown: () => _worldManager.getPlayer().backward(), keepPressed: true }, ArrowUp: { onkeydown: () => _worldManager.getPlayer().forward(), keepPressed: true }, ArrowLeft: { onkeydown: () => _worldManager.getPlayer().anticlockwise(), keepPressed: true }, ArrowRight: { onkeydown: () => _worldManager.getPlayer().clockwise(), keepPressed: true } } }) } ...
Давай проверим
Создать рампу
Чтобы добавить азарта в нашу игру, давайте создадим пандус. Для этого нам потребуется реализовать функцию createRamp
в MyGame.js
и включить ее вызов в нашу функцию gameLogic
.
... function gameLogic() { ... createRamp() } ... function createRamp() { const staticRender = { type: 'draw', drawOpts: { bgColorStyle: 'solid', bgSolidColorOpts: { color: 'black' } } } // Ramp 1 _worldManager.createEntity({ type: 'static', x: 4000, y: 450, angle: 75, shape: 'box', boxOpts: { width: 10, height: 400 }, render: staticRender }) // Ramp 2 _worldManager.createEntity({ type: 'static', x: 4390, y: 450, angle: -75, shape: 'box', boxOpts: { width: 10, height: 400 }, render: staticRender }) } ...
Проверим, достаточно ли высок пандус 😅
Отключить отладку
Теперь, когда наша игра завершена, мы можем отключить свойство WorldManager
enabledDebug
. Для этого нам просто нужно установить для свойства enabledDebug
значение false
в MyGame.js
.
this.Box2DCreateJS = this.Box2DCreateJS || {}; (function () { ... MyGame.prototype.initialize = function () { ... _worldManager = new Box2DCreateJS.WorldManager( easeljsCanvas, box2dCanvas, { world: new box2d.b2World(new box2d.b2Vec2(0, 10), true), enableDebug: false, ... } ) } ... }())
Пришло время играть! 🕹
Следите за обновлениями
В ближайшие дни я поделюсь серией статей, целью которых является демонстрация обширных возможностей Box2DCreateJS.
В этих статьях я покажу, насколько просто и просто создавать увлекательные игры с помощью Box2DCreateJS.
Исходный код можно найти в Box2DCreateJS GitHub Репозиторий.
Так что следите за обновлениями, чтобы отправиться в захватывающее путешествие в мир разработки игр на JavaScript!