/physics-typescript

Primary LanguageTypeScriptMIT LicenseMIT

Physics met Matter.js, Typescript en Parcel

Dit project bevat een demo waarbij we MatterJS physics gebruiken in een Typescript Object-Oriented project.

barrels

MatterJS berekent physics simulaties. Bekijk de demo's en de broncode van de demo's

ParcelJS bouwt een project van je dev map. Daarbij worden modules van MatterJS en je eigen project samengevoegd. Parcel zet Typescript om naar Javascript. Ook je afbeeldingen, css en html worden overgezet naar de dist folder!




Installeren

Installeer de Parcel module bundler globaal met -g. Dit zorgt dat je MatterJS modules kan gebruiken! Op de mac kan je er sudo voor zetten, en op windows kan je run as administrator doen, als je iets globaal wil installeren.

npm install -g parcel-bundler

Installeer de benodigde modules met

npm install



Project openen

Tijdens development bekijk je je voortgang met

npm run dev

Als je project af is kan je de dist map bouwen met

npm run build



Physics

Met Matter.JS maak je een physics world. Hierin wordt de positie van elementen uitgerekend door de physics engine. Elementen krijgen gravity, collisions, force, velocity en friction.

De physics world speelt zich puur af in het geheugen, er wordt niks getekend als je geen renderer toevoegt. In dit voorbeeld tekenen we DOM elementen op de positie van de elementen in de physics world.

Je kan de physics world 60 keer per seconde updaten, voordat je de posities gaat uitlezen:

private engine : Matter.Engine
 
private setupMatter(){
    this.engine = Matter.Engine.create()
}

private gameLoop(){
    Matter.Engine.update(this.engine, 1000 / 60) 
    requestAnimationFrame(() => this.gameLoop())
}



Physics objecten toevoegen

Je kan default shapes zoals rectangles en circles toevoegen aan de physics world.

Een rectangle verwacht een x, y, width, height. Een circle verwacht een x,y,radius. Let op dat x,y het middelpunt van het object is.

Een static shape ondervindt geen forces maar geeft wel collisions aan andere objecten. Dit gebruik je voor platforms en de vloer.

let boxOne = Matter.Bodies.rectangle(0, 0, 50, 50)
let boxTwo = Matter.Bodies.rectangle(0, 500, 500, 10, {isStatic:true})
Matter.Composite.add(this.engine.world, [boxOne, boxTwo])



Voorbeeld

Game

De game.ts class maakt de physics world. Daarna maken we game elementen aan, zoals een Box.

class Game {

    private engine : Matter.Engine
    private box : Box
        
    constructor() {
        this.engine = Matter.Engine.create()
        this.box = new Box(this.engine.world)
    }

    gameLoop(){
        Matter.Engine.update(this.engine, 1000 / 60) 
        this.box.update()
        requestAnimationFrame(() => this.gameLoop())
    }
}

Box

De Box class voegt een physics box toe aan de physics world, en maakt een DOM element.

class Box {
    constructor(world : Matter.Composite){
        // physics object
        this.physicsBox = Matter.Bodies.rectangle(x, y, w, h, options)
        Matter.Composite.add(world, this.physicsBox)

        // create div
        this.div = document.createElement("crate")
        document.body.appendChild(this.div)
    }
}

Update de box positie

De Box class kan in de update functie telkens het DOM element op dezelfde plek zetten als het physics element:

update() {
    let pos = this.physicsBox.position // physics position
    let angle = this.physicsBox.angle
    let degrees = angle * (180 / Math.PI)

    this.div.style.transform = `translate(${pos.x - (this.width/2)}px, ${pos.y-(this.height/2)}px) rotate(${degrees}deg)`
}



Controls

Om een karakter of vijand te bewegen gebruik je

  • Matter.body.translate - Hiermee "hardcode" je de nieuwe positie ongeacht physics. Dit gebruik je voor static elementen zoals een platform.
  • Matter.Body.setVelocity - Hiermee zet je een nieuwe snelheid. Bv. om een player of een enemy naar links / rechts te bewegen.
  • Matter.Body.addForce - Hiermee geef je een object een eenmalige "boost", bv. een raket of kogel die je afvuurt, of een player die springt.

Voorbeeld lopen

Matter.Body.setVelocity(this.physicsBox, { x: this.speed, y: this.physicsBox.velocity.y })

Voorbeeld springen

Matter.Body.applyForce(this.physicsBox, { x: this.physicsBox.position.x, y: this.physicsBox.position.y }, { x: 0, y: -0.15 })



Friction, gravity, bouncyness

Je kan de gravity van de wereld op x, y, of geen zetten, afhankelijk van het type spel. Een space game of een top-down view heeft geen gravity.

this.engine = Matter.Engine.create()
this.engine.world.gravity.x = 1
this.engine.world.gravity.y = 0
this.engine.world.gravity.scale = 0.001

Je kan elementen (en zelfs de lucht) friction geven. Je kan elementen restitution geven om ze te laten stuiteren.




Collision

Geef eerst een label mee in het options object om te weten welk physics element een collision heeft.

let player = Matter.Bodies.circle(0, 0, 14, { label: 'player' })

Vervolgens kan je via het onCollisionStart event kijken welke elementen met elkaar botsen:

Matter.Events.on(engine, 'collisionStart', (event) => {
    let collision = event.pairs[0]
    let bodyA = collision.bodyA
    let bodyB = collision.bodyB

    if (bodyA.label === 'player') {
        switch (bodyB.label) {
        case 'enemy':
            score--
            break;
        case 'food':
            score++
            break;
        }
    }
})



Canvas

MatterJS heeft een basic "Render" functie. Die gebruiken we nu niet omdat we onze eigen DOM elementen gebruiken. Je kan ook de matterjs physics rechtstreeks tekenen in een canvas.




Links




Bugs

ParcelJS CSSNano bug. Tijdelijk gefixed met --no-minify. Todo: downgrade parcel naar 1.8.1.