From 348da8c81ee999816e1169bbd257bf82c7d9fd0f Mon Sep 17 00:00:00 2001 From: Michael Hadley Date: Sun, 21 Jan 2018 12:53:27 -0600 Subject: [PATCH] MatterTileBody: wrapper around a Tile that provides access to a matter body --- src/gameobjects/tilemap/Tile.js | 6 + src/physics/matter-js/MatterTileBody.js | 188 ++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 src/physics/matter-js/MatterTileBody.js diff --git a/src/gameobjects/tilemap/Tile.js b/src/gameobjects/tilemap/Tile.js index c8572302f..4078e2f15 100644 --- a/src/gameobjects/tilemap/Tile.js +++ b/src/gameobjects/tilemap/Tile.js @@ -182,6 +182,12 @@ var Tile = new Class({ * @default */ this.tint = 0xffffff; + + /** + * An empty object where physics-engine specific information (e.g. bodies) may be stored. + * @property {object} physics + */ + this.physics = {}; }, /** diff --git a/src/physics/matter-js/MatterTileBody.js b/src/physics/matter-js/MatterTileBody.js new file mode 100644 index 000000000..ae9360edb --- /dev/null +++ b/src/physics/matter-js/MatterTileBody.js @@ -0,0 +1,188 @@ +var AnimationComponent = require('../../gameobjects/components/Animation'); +var Bodies = require('./lib/factory/Bodies'); +var Class = require('../../utils/Class'); +var Components = require('./components'); +var GetFastValue = require('../../utils/object/GetFastValue'); +var HasValue = require('../../utils/object/HasValue'); +var Extend = require('../../utils/object/Extend'); +var Body = require('./lib/body/Body'); +var Vertices = require('./lib/geometry/Vertices'); + +var MatterTileBody = new Class({ + + Mixins: [ + Components.Bounce, + Components.Collision, + Components.Friction, + Components.Gravity, + Components.Mass, + Components.Sensor, + Components.Sleep, + Components.Static + ], + + initialize: + + function MatterTileBody (world, tile, options) + { + this.tile = tile; + this.world = world; + + // A tile is only allowed one matter body + if (tile.physics.matterBody) + { + tile.physics.matterBody.destroy(); + } + tile.physics.matterBody = this; + + // Set the body either from an existing body (if provided), the shapes in the tileset + // collision layer (if it exists) or a rectangle matching the tile. + var body = GetFastValue(options, 'body', null); + var addToWorld = GetFastValue(options, 'addToWorld', true); + if (!body) + { + var tileset = tile.layer.tilemapLayer.tileset; + var collisionGroup = tileset.getTileCollisionGroup(tile.index); + var collisionObjects = GetFastValue(collisionGroup, 'objects', []); + if (collisionObjects.length > 0) + { + this.setFromTileCollision(options); + } + else + { + this.setFromTileRectangle(options); + } + } + else + { + this.setBody(body, addToWorld); + } + }, + + setFromTileRectangle: function (options) + { + if (options === undefined) { options = {}; } + if (!HasValue(options, "isStatic")) { options.isStatic = true; } + if (!HasValue(options, "addToWorld")) { options.addToWorld = true; } + + var tile = this.tile; + var tilemapLayer = tile.layer.tilemapLayer; + var tileset = tilemapLayer.tileset; + var w = tile.width * tilemapLayer.scaleX; + var h = tile.height * tilemapLayer.scaleY; + var x = tilemapLayer.tileToWorldX(tile.x); + var y = tilemapLayer.tileToWorldY(tile.y); + var body = Bodies.rectangle(x + (w / 2), y + (h / 2), w, h, options); + this.setBody(body, options.addToWorld); + + return this; + }, + + setFromTileCollision: function (options) + { + if (options === undefined) { options = {}; } + if (!HasValue(options, "isStatic")) { options.isStatic = true; } + if (!HasValue(options, "addToWorld")) { options.addToWorld = true; } + + var tile = this.tile; + var tilemapLayer = tile.layer.tilemapLayer; + var tileset = tilemapLayer.tileset; + var w = tile.width * tilemapLayer.scaleX; + var h = tile.height * tilemapLayer.scaleY; + var x = tilemapLayer.tileToWorldX(tile.x); + var y = tilemapLayer.tileToWorldY(tile.y); + + var collisionGroup = tileset.getTileCollisionGroup(tile.index); + var collisionObjects = GetFastValue(collisionGroup, 'objects', []); + + var parts = []; + for (var i = 0; i < collisionObjects.length; i++) + { + var object = collisionObjects[i]; + var ox = x + object.x; + var oy = y + object.y; + var ow = object.width; + var oh = object.height; + var body = null; + + if (object.rectangle) + { + body = Bodies.rectangle(ox + ow / 2, oy + oh / 2, ow, oh, options); + } + else if (object.ellipse) + { + body = Bodies.circle(ox + ow / 2, oy + oh / 2, ow / 2, options); + } + else if (object.polygon || object.polyline) + { + // Polygons and polylines are both treated as closed polygons. Concave shapes are + // supported if poly-decomp library is included, but it's best to manually create + // convex polygons. + var originalPoints = object.polygon ? object.polygon : object.polyline; + var points = originalPoints.map(function (p) { + return { x: p[0], y: p[1] }; + }); + var vertices = Vertices.create(points); + + // Translate from object position to center of mass + var center = Vertices.centre(vertices); + body = Bodies.fromVertices(ox + center.x, oy + center.y, vertices, options); + } + + if (body) + { + parts.push(body); + } + } + + if (parts.length === 1) + { + this.setBody(parts[0], options.addToWorld); + } + else if (parts.length > 1) + { + options.parts = parts; + this.setBody(Body.create(options), options.addToWorld); + } + + return this; + }, + + setBody: function (body, addToWorld) + { + if (addToWorld === undefined) { addToWorld = true; } + + if (this.body) + { + this.removeBody(); + } + + this.body = body; + this.body.gameObject = this; + + if (addToWorld) + { + this.world.add(this.body); + } + + return this; + }, + + removeBody: function () + { + if (this.body) + { + this.world.remove(this.body); + this.body.gameObject = undefined; + this.body = undefined; + } + }, + + destroy: function () + { + this.removeBody(); + tile.physics.matterBody = undefined; + } +}); + +module.exports = MatterTileBody;