Добавление физики в веб-компоненты
В предыдущем посте я рассмотрел использование веб-компонентов (настраиваемых элементов) для разработки браузерных игр.
Сегодня мы добавим физику в наши HTML-теги просто потому, что это возможно! И немного узнать о веб-компонентах и физике в javascript Материя.JS
Мы рассмотрим:
- Пользовательские элементы
- Цикл игры
- Добавление физики (с Matter.js)
- Настройка проекта (с Parcel.js)
Симуляция с прыгучими бочками, неподатливыми ящиками, платформами и персонажем игрока..
Пример кода находится в Typescript, но вы можете опустить аннотации типа, такие как a:number
и public, private
конвертировать в Javascript.
Пользовательские элементы
Пользовательский элемент — это HTML-тег, к которому добавлен исполняемый код. Это очень удобно для игровых объектов! Мы будем использовать это, чтобы добавить физику позже. Пользовательские элементы можно вкладывать друг в друга для создания иерархии. Имена тегов должны заканчиваться на -component
(по крайней мере, я получаю сообщение об ошибке, если пропущу это)…
HTML
<game-component> <platform-component></platform-component> <crate-component></crate-component> <player-component></player-component>
</game-component>
CSS
Мы будем использовать translate
для позиционирования наших элементов с помощью javascript, так что это означает, что все элементы должны position:absolute
и display:block
. Вы можете использовать фоновое изображение для визуализации, это короче и быстрее, чем использование <img>
теги, и вы можете использовать повторяющиеся фоны.
platform-component { position:absolute; display:block; background-image:url(./images/platform.png); width:400px; height:20px;
}
МАШИНОПИСЬ
Сначала мы должны связать наш код с тегом HTML, создав класс и зарегистрировав его с помощью customElments.define()
.
😬 В Javascript все точно так же, за исключением
:number
аннотации типов
export class Crate extends HTMLElement { constructor(x:number, y:number) { super() console.log(`I am a crate at ${x}, ${y}`) }
} customElements.define('crate-component', Crate)
Вы можете добавить его в DOM, поместив тег в HTML-документ: <crate-component></crate-component>
. Но если мы делаем это с помощью кода, мы можем передавать аргументы конструктора, в данном случае x
и y
должность. Это удобно, если нам нужно несколько ящиков в разных позициях:
let c = new Crate(200,20)
document.body.appendChild(c)
ИГРОВОЙ ЦИКЛ
Чтобы использовать физику, нам нужен игровой цикл. Это будет обновлять физический движок 60 раз в секунду. Затем игровой цикл обновит все пользовательские элементы. В этом примере мы создаем игровой класс с игровым циклом, который обновляет все ящики.
import { Crate } from "./crate" export class Game extends HTMLElement { private crates : Crate[] = [] constructor() { super() this.elements.push(new Crate(270, 20)) this.gameLoop() } private gameLoop(){ for (let c of this.crates){ c.update() } requestAnimationFrame(() => this.gameLoop()) }
}
customElements.define('game-component', Game)
Компонент ящика получает функцию обновления для translate
его положение.
export class Crate extends HTMLElement { constructor(private x:number, private y:number) { super() } public update() { this.style.transform = `translate(${this.x}px, ${this.y}px)` }
}
customElements.define('crate-component', Crate)
🔥 ФИЗИКА
НАКОНЕЦ мы добираемся до точки, где мы добавляем Matter.js физика! Matter.js создает физический движок, который может работать незаметно в фоновом режиме.. Если мы добавим к нему такие объекты, как коробки, цилиндры, полы и потолки, это создаст физическую симуляцию с этими объектами. Наши элементы будут реагировать на гравитацию, трение, скорость, силу, упругость и получать точное обнаружение столкновений.
Matter.js имеет renderer
который может рисовать эти объекты прямо на холсте, но это скучно 🥱. Мы будем использовать позиции физических элементов для позиционирования элементов DOM!
Строить планы:
1 — Добавление физического мира в игровой класс 2 — Добавление физики в ящики
3 — Что еще вы можете сделать с физикой?
1 — Добавление Matter.js в класс Game
import Matter from 'matter-js'
import { Crate } from "./crate" export class Game extends HTMLElement { private engine : Matter.Engine private world : Matter.World private crates : Crate[] = [] constructor() { super() this.engine = Matter.Engine.create() this.world = this.engine.world this.crates.push( new Crate(this.world, 270, 20, 60, 60), new Crate(this.world, 320, 70, 60, 60) ) this.gameLoop() } private gameLoop(){ Matter.Engine.update(this.engine, 1000 / 60) for (let c of this.crates){ c.update() } requestAnimationFrame(() => this.gameLoop()) }
} customElements.define('game-component', Game)
2 — Добавление физики в ящики
Класс Crate добавит физический ящик в физический мир. Затем он прочитает позицию поля физики в функции обновления и обновит позицию элемента ящика в мире DOM.
import Matter from 'matter-js' export class Crate extends HTMLElement { private physicsBox: Matter.Body constructor(x: number, y: number, private width: number, private height: number) { super() this.physicsBox = Matter.Bodies.rectangle(x, y, this.width, this.height, options) Matter.Composite.add(game.getWorld(), this.physicsBox) document.body.appendChild(this) } public update() { let pos = this.physicsBox.position let angle = this.physicsBox.angle let degrees = angle * (180 / Math.PI) this.style.transform = `translate(${pos.x - (this.width/2)}px, ${pos.y-(this.height/2)}px) rotate(${degrees}deg)` }
}
customElements.define('crate-component', Crate)
3 — Что еще вы можете сделать с физикой?
Мы действительно только начинаем использовать Matter.JS. Чтобы построить игру, которую вы видите на изображениях из этого поста, вы используете следующие концепции:
Статические элементы
Это такие элементы, как платформы и стены, к которым не применяются силы, но которые все же вызывают столкновения.
this.physicsBox = Matter.Bodies.rectangle(x, y, w, h, {isStatic:true})
Скорость
Установив скорость объекта вручную, вы можете создать персонажа игрока или врага, который движется в соответствии с вводом игрока.
Matter.Body.setVelocity(this.physicsBox, { x: 5, y: this.physicsBox.velocity.y })
Сила
Добавляя сила вы можете временно подтолкнуть объект в определенном направлении, например, ракету или пулю. Вы можете использовать силу, чтобы сделать прыжок персонажа.
Matter.Body.applyForce(this.physicsBox, { x: this.physicsBox.position.x, y: this.physicsBox.position.y }, { x: 0, y: -0.15 })
Настройка проекта
Вы можете настроить приведенный выше проект (с Typescript или без него), используя Parcel для объединения ваших модулей:
npm install -g parcel-bundler
npm install matter-js
npm install @types/matter-js
npm install typescript
Затем вы можете запустить проект в режиме просмотра, используя
Или построить весь проект, используя
parcel build dev/index.html --public-url ./
Заключение
Надеюсь, этот пост не стал слишком длинным! Я думаю, что этот подход очень забавен, но действительно ли он полезен по сравнению с использованием холста для моделирования физики? Хорошо…
- Элементы холста не могут иметь прослушивателей событий
- В Canvas нет красивого дерева DOM, по которому можно было бы пройти.
Недостатки:
- Рендеринг и структура игры слишком переплетены (вы не можете легко переключиться на рендеринг холста на поздней стадии разработки).
- Если вы хотите, чтобы вокруг прыгали тысячи (или десятки тысяч) объектов, холст гораздо эффективнее.