New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

phaser-matter-collision-plugin

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

phaser-matter-collision-plugin

A plugin for making it easier to manage collisions with Phaser + Matter.js

  • 0.10.0
  • Source
  • npm
  • Socket score

Version published
Maintainers
1
Created
Source

Phaser Matter Collision Plugin 💥

A plugin for making it easier to manage collisions with the Phaser game engine and the Matter.js physics engine.

Matter is one of the cool physics engine choices you have in Phaser 3. Phaser has a thin wrapper over Matter's API, so you need to dig into Matter's native collision event system if you want to detect and respond to collisions. That system just gives you a dump of all the pairs of bodies that collided in a tick of the engine. This plugin wraps up that collision logic in a friendlier, more modular way:

const player = this.matter.add.sprite(0, 0, "player");
const trapDoor = this.matter.add.sprite(200, 0, "door");

this.matterCollision.addOnCollideStart({
  objectA: player,
  objectB: trapDoor,
  callback: () => console.log("Player touched door!")
});

Check out the HTML documentation here.

Note: this readme is still in progress, but it should complete enough for you to get started.

Installation

You can install this plugin globally as a script, or locally as a module using your bundler of choice.

As a Script

You can drop in any of the transpiled code into your project as a standalone script. Choose the version that you want:

E.g. if you wanted the minified code, you would add this to your HTML:

<script src="phaser-matter-collision-plugin.min.js"></script>

Or use the jsdelivr CDN:

<script src="//cdn.jsdelivr.net/npm/phaser-matter-collision-plugin"></script>

Now you can use the global PhaserMatterCollisionPlugin. See usage for how to use the plugin.

As a Module

Install via npm:

npm install --save phaser-matter-collision-plugin

To use the transpiled and minified distribution of the library:

import PhaserMatterCollisionPlugin from "phaser-matter-collision-plugin";

To use the raw library (so you can transpile it to match your own project settings):

import PhaserMatterCollisionPlugin from "phaser-matter-collision-plugin/src";

See usage for how to use the plugin.

Usage

Initial Setup

When setting up your game config, add the plugin:

// prettier-ignore
const config = {
  // ...
  physics: {
    default: "matter"
  },
  // Install the scene plugin
  plugins: {
    scene: [
      {
        plugin: PhaserMatterCollisionPlugin, // The plugin class
        key: "matterCollision", // Where to store in Scene.Systems, e.g. scene.sys.matterCollision
        mapping: "matterCollision" // Where to store in the Scene, e.g. scene.matterCollision
      }
    ]
  }
};

const game = new Phaser.Game(config);

Now, within a scene, you can use this.matterCollision to access the plugin instance.

Usage in Scene

Tracking Collisions

The plugin has addOnCollideStart, addOnCollideActive and addOnCollideEnd methods which allow you to listen to collisions between "objects" in your scene. Those objects can be: a native Matter body, a tile, a Matter sprite, any object with a body property, or an array of any of those.

For example, game object vs game object collisions:

const player = this.matter.add.sprite(0, 0, "player");
const trapDoor = this.matter.add.image(200, 0, "door");

this.matterCollision.addOnCollideStart({
  objectA: player,
  objectB: trapDoor,
  callback: function(eventData) {
    // This function will be invoked any time the player and trap door collide
    const { bodyA, bodyB, gameObjectA, gameObjectB, pair } = eventData;
    // bodyA & bodyB are the Matter bodies of the player and door respectively
    // gameObjectA & gameObjectB are the player and door respectively
    // pair is the raw Matter pair data
  },
  context: this // Context to apply to the callback function
});

If you omit the objectB property, you'll get all collisions involving objectA:

const player = this.matter.add.sprite(0, 0, "player");

this.matterCollision.addOnCollideStart({
  objectA: player,
  callback: eventData => {
    const { bodyB, gameObjectB } = eventData;
    console.log("Player touched something.");
    // bodyB will be the matter body that the player touched
    // gameObjectB will be the game object that owns bodyB, or undefined if there's no game object
  }
});

Game object vs Matter sensor:

const player = this.matter.add.sprite(0, 0, "player");
const sensor = this.matter.world.add.rectangle(100, 0, 50, 50, { isStatic: true, isSensor: true });

this.matterCollision.addOnCollideStart({
  objectA: player,
  objectB: sensor,
  callback: eventData => console.log("Player touched hidden sensor")
});

Game object vs array of objects:

const player = this.matter.add.sprite(0, 0, "player");
const enemy1 = this.matter.add.sprite(100, 0, "enemy");
const enemy2 = this.matter.add.sprite(200, 0, "enemy");
const enemy3 = this.matter.add.sprite(300, 0, "enemy");

this.matterCollision.addOnCollideStart({
  objectA: player,
  objectB: [enemy1, enemy2, enemy3],
  callback: eventData => {
    console.log("Player hit an enemy");
    // eventData.gameObjectB will be the specific enemy that was hit!
  }
});

Or, array vs array:

this.matterCollision.addOnCollideStart({
  objectA: [player1, player2],
  objectB: [enemy1, enemy2, enemy3],
  callback: eventData => {
    console.log("A player hit an enemy");
    // eventData.gameObjectA will be the specific player involved in the collision
    // eventData.gameObjectB will be the specific enemy involved in the collision
  }
});

You can listen for collisions vs a single tile (or an array of tiles), but it's likely more useful to do something like:

this.matterCollision.addOnCollideStart({
  objectA: player,
  callback: eventData => {
    const { bodyB, gameObjectB } = eventData;

    if (gameObjectB !== undefined && gameObjectB instanceof Phaser.Tilemaps.Tile) {
      // Now you know that gameObjectB is a Tile, so you can check the index, properties, etc.
      if (gameObjectB.properties.isDeadly) console.log("Stepped on deadly tile");
      else if (gameObjectB.index === 32) console.log("Stepped on the tile with index 32");
    }
  }
});

The plugin also exposes two sets of events via the this.matterCollision.events event emitter:

  • "collisionstart", "collisionactive", "collisionend" - these match the Matter events. They emit a single parameter event. Aside from the normal Matter data in event, each pair in event.pairs has a gameObjectA and gameObjectB property that points to the game object that owns each body (if one exists).
  • "paircollisionstart", "paircollisionactive", "paircollisionend" - these are similar to the above, except they fire once for each pair. They have one parameter that looks like this: { bodyA, bodyB, gameObjectA, gameObjectB, pair }

You can listen to them via this.matterCollision.events.on("collisionstart", ...).

Stop Tracking Collisions

You can stop tracking a collision via removeOnCollideStart, removeOnCollideActive and removeOnCollideEnd. They take the same parameters as addOnCollideStart.

In addition, the addOnCollide methods will also return a function that automatically unsubscribes from the collision event (which can be useful if you use arrow functions):

const unsubscribe = this.matterCollision.addOnCollideStart({
  objectA: player,
  objectB: trapDoor,
  callback: eventData => {
    // Do something, like dropping the door out underneath the player
    // Then unsubscribe so this callback is never called again
    unsubscribe();
  }
});

If you want to remove all listeners that have been added - not just one colliding pair - there are also the following methods:

  • removeAllCollideStartListeners
  • removeAllCollideActiveListeners
  • removeAllCollideEndListeners
  • removeAllCollideListeners - removes start, active and end listeners

Live Examples

For now, you can check out the "tests" folder of this repository for usage examples.

TODO: insert at least one codesandbox TODO: add a module example and a script example

Development

The project is controlled by npm scripts and uses cypress & jest for testing. Cypress is used for end-to-end verification that the plugin works as expected with Phaser. Jest is used for unit testing the plugin (via heavy mocking since Phaser headless mode is not complete).

  • The watch and build tasks will build the plugin source in library/ or the projects in tests/
  • The serve task opens the whole project (starting at the root) in a server
  • The dev task will build & watch the library, tests and open up the server. This is useful for creating tests and updating the library.
  • The dev:cypress task will build & watch the library & tests, as well as open up cypress in headed mode. This is useful for checking out individual tests and debugging them.
  • The test:cypress task will build the tests and run cypress in headless mode to check all end-to-end tests.
  • The test:jest will run the jest tests.

Tests

The cypress tests rely on a particular structure:

  • Each test game inside of "tests/" should have an "index.html" file as the entry point. "src/js/index.js" will be compiled to "build/js/index.js" by webpack. (Cypress doesn't support type="module" on scripts, so this is necessary if we need modules.)
  • Each test has access to test-utils.js which provides startTest, passTest and failTest methods. Call startTest at the beginning and pass/fail when the test passes/fails. This manipulates in the DOM in a way that cypress is expecting.
  • Each test in "cypress/integration/" simply loads up the specified URL and waits for it to pass or timeout. (Technically, startTest and failTest are ignored, but they are useful for visual inspection of a test.)

The jest unit tests rely on a simple mocking of Phaser and Matter. They are stored inside "src/". Once Phaser headless is available, this testing structure could be re-evaluated.

(Moved Section) Why

TODO: put this somewhere else

The Matter collision event system just dumps an event with all the pairs of Matter bodies that collided on the current tick of the physics engine. That leaves it up to the developer to:

  • Figure out which game object the bodies belong to
  • Search through the pairs of bodies to find the collisions that matter
  • Organize the single Matter event into modular pieces
  • Parameter ordering (ab vs ba)

Take something like this:

// Suppose we have a world of matter-enabled objects (and maybe even tiles)
const player = this.matter.add.sprite(0, 0, "player");
const enemy = this.matter.add.sprite(100, 0, "enemy");
const trapDoor = this.matter.add.sprite(200, 0, "door");
const crate = this.matter.add.sprite(300, 0, "crate");

// Suppose we only want to watch for player touching the trap door
this.matter.world.on("collisionstart", event => {
  // Loop over all colliding pairs
  event.pairs.map({ bodyA, bodyB } => {
    // bodyA & bodyB are Matter bodies - which game object owns them?
    // Is this player vs door?
  });
});

And turn it into this:

const player = this.matter.add.sprite(0, 0, "player");
const enemy = this.matter.add.sprite(100, 0, "enemy");
const trapDoor = this.matter.add.sprite(200, 0, "door");
const crate = this.matter.add.sprite(300, 0, "crate");

this.matterCollision.addOnCollideStart({
  objectA: player,
  objectB: trapDoor,
  callback: () => console.log("Player touched trapDoor!")
});

Keywords

FAQs

Package last updated on 18 Aug 2018

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc