phaser/src/physics/matter-js/MatterPhysics.js
2020-11-23 10:22:13 +00:00

1463 lines
51 KiB
JavaScript

/**
* @author Richard Davey <rich@photonstorm.com>
* @copyright 2020 Photon Storm Ltd.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var ALIGN_CONST = require('../../display/align/const');
var Axes = require('./lib/geometry/Axes');
var Bodies = require('./lib/factory/Bodies');
var Body = require('./lib/body/Body');
var BodyBounds = require('./BodyBounds');
var Bounds = require('./lib/geometry/Bounds');
var Class = require('../../utils/Class');
var Composite = require('./lib/body/Composite');
var Composites = require('./lib/factory/Composites');
var Constraint = require('./lib/constraint/Constraint');
var Detector = require('./lib/collision/Detector');
var DistanceBetween = require('../../math/distance/DistanceBetween');
var Factory = require('./Factory');
var GetFastValue = require('../../utils/object/GetFastValue');
var GetValue = require('../../utils/object/GetValue');
var Grid = require('./lib/collision/Grid');
var MatterAttractors = require('./lib/plugins/MatterAttractors');
var MatterCollisionEvents = require('./lib/plugins/MatterCollisionEvents');
var MatterLib = require('./lib/core/Matter');
var MatterWrap = require('./lib/plugins/MatterWrap');
var Merge = require('../../utils/object/Merge');
var Pair = require('./lib/collision/Pair');
var Pairs = require('./lib/collision/Pairs');
var Plugin = require('./lib/core/Plugin');
var PluginCache = require('../../plugins/PluginCache');
var Query = require('./lib/collision/Query');
var Resolver = require('./lib/collision/Resolver');
var SAT = require('./lib/collision/SAT');
var SceneEvents = require('../../scene/events');
var Svg = require('./lib/geometry/Svg');
var Vector = require('./lib/geometry/Vector');
var Vertices = require('./lib/geometry/Vertices');
var World = require('./World');
/**
* @classdesc
* The Phaser Matter plugin provides the ability to use the Matter JS Physics Engine within your Phaser games.
*
* Unlike Arcade Physics, the other physics system provided with Phaser, Matter JS is a full-body physics system.
* It features:
*
* * Rigid bodies
* * Compound bodies
* * Composite bodies
* * Concave and convex hulls
* * Physical properties (mass, area, density etc.)
* * Restitution (elastic and inelastic collisions)
* * Collisions (broad-phase, mid-phase and narrow-phase)
* * Stable stacking and resting
* * Conservation of momentum
* * Friction and resistance
* * Constraints
* * Gravity
* * Sleeping and static bodies
* * Rounded corners (chamfering)
* * Views (translate, zoom)
* * Collision queries (raycasting, region tests)
* * Time scaling (slow-mo, speed-up)
*
* Configuration of Matter is handled via the Matter World Config object, which can be passed in either the
* Phaser Game Config, or Phaser Scene Config. Here is a basic example:
*
* ```js
* physics: {
* default: 'matter',
* matter: {
* enableSleeping: true,
* gravity: {
* y: 0
* },
* debug: {
* showBody: true,
* showStaticBody: true
* }
* }
* }
* ```
*
* This class acts as an interface between a Phaser Scene and a single instance of the Matter Engine.
*
* Use it to access the most common Matter features and helper functions.
*
* You can find details, documentation and examples on the Matter JS website: https://brm.io/matter-js/
*
* @class MatterPhysics
* @memberof Phaser.Physics.Matter
* @constructor
* @since 3.0.0
*
* @param {Phaser.Scene} scene - The Phaser Scene that owns this Matter Physics instance.
*/
var MatterPhysics = new Class({
initialize:
function MatterPhysics (scene)
{
/**
* The Phaser Scene that owns this Matter Physics instance
*
* @name Phaser.Physics.Matter.MatterPhysics#scene
* @type {Phaser.Scene}
* @since 3.0.0
*/
this.scene = scene;
/**
* A reference to the Scene Systems that belong to the Scene owning this Matter Physics instance.
*
* @name Phaser.Physics.Matter.MatterPhysics#systems
* @type {Phaser.Scenes.Systems}
* @since 3.0.0
*/
this.systems = scene.sys;
/**
* The parsed Matter Configuration object.
*
* @name Phaser.Physics.Matter.MatterPhysics#config
* @type {Phaser.Types.Physics.Matter.MatterWorldConfig}
* @since 3.0.0
*/
this.config = this.getConfig();
/**
* An instance of the Matter World class. This class is responsible for the updating of the
* Matter Physics world, as well as handling debug drawing functions.
*
* @name Phaser.Physics.Matter.MatterPhysics#world
* @type {Phaser.Physics.Matter.World}
* @since 3.0.0
*/
this.world;
/**
* An instance of the Matter Factory. This class provides lots of functions for creating a
* wide variety of physics objects and adds them automatically to the Matter World.
*
* You can use this class to cut-down on the amount of code required in your game, however,
* use of the Factory is entirely optional and should be seen as a development aid. It's
* perfectly possible to create and add components to the Matter world without using it.
*
* @name Phaser.Physics.Matter.MatterPhysics#add
* @type {Phaser.Physics.Matter.Factory}
* @since 3.0.0
*/
this.add;
/**
* An instance of the Body Bounds class. This class contains functions used for getting the
* world position from various points around the bounds of a physics body.
*
* @name Phaser.Physics.Matter.MatterPhysics#bodyBounds
* @type {Phaser.Physics.Matter.BodyBounds}
* @since 3.22.0
*/
this.bodyBounds;
// Body
/**
* A reference to the `Matter.Body` module.
*
* The `Matter.Body` module contains methods for creating and manipulating body models.
* A `Matter.Body` is a rigid body that can be simulated by a `Matter.Engine`.
* Factories for commonly used body configurations (such as rectangles, circles and other polygons) can be found in the `Bodies` module.
*
* @name Phaser.Physics.Matter.MatterPhysics#body
* @type {MatterJS.BodyFactory}
* @since 3.18.0
*/
this.body = Body;
/**
* A reference to the `Matter.Composite` module.
*
* The `Matter.Composite` module contains methods for creating and manipulating composite bodies.
* A composite body is a collection of `Matter.Body`, `Matter.Constraint` and other `Matter.Composite`, therefore composites form a tree structure.
* It is important to use the functions in this module to modify composites, rather than directly modifying their properties.
* Note that the `Matter.World` object is also a type of `Matter.Composite` and as such all composite methods here can also operate on a `Matter.World`.
*
* @name Phaser.Physics.Matter.MatterPhysics#composite
* @type {MatterJS.CompositeFactory}
* @since 3.22.0
*/
this.composite = Composite;
// Collision:
/**
* A reference to the `Matter.Detector` module.
*
* The `Matter.Detector` module contains methods for detecting collisions given a set of pairs.
*
* @name Phaser.Physics.Matter.MatterPhysics#detector
* @type {MatterJS.DetectorFactory}
* @since 3.22.0
*/
this.detector = Detector;
/**
* A reference to the `Matter.Grid` module.
*
* The `Matter.Grid` module contains methods for creating and manipulating collision broadphase grid structures.
*
* @name Phaser.Physics.Matter.MatterPhysics#grid
* @type {MatterJS.GridFactory}
* @since 3.22.0
*/
this.grid = Grid;
/**
* A reference to the `Matter.Pair` module.
*
* The `Matter.Pair` module contains methods for creating and manipulating collision pairs.
*
* @name Phaser.Physics.Matter.MatterPhysics#pair
* @type {MatterJS.PairFactory}
* @since 3.22.0
*/
this.pair = Pair;
/**
* A reference to the `Matter.Pairs` module.
*
* The `Matter.Pairs` module contains methods for creating and manipulating collision pair sets.
*
* @name Phaser.Physics.Matter.MatterPhysics#pairs
* @type {MatterJS.PairsFactory}
* @since 3.22.0
*/
this.pairs = Pairs;
/**
* A reference to the `Matter.Query` module.
*
* The `Matter.Query` module contains methods for performing collision queries.
*
* @name Phaser.Physics.Matter.MatterPhysics#query
* @type {MatterJS.QueryFactory}
* @since 3.22.0
*/
this.query = Query;
/**
* A reference to the `Matter.Resolver` module.
*
* The `Matter.Resolver` module contains methods for resolving collision pairs.
*
* @name Phaser.Physics.Matter.MatterPhysics#resolver
* @type {MatterJS.ResolverFactory}
* @since 3.22.0
*/
this.resolver = Resolver;
/**
* A reference to the `Matter.SAT` module.
*
* The `Matter.SAT` module contains methods for detecting collisions using the Separating Axis Theorem.
*
* @name Phaser.Physics.Matter.MatterPhysics#sat
* @type {MatterJS.SATFactory}
* @since 3.22.0
*/
this.sat = SAT;
// Constraint
/**
* A reference to the `Matter.Constraint` module.
*
* The `Matter.Constraint` module contains methods for creating and manipulating constraints.
* Constraints are used for specifying that a fixed distance must be maintained between two bodies (or a body and a fixed world-space position).
* The stiffness of constraints can be modified to create springs or elastic.
*
* @name Phaser.Physics.Matter.MatterPhysics#constraint
* @type {MatterJS.ConstraintFactory}
* @since 3.22.0
*/
this.constraint = Constraint;
// Factory
/**
* A reference to the `Matter.Bodies` module.
*
* The `Matter.Bodies` module contains factory methods for creating rigid bodies
* with commonly used body configurations (such as rectangles, circles and other polygons).
*
* @name Phaser.Physics.Matter.MatterPhysics#bodies
* @type {MatterJS.BodiesFactory}
* @since 3.18.0
*/
this.bodies = Bodies;
/**
* A reference to the `Matter.Composites` module.
*
* The `Matter.Composites` module contains factory methods for creating composite bodies
* with commonly used configurations (such as stacks and chains).
*
* @name Phaser.Physics.Matter.MatterPhysics#composites
* @type {MatterJS.CompositesFactory}
* @since 3.22.0
*/
this.composites = Composites;
// Geometry
/**
* A reference to the `Matter.Axes` module.
*
* The `Matter.Axes` module contains methods for creating and manipulating sets of axes.
*
* @name Phaser.Physics.Matter.MatterPhysics#axes
* @type {MatterJS.AxesFactory}
* @since 3.22.0
*/
this.axes = Axes;
/**
* A reference to the `Matter.Bounds` module.
*
* The `Matter.Bounds` module contains methods for creating and manipulating axis-aligned bounding boxes (AABB).
*
* @name Phaser.Physics.Matter.MatterPhysics#bounds
* @type {MatterJS.BoundsFactory}
* @since 3.22.0
*/
this.bounds = Bounds;
/**
* A reference to the `Matter.Svg` module.
*
* The `Matter.Svg` module contains methods for converting SVG images into an array of vector points.
*
* To use this module you also need the SVGPathSeg polyfill: https://github.com/progers/pathseg
*
* @name Phaser.Physics.Matter.MatterPhysics#svg
* @type {MatterJS.SvgFactory}
* @since 3.22.0
*/
this.svg = Svg;
/**
* A reference to the `Matter.Vector` module.
*
* The `Matter.Vector` module contains methods for creating and manipulating vectors.
* Vectors are the basis of all the geometry related operations in the engine.
* A `Matter.Vector` object is of the form `{ x: 0, y: 0 }`.
*
* @name Phaser.Physics.Matter.MatterPhysics#vector
* @type {MatterJS.VectorFactory}
* @since 3.22.0
*/
this.vector = Vector;
/**
* A reference to the `Matter.Vertices` module.
*
* The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices.
* A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`.
* A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull).
*
* @name Phaser.Physics.Matter.MatterPhysics#vertices
* @type {MatterJS.VerticesFactory}
* @since 3.22.0
*/
this.vertices = Vertices;
/**
* A reference to the `Matter.Vertices` module.
*
* The `Matter.Vertices` module contains methods for creating and manipulating sets of vertices.
* A set of vertices is an array of `Matter.Vector` with additional indexing properties inserted by `Vertices.create`.
* A `Matter.Body` maintains a set of vertices to represent the shape of the object (its convex hull).
*
* @name Phaser.Physics.Matter.MatterPhysics#verts
* @type {MatterJS.VerticesFactory}
* @since 3.14.0
*/
this.verts = Vertices;
/**
* An internal temp vector used for velocity and force calculations.
*
* @name Phaser.Physics.Matter.MatterPhysics#_tempVec2
* @type {MatterJS.Vector}
* @private
* @since 3.22.0
*/
this._tempVec2 = Vector.create();
// Matter plugins
if (GetValue(this.config, 'plugins.collisionevents', true))
{
this.enableCollisionEventsPlugin();
}
if (GetValue(this.config, 'plugins.attractors', false))
{
this.enableAttractorPlugin();
}
if (GetValue(this.config, 'plugins.wrap', false))
{
this.enableWrapPlugin();
}
Resolver._restingThresh = GetValue(this.config, 'restingThresh', 4);
Resolver._restingThreshTangent = GetValue(this.config, 'restingThreshTangent', 6);
Resolver._positionDampen = GetValue(this.config, 'positionDampen', 0.9);
Resolver._positionWarming = GetValue(this.config, 'positionWarming', 0.8);
Resolver._frictionNormalMultiplier = GetValue(this.config, 'frictionNormalMultiplier', 5);
scene.sys.events.once(SceneEvents.BOOT, this.boot, this);
scene.sys.events.on(SceneEvents.START, this.start, this);
},
/**
* This method is called automatically, only once, when the Scene is first created.
* Do not invoke it directly.
*
* @method Phaser.Physics.Matter.MatterPhysics#boot
* @private
* @since 3.5.1
*/
boot: function ()
{
this.world = new World(this.scene, this.config);
this.add = new Factory(this.world);
this.bodyBounds = new BodyBounds();
this.systems.events.once(SceneEvents.DESTROY, this.destroy, this);
},
/**
* This method is called automatically by the Scene when it is starting up.
* It is responsible for creating local systems, properties and listening for Scene events.
* Do not invoke it directly.
*
* @method Phaser.Physics.Matter.MatterPhysics#start
* @private
* @since 3.5.0
*/
start: function ()
{
if (!this.world)
{
this.world = new World(this.scene, this.config);
this.add = new Factory(this.world);
}
var eventEmitter = this.systems.events;
eventEmitter.on(SceneEvents.UPDATE, this.world.update, this.world);
eventEmitter.on(SceneEvents.POST_UPDATE, this.world.postUpdate, this.world);
eventEmitter.once(SceneEvents.SHUTDOWN, this.shutdown, this);
},
/**
* This internal method is called when this class starts and retrieves the final Matter World Config.
*
* @method Phaser.Physics.Matter.MatterPhysics#getConfig
* @since 3.0.0
*
* @return {Phaser.Types.Physics.Matter.MatterWorldConfig} The Matter World Config.
*/
getConfig: function ()
{
var gameConfig = this.systems.game.config.physics;
var sceneConfig = this.systems.settings.physics;
var config = Merge(
GetFastValue(sceneConfig, 'matter', {}),
GetFastValue(gameConfig, 'matter', {})
);
return config;
},
/**
* Enables the Matter Attractors Plugin.
*
* The attractors plugin that makes it easy to apply continual forces on bodies.
* It's possible to simulate effects such as wind, gravity and magnetism.
*
* https://github.com/liabru/matter-attractors
*
* This method is called automatically if `plugins.attractors` is set in the Matter World Config.
* However, you can also call it directly from within your game.
*
* @method Phaser.Physics.Matter.MatterPhysics#enableAttractorPlugin
* @since 3.0.0
*
* @return {this} This Matter Physics instance.
*/
enableAttractorPlugin: function ()
{
Plugin.register(MatterAttractors);
Plugin.use(MatterLib, MatterAttractors);
return this;
},
/**
* Enables the Matter Wrap Plugin.
*
* The coordinate wrapping plugin that automatically wraps the position of bodies such that they always stay
* within the given bounds. Upon crossing a boundary the body will appear on the opposite side of the bounds,
* while maintaining its velocity.
*
* https://github.com/liabru/matter-wrap
*
* This method is called automatically if `plugins.wrap` is set in the Matter World Config.
* However, you can also call it directly from within your game.
*
* @method Phaser.Physics.Matter.MatterPhysics#enableWrapPlugin
* @since 3.0.0
*
* @return {this} This Matter Physics instance.
*/
enableWrapPlugin: function ()
{
Plugin.register(MatterWrap);
Plugin.use(MatterLib, MatterWrap);
return this;
},
/**
* Enables the Matter Collision Events Plugin.
*
* Note that this plugin is enabled by default. So you should only ever need to call this
* method if you have specifically disabled the plugin in your Matter World Config.
* You can disable it by setting `plugins.collisionevents: false` in your Matter World Config.
*
* This plugin triggers three new events on Matter.Body:
*
* 1. `onCollide`
* 2. `onCollideEnd`
* 3. `onCollideActive`
*
* These events correspond to the Matter.js events `collisionStart`, `collisionActive` and `collisionEnd`, respectively.
* You can listen to these events via Matter.Events or they will also be emitted from the Matter World.
*
* This plugin also extends Matter.Body with three convenience functions:
*
* `Matter.Body.setOnCollide(callback)`
* `Matter.Body.setOnCollideEnd(callback)`
* `Matter.Body.setOnCollideActive(callback)`
*
* You can register event callbacks by providing a function of type (pair: Matter.Pair) => void
*
* https://github.com/dxu/matter-collision-events
*
* @method Phaser.Physics.Matter.MatterPhysics#enableCollisionEventsPlugin
* @since 3.22.0
*
* @return {this} This Matter Physics instance.
*/
enableCollisionEventsPlugin: function ()
{
Plugin.register(MatterCollisionEvents);
Plugin.use(MatterLib, MatterCollisionEvents);
return this;
},
/**
* Pauses the Matter World instance and sets `enabled` to `false`.
*
* A paused world will not run any simulations for the duration it is paused.
*
* @method Phaser.Physics.Matter.MatterPhysics#pause
* @fires Phaser.Physics.Matter.Events#PAUSE
* @since 3.0.0
*
* @return {Phaser.Physics.Matter.World} The Matter World object.
*/
pause: function ()
{
return this.world.pause();
},
/**
* Resumes this Matter World instance from a paused state and sets `enabled` to `true`.
*
* @method Phaser.Physics.Matter.MatterPhysics#resume
* @since 3.0.0
*
* @return {Phaser.Physics.Matter.World} The Matter World object.
*/
resume: function ()
{
return this.world.resume();
},
/**
* Sets the Matter Engine to run at fixed timestep of 60Hz and enables `autoUpdate`.
* If you have set a custom `getDelta` function then this will override it.
*
* @method Phaser.Physics.Matter.MatterPhysics#set60Hz
* @since 3.4.0
*
* @return {this} This Matter Physics instance.
*/
set60Hz: function ()
{
this.world.getDelta = this.world.update60Hz;
this.world.autoUpdate = true;
return this;
},
/**
* Sets the Matter Engine to run at fixed timestep of 30Hz and enables `autoUpdate`.
* If you have set a custom `getDelta` function then this will override it.
*
* @method Phaser.Physics.Matter.MatterPhysics#set30Hz
* @since 3.4.0
*
* @return {this} This Matter Physics instance.
*/
set30Hz: function ()
{
this.world.getDelta = this.world.update30Hz;
this.world.autoUpdate = true;
return this;
},
/**
* Manually advances the physics simulation by one iteration.
*
* You can optionally pass in the `delta` and `correction` values to be used by Engine.update.
* If undefined they use the Matter defaults of 60Hz and no correction.
*
* Calling `step` directly bypasses any checks of `enabled` or `autoUpdate`.
*
* It also ignores any custom `getDelta` functions, as you should be passing the delta
* value in to this call.
*
* You can adjust the number of iterations that Engine.update performs internally.
* Use the Scene Matter Physics config object to set the following properties:
*
* positionIterations (defaults to 6)
* velocityIterations (defaults to 4)
* constraintIterations (defaults to 2)
*
* Adjusting these values can help performance in certain situations, depending on the physics requirements
* of your game.
*
* @method Phaser.Physics.Matter.MatterPhysics#step
* @since 3.4.0
*
* @param {number} [delta=16.666] - The delta value.
* @param {number} [correction=1] - Optional delta correction value.
*/
step: function (delta, correction)
{
this.world.step(delta, correction);
},
/**
* Checks if the vertices of the given body, or an array of bodies, contains the given point, or not.
*
* You can pass in either a single body, or an array of bodies to be checked. This method will
* return `true` if _any_ of the bodies in the array contain the point. See the `intersectPoint` method if you need
* to get a list of intersecting bodies.
*
* The point should be transformed into the Matter World coordinate system in advance. This happens by
* default with Input Pointers, but if you wish to use points from another system you may need to
* transform them before passing them.
*
* @method Phaser.Physics.Matter.MatterPhysics#containsPoint
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} body - The body, or an array of bodies, to check against the point.
* @param {number} x - The horizontal coordinate of the point.
* @param {number} y - The vertical coordinate of the point.
*
* @return {boolean} `true` if the point is within one of the bodies given, otherwise `false`.
*/
containsPoint: function (body, x, y)
{
body = this.getMatterBodies(body);
var position = Vector.create(x, y);
var result = Query.point(body, position);
return (result.length > 0) ? true : false;
},
/**
* Checks the given coordinates to see if any vertices of the given bodies contain it.
*
* If no bodies are provided it will search all bodies in the Matter World, including within Composites.
*
* The coordinates should be transformed into the Matter World coordinate system in advance. This happens by
* default with Input Pointers, but if you wish to use coordinates from another system you may need to
* transform them before passing them.
*
* @method Phaser.Physics.Matter.MatterPhysics#intersectPoint
* @since 3.22.0
*
* @param {number} x - The horizontal coordinate of the point.
* @param {number} y - The vertical coordinate of the point.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world.
*
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies which contain the given point.
*/
intersectPoint: function (x, y, bodies)
{
bodies = this.getMatterBodies(bodies);
var position = Vector.create(x, y);
var output = [];
var result = Query.point(bodies, position);
result.forEach(function (body)
{
if (output.indexOf(body) === -1)
{
output.push(body);
}
});
return output;
},
/**
* Checks the given rectangular area to see if any vertices of the given bodies intersect with it.
* Or, if the `outside` parameter is set to `true`, it checks to see which bodies do not
* intersect with it.
*
* If no bodies are provided it will search all bodies in the Matter World, including within Composites.
*
* @method Phaser.Physics.Matter.MatterPhysics#intersectRect
* @since 3.22.0
*
* @param {number} x - The horizontal coordinate of the top-left of the area.
* @param {number} y - The vertical coordinate of the top-left of the area.
* @param {number} width - The width of the area.
* @param {number} height - The height of the area.
* @param {boolean} [outside=false] - If `false` it checks for vertices inside the area, if `true` it checks for vertices outside the area.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world.
*
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies that intersect with the given area.
*/
intersectRect: function (x, y, width, height, outside, bodies)
{
if (outside === undefined) { outside = false; }
bodies = this.getMatterBodies(bodies);
var bounds = {
min: { x: x, y: y },
max: { x: x + width, y: y + height }
};
var output = [];
var result = Query.region(bodies, bounds, outside);
result.forEach(function (body)
{
if (output.indexOf(body) === -1)
{
output.push(body);
}
});
return output;
},
/**
* Checks the given ray segment to see if any vertices of the given bodies intersect with it.
*
* If no bodies are provided it will search all bodies in the Matter World.
*
* The width of the ray can be specified via the `rayWidth` parameter.
*
* @method Phaser.Physics.Matter.MatterPhysics#intersectRay
* @since 3.22.0
*
* @param {number} x1 - The horizontal coordinate of the start of the ray segment.
* @param {number} y1 - The vertical coordinate of the start of the ray segment.
* @param {number} x2 - The horizontal coordinate of the end of the ray segment.
* @param {number} y2 - The vertical coordinate of the end of the ray segment.
* @param {number} [rayWidth=1] - The width of the ray segment.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check. If not provided it will search all bodies in the world.
*
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies whos vertices intersect with the ray segment.
*/
intersectRay: function (x1, y1, x2, y2, rayWidth, bodies)
{
if (rayWidth === undefined) { rayWidth = 1; }
bodies = this.getMatterBodies(bodies);
var result = [];
var collisions = Query.ray(bodies, Vector.create(x1, y1), Vector.create(x2, y2), rayWidth);
for (var i = 0; i < collisions.length; i++)
{
result.push(collisions[i].body);
}
return result;
},
/**
* Checks the given Matter Body to see if it intersects with any of the given bodies.
*
* If no bodies are provided it will check against all bodies in the Matter World.
*
* @method Phaser.Physics.Matter.MatterPhysics#intersectBody
* @since 3.22.0
*
* @param {Phaser.Types.Physics.Matter.MatterBody} body - The target body.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - An array of bodies to check the target body against. If not provided it will search all bodies in the world.
*
* @return {Phaser.Types.Physics.Matter.MatterBody[]} An array of bodies whos vertices intersect with target body.
*/
intersectBody: function (body, bodies)
{
bodies = this.getMatterBodies(bodies);
var result = [];
var collisions = Query.collides(body, bodies);
for (var i = 0; i < collisions.length; i++)
{
var pair = collisions[i];
if (pair.bodyA === body)
{
result.push(pair.bodyB);
}
else
{
result.push(pair.bodyA);
}
}
return result;
},
/**
* Checks to see if the target body, or an array of target bodies, intersects with any of the given bodies.
*
* If intersection occurs this method will return `true` and, if provided, invoke the callbacks.
*
* If no bodies are provided for the second parameter the target will check again all bodies in the Matter World.
*
* Note that bodies can only overlap if they are in non-colliding collision groups or categories.
*
* If you provide a `processCallback` then the two bodies that overlap are sent to it. This callback
* must return a boolean and is used to allow you to perform additional processing tests before a final
* outcome is decided. If it returns `true` then the bodies are finally passed to the `overlapCallback`, if set.
*
* If you provide an `overlapCallback` then the matching pairs of overlapping bodies will be sent to it.
*
* Both callbacks have the following signature: `function (bodyA, bodyB, collisionInfo)` where `bodyA` is always
* the target body. The `collisionInfo` object contains additional data, such as the angle and depth of penetration.
*
* @method Phaser.Physics.Matter.MatterPhysics#overlap
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} target - The target body, or array of target bodies, to check.
* @param {Phaser.Types.Physics.Matter.MatterBody[]} [bodies] - The second body, or array of bodies, to check. If falsey it will check against all bodies in the world.
* @param {ArcadePhysicsCallback} [overlapCallback] - An optional callback function that is called if the bodies overlap.
* @param {ArcadePhysicsCallback} [processCallback] - An optional callback function that lets you perform additional checks against the two bodies if they overlap. If this is set then `overlapCallback` will only be invoked if this callback returns `true`.
* @param {*} [callbackContext] - The context, or scope, in which to run the callbacks.
*
* @return {boolean} `true` if the target body intersects with _any_ of the bodies given, otherwise `false`.
*/
overlap: function (target, bodies, overlapCallback, processCallback, callbackContext)
{
if (overlapCallback === undefined) { overlapCallback = null; }
if (processCallback === undefined) { processCallback = null; }
if (callbackContext === undefined) { callbackContext = overlapCallback; }
if (!Array.isArray(target))
{
target = [ target ];
}
target = this.getMatterBodies(target);
bodies = this.getMatterBodies(bodies);
var match = false;
for (var i = 0; i < target.length; i++)
{
var entry = target[i];
var collisions = Query.collides(entry, bodies);
for (var c = 0; c < collisions.length; c++)
{
var info = collisions[c];
var bodyB = (info.bodyA.id === entry.id) ? info.bodyB : info.bodyA;
if (!processCallback || processCallback.call(callbackContext, entry, bodyB, info))
{
match = true;
if (overlapCallback)
{
overlapCallback.call(callbackContext, entry, bodyB, info);
}
else if (!processCallback)
{
// If there are no callbacks we don't need to test every body, just exit when the first is found
return true;
}
}
}
}
return match;
},
/**
* Sets the collision filter category of all given Matter Bodies to the given value.
*
* This number must be a power of two between 2^0 (= 1) and 2^31.
*
* Bodies with different collision groups (see {@link #setCollisionGroup}) will only collide if their collision
* categories are included in their collision masks (see {@link #setCollidesWith}).
*
* @method Phaser.Physics.Matter.MatterPhysics#setCollisionCategory
* @since 3.22.0
*
* @param {Phaser.Types.Physics.Matter.MatterBody[]} bodies - An array of bodies to update. If falsey it will use all bodies in the world.
* @param {number} value - Unique category bitfield.
*
* @return {this} This Matter Physics instance.
*/
setCollisionCategory: function (bodies, value)
{
bodies = this.getMatterBodies(bodies);
bodies.forEach(function (body)
{
body.collisionFilter.category = value;
});
return this;
},
/**
* Sets the collision filter group of all given Matter Bodies to the given value.
*
* If the group value is zero, or if two Matter Bodies have different group values,
* they will collide according to the usual collision filter rules (see {@link #setCollisionCategory} and {@link #setCollisionGroup}).
*
* If two Matter Bodies have the same positive group value, they will always collide;
* if they have the same negative group value they will never collide.
*
* @method Phaser.Physics.Matter.MatterPhysics#setCollisionGroup
* @since 3.22.0
*
* @param {Phaser.Types.Physics.Matter.MatterBody[]} bodies - An array of bodies to update. If falsey it will use all bodies in the world.
* @param {number} value - Unique group index.
*
* @return {this} This Matter Physics instance.
*/
setCollisionGroup: function (bodies, value)
{
bodies = this.getMatterBodies(bodies);
bodies.forEach(function (body)
{
body.collisionFilter.group = value;
});
return this;
},
/**
* Sets the collision filter mask of all given Matter Bodies to the given value.
*
* Two Matter Bodies with different collision groups will only collide if each one includes the others
* category in its mask based on a bitwise AND operation: `(categoryA & maskB) !== 0` and
* `(categoryB & maskA) !== 0` are both true.
*
* @method Phaser.Physics.Matter.MatterPhysics#setCollidesWith
* @since 3.22.0
*
* @param {Phaser.Types.Physics.Matter.MatterBody[]} bodies - An array of bodies to update. If falsey it will use all bodies in the world.
* @param {(number|number[])} categories - A unique category bitfield, or an array of them.
*
* @return {this} This Matter Physics instance.
*/
setCollidesWith: function (bodies, categories)
{
bodies = this.getMatterBodies(bodies);
var flags = 0;
if (!Array.isArray(categories))
{
flags = categories;
}
else
{
for (var i = 0; i < categories.length; i++)
{
flags |= categories[i];
}
}
bodies.forEach(function (body)
{
body.collisionFilter.mask = flags;
});
return this;
},
/**
* Takes an array and returns a new array made from all of the Matter Bodies found in the original array.
*
* For example, passing in Matter Game Objects, such as a bunch of Matter Sprites, to this method, would
* return an array containing all of their native Matter Body objects.
*
* If the `bodies` argument is falsey, it will return all bodies in the world.
*
* @method Phaser.Physics.Matter.MatterPhysics#getMatterBodies
* @since 3.22.0
*
* @param {array} [bodies] - An array of objects to extract the bodies from. If falsey, it will return all bodies in the world.
*
* @return {MatterJS.BodyType[]} An array of native Matter Body objects.
*/
getMatterBodies: function (bodies)
{
if (!bodies)
{
return this.world.getAllBodies();
}
if (!Array.isArray(bodies))
{
bodies = [ bodies ];
}
var output = [];
for (var i = 0; i < bodies.length; i++)
{
var body = (bodies[i].hasOwnProperty('body')) ? bodies[i].body : bodies[i];
output.push(body);
}
return output;
},
/**
* Sets both the horizontal and vertical linear velocity of the physics bodies.
*
* @method Phaser.Physics.Matter.MatterPhysics#setVelocity
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} bodies - Either a single Body, or an array of bodies to update. If falsey it will use all bodies in the world.
* @param {number} x - The horizontal linear velocity value.
* @param {number} y - The vertical linear velocity value.
*
* @return {this} This Matter Physics instance.
*/
setVelocity: function (bodies, x, y)
{
bodies = this.getMatterBodies(bodies);
var vec2 = this._tempVec2;
vec2.x = x;
vec2.y = y;
bodies.forEach(function (body)
{
Body.setVelocity(body, vec2);
});
return this;
},
/**
* Sets just the horizontal linear velocity of the physics bodies.
* The vertical velocity of the body is unchanged.
*
* @method Phaser.Physics.Matter.MatterPhysics#setVelocityX
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} bodies - Either a single Body, or an array of bodies to update. If falsey it will use all bodies in the world.
* @param {number} x - The horizontal linear velocity value.
*
* @return {this} This Matter Physics instance.
*/
setVelocityX: function (bodies, x)
{
bodies = this.getMatterBodies(bodies);
var vec2 = this._tempVec2;
vec2.x = x;
bodies.forEach(function (body)
{
vec2.y = body.velocity.y;
Body.setVelocity(body, vec2);
});
return this;
},
/**
* Sets just the vertical linear velocity of the physics bodies.
* The horizontal velocity of the body is unchanged.
*
* @method Phaser.Physics.Matter.MatterPhysics#setVelocityY
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} bodies - Either a single Body, or an array of bodies to update. If falsey it will use all bodies in the world.
* @param {number} y - The vertical linear velocity value.
*
* @return {this} This Matter Physics instance.
*/
setVelocityY: function (bodies, y)
{
bodies = this.getMatterBodies(bodies);
var vec2 = this._tempVec2;
vec2.y = y;
bodies.forEach(function (body)
{
vec2.x = body.velocity.x;
Body.setVelocity(body, vec2);
});
return this;
},
/**
* Sets the angular velocity of the bodies instantly.
* Position, angle, force etc. are unchanged.
*
* @method Phaser.Physics.Matter.MatterPhysics#setAngularVelocity
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} bodies - Either a single Body, or an array of bodies to update. If falsey it will use all bodies in the world.
* @param {number} value - The angular velocity.
*
* @return {this} This Matter Physics instance.
*/
setAngularVelocity: function (bodies, value)
{
bodies = this.getMatterBodies(bodies);
bodies.forEach(function (body)
{
Body.setAngularVelocity(body, value);
});
return this;
},
/**
* Applies a force to a body, at the bodies current position, including resulting torque.
*
* @method Phaser.Physics.Matter.MatterPhysics#applyForce
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} bodies - Either a single Body, or an array of bodies to update. If falsey it will use all bodies in the world.
* @param {Phaser.Types.Math.Vector2Like} force - A Vector that specifies the force to apply.
*
* @return {this} This Matter Physics instance.
*/
applyForce: function (bodies, force)
{
bodies = this.getMatterBodies(bodies);
var vec2 = this._tempVec2;
bodies.forEach(function (body)
{
vec2.x = body.position.x;
vec2.y = body.position.y;
Body.applyForce(body, vec2, force);
});
return this;
},
/**
* Applies a force to a body, from the given world position, including resulting torque.
* If no angle is given, the current body angle is used.
*
* Use very small speed values, such as 0.1, depending on the mass and required velocity.
*
* @method Phaser.Physics.Matter.MatterPhysics#applyForceFromPosition
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} bodies - Either a single Body, or an array of bodies to update. If falsey it will use all bodies in the world.
* @param {Phaser.Types.Math.Vector2Like} position - A Vector that specifies the world-space position to apply the force at.
* @param {number} speed - A speed value to be applied to a directional force.
* @param {number} [angle] - The angle, in radians, to apply the force from. Leave undefined to use the current body angle.
*
* @return {this} This Matter Physics instance.
*/
applyForceFromPosition: function (bodies, position, speed, angle)
{
bodies = this.getMatterBodies(bodies);
var vec2 = this._tempVec2;
bodies.forEach(function (body)
{
if (angle === undefined)
{
angle = body.angle;
}
vec2.x = speed * Math.cos(angle);
vec2.y = speed * Math.sin(angle);
Body.applyForce(body, position, vec2);
});
return this;
},
/**
* Apply a force to a body based on the given angle and speed.
* If no angle is given, the current body angle is used.
*
* Use very small speed values, such as 0.1, depending on the mass and required velocity.
*
* @method Phaser.Physics.Matter.MatterPhysics#applyForceFromAngle
* @since 3.22.0
*
* @param {(Phaser.Types.Physics.Matter.MatterBody|Phaser.Types.Physics.Matter.MatterBody[])} bodies - Either a single Body, or an array of bodies to update. If falsey it will use all bodies in the world.
* @param {number} speed - A speed value to be applied to a directional force.
* @param {number} [angle] - The angle, in radians, to apply the force from. Leave undefined to use the current body angle.
*
* @return {this} This Matter Physics instance.
*/
applyForceFromAngle: function (bodies, speed, angle)
{
bodies = this.getMatterBodies(bodies);
var vec2 = this._tempVec2;
bodies.forEach(function (body)
{
if (angle === undefined)
{
angle = body.angle;
}
vec2.x = speed * Math.cos(angle);
vec2.y = speed * Math.sin(angle);
Body.applyForce(body, { x: body.position.x, y: body.position.y }, vec2);
});
return this;
},
/**
* Returns the length of the given constraint, which is the distance between the two points.
*
* @method Phaser.Physics.Matter.MatterPhysics#getConstraintLength
* @since 3.22.0
*
* @param {MatterJS.ConstraintType} constraint - The constraint to get the length from.
*
* @return {number} The length of the constraint.
*/
getConstraintLength: function (constraint)
{
var aX = constraint.pointA.x;
var aY = constraint.pointA.y;
var bX = constraint.pointB.x;
var bY = constraint.pointB.y;
if (constraint.bodyA)
{
aX += constraint.bodyA.position.x;
aY += constraint.bodyA.position.y;
}
if (constraint.bodyB)
{
bX += constraint.bodyB.position.x;
bY += constraint.bodyB.position.y;
}
return DistanceBetween(aX, aY, bX, bY);
},
/**
* Aligns a Body, or Matter Game Object, against the given coordinates.
*
* The alignment takes place using the body bounds, which take into consideration things
* like body scale and rotation.
*
* Although a Body has a `position` property, it is based on the center of mass for the body,
* not a dimension based center. This makes aligning bodies difficult, especially if they have
* rotated or scaled. This method will derive the correct position based on the body bounds and
* its center of mass offset, in order to align the body with the given coordinate.
*
* For example, if you wanted to align a body so it sat in the bottom-center of the
* Scene, and the world was 800 x 600 in size:
*
* ```javascript
* this.matter.alignBody(body, 400, 600, Phaser.Display.Align.BOTTOM_CENTER);
* ```
*
* You pass in 400 for the x coordinate, because that is the center of the world, and 600 for
* the y coordinate, as that is the base of the world.
*
* @method Phaser.Physics.Matter.MatterPhysics#alignBody
* @since 3.22.0
*
* @param {Phaser.Types.Physics.Matter.MatterBody} body - The Body to align.
* @param {number} x - The horizontal position to align the body to.
* @param {number} y - The vertical position to align the body to.
* @param {number} align - One of the `Phaser.Display.Align` constants, such as `Phaser.Display.Align.TOP_LEFT`.
*
* @return {this} This Matter Physics instance.
*/
alignBody: function (body, x, y, align)
{
body = (body.hasOwnProperty('body')) ? body.body : body;
var pos;
switch (align)
{
case ALIGN_CONST.TOP_LEFT:
case ALIGN_CONST.LEFT_TOP:
pos = this.bodyBounds.getTopLeft(body, x, y);
break;
case ALIGN_CONST.TOP_CENTER:
pos = this.bodyBounds.getTopCenter(body, x, y);
break;
case ALIGN_CONST.TOP_RIGHT:
case ALIGN_CONST.RIGHT_TOP:
pos = this.bodyBounds.getTopRight(body, x, y);
break;
case ALIGN_CONST.LEFT_CENTER:
pos = this.bodyBounds.getLeftCenter(body, x, y);
break;
case ALIGN_CONST.CENTER:
pos = this.bodyBounds.getCenter(body, x, y);
break;
case ALIGN_CONST.RIGHT_CENTER:
pos = this.bodyBounds.getRightCenter(body, x, y);
break;
case ALIGN_CONST.LEFT_BOTTOM:
case ALIGN_CONST.BOTTOM_LEFT:
pos = this.bodyBounds.getBottomLeft(body, x, y);
break;
case ALIGN_CONST.BOTTOM_CENTER:
pos = this.bodyBounds.getBottomCenter(body, x, y);
break;
case ALIGN_CONST.BOTTOM_RIGHT:
case ALIGN_CONST.RIGHT_BOTTOM:
pos = this.bodyBounds.getBottomRight(body, x, y);
break;
}
if (pos)
{
Body.setPosition(body, pos);
}
return this;
},
/**
* The Scene that owns this plugin is shutting down.
* We need to kill and reset all internal properties as well as stop listening to Scene events.
*
* @method Phaser.Physics.Matter.MatterPhysics#shutdown
* @private
* @since 3.0.0
*/
shutdown: function ()
{
var eventEmitter = this.systems.events;
if (this.world)
{
eventEmitter.off(SceneEvents.UPDATE, this.world.update, this.world);
eventEmitter.off(SceneEvents.POST_UPDATE, this.world.postUpdate, this.world);
}
eventEmitter.off(SceneEvents.SHUTDOWN, this.shutdown, this);
if (this.add)
{
this.add.destroy();
}
if (this.world)
{
this.world.destroy();
}
this.add = null;
this.world = null;
},
/**
* The Scene that owns this plugin is being destroyed.
* We need to shutdown and then kill off all external references.
*
* @method Phaser.Physics.Matter.MatterPhysics#destroy
* @private
* @since 3.0.0
*/
destroy: function ()
{
this.shutdown();
this.scene.sys.events.off(SceneEvents.START, this.start, this);
this.scene = null;
this.systems = null;
}
});
PluginCache.register('MatterPhysics', MatterPhysics, 'matterPhysics');
module.exports = MatterPhysics;