From 8ec6528d20a87fe96d6326daf9fc84ab87a5c0bb Mon Sep 17 00:00:00 2001 From: Michael Hadley Date: Sun, 26 Nov 2017 07:55:44 -0600 Subject: [PATCH] Tilemap & layers: replace v2 raycasting with getTilesWithinShape --- v3/src/gameobjects/tilemap/Tilemap.js | 7 ++ .../tilemap/components/GetTilesWithinShape.js | 68 +++++++++++++++++++ .../tilemap/components/TileToWorldX.js | 19 ++++++ .../tilemap/components/TileToWorldY.js | 19 ++++++ .../gameobjects/tilemap/components/index.js | 1 + .../dynamiclayer/DynamicTilemapLayer.js | 5 ++ .../tilemap/staticlayer/StaticTilemapLayer.js | 5 ++ 7 files changed, 124 insertions(+) create mode 100644 v3/src/gameobjects/tilemap/components/GetTilesWithinShape.js create mode 100644 v3/src/gameobjects/tilemap/components/TileToWorldX.js create mode 100644 v3/src/gameobjects/tilemap/components/TileToWorldY.js diff --git a/v3/src/gameobjects/tilemap/Tilemap.js b/v3/src/gameobjects/tilemap/Tilemap.js index 7f976ae96..9b227cefd 100644 --- a/v3/src/gameobjects/tilemap/Tilemap.js +++ b/v3/src/gameobjects/tilemap/Tilemap.js @@ -310,6 +310,13 @@ var Tilemap = new Class({ return TilemapComponents.GetTilesWithin(tileX, tileY, width, height, filteringOptions, layer); }, + getTilesWithinShape: function (shape, filteringOptions, camera, layer) + { + layer = this.getLayer(layer); + if (layer === null) { return null; } + return TilemapComponents.GetTilesWithinShape(shape, filteringOptions, camera, layer); + }, + getTilesWithinWorldXY: function (worldX, worldY, width, height, filteringOptions, camera, layer) { layer = this.getLayer(layer); diff --git a/v3/src/gameobjects/tilemap/components/GetTilesWithinShape.js b/v3/src/gameobjects/tilemap/components/GetTilesWithinShape.js new file mode 100644 index 000000000..d04673d75 --- /dev/null +++ b/v3/src/gameobjects/tilemap/components/GetTilesWithinShape.js @@ -0,0 +1,68 @@ + +var GetTilesWithin = require('./GetTilesWithin'); +var WorldToTileX = require('./WorldToTileX'); +var WorldToTileY = require('./WorldToTileY'); +var TileToWorldX = require('./TileToWorldX'); +var TileToWorldY = require('./TileToWorldY'); +var Geom = require('../../../geom/'); +var Intersects = require('../../../geom/intersects/'); +var NOOP = require('../../../utils/NOOP'); + +var TriangleToRectangle = function (triangle, rect) +{ + return Intersects.RectangleToTriangle(rect, triangle); +}; + +// Circle, Line, Rect, Triangle in world coordinates. +// Notes: circle is not working yet - see CircleToRectangle in geom. Could possibly be optimized +// by copying the shape and shifting it into tilemapLayer coordinates instead of shifting the tiles. +var GetTilesWithinShape = function (shape, filteringOptions, camera, layer) +{ + if (shape === undefined) { return []; } + + // intersectTest is a function with parameters: shape, rect + var intersectTest = NOOP; + if (shape instanceof Geom.Circle) { intersectTest = Intersects.CircleToRectangle; } + else if (shape instanceof Geom.Rectangle) { intersectTest = Intersects.RectangleToRectangle; } + else if (shape instanceof Geom.Triangle) { intersectTest = TriangleToRectangle; } + else if (shape instanceof Geom.Line) { intersectTest = Intersects.LineToRectangle; } + + // Top left corner of the shapes's bounding box, rounded down to include partial tiles + var xStart = WorldToTileX(shape.left, true, camera, layer); + var yStart = WorldToTileY(shape.top, true, camera, layer); + + // Bottom right corner of the shapes's bounding box, rounded up to include partial tiles + var xEnd = Math.ceil(WorldToTileX(shape.right, false, camera, layer)); + var yEnd = Math.ceil(WorldToTileY(shape.bottom, false, camera, layer)); + + // Tiles within bounding rectangle of shape. Bounds are forced to be at least 1 x 1 tile in size + // to grab tiles for shapes that don't have a height or width (e.g. a horizontal line). + var width = Math.max(xEnd - xStart, 1); + var height = Math.max(yEnd - yStart, 1); + var tiles = GetTilesWithin(xStart, yStart, width, height, filteringOptions, layer); + + var tileWidth = layer.tileWidth; + var tileHeight = layer.tileHeight; + if (layer.tilemapLayer) + { + tileWidth *= layer.tilemapLayer.scaleX; + tileHeight *= layer.tilemapLayer.scaleY; + } + + var results = []; + var tileRect = new Geom.Rectangle(0, 0, tileWidth, tileHeight); + for (var i = 0; i < tiles.length; i++) + { + var tile = tiles[i]; + tileRect.x = TileToWorldX(tile.x, camera, layer); + tileRect.y = TileToWorldY(tile.y, camera, layer); + if (intersectTest(shape, tileRect)) + { + results.push(tile); + } + } + + return results; +}; + +module.exports = GetTilesWithinShape; diff --git a/v3/src/gameobjects/tilemap/components/TileToWorldX.js b/v3/src/gameobjects/tilemap/components/TileToWorldX.js new file mode 100644 index 000000000..6710dd21e --- /dev/null +++ b/v3/src/gameobjects/tilemap/components/TileToWorldX.js @@ -0,0 +1,19 @@ +var TileToWorldX = function (tileX, camera, layer) +{ + var tileWidth = layer.tileWidth; + var tilemapLayer = layer.tilemapLayer; + var layerWorldX = 0; + + if (tilemapLayer) + { + if (camera === undefined) { camera = tilemapLayer.scene.cameras.main; } + + layerWorldX = tilemapLayer.x - (camera.scrollX * tilemapLayer.scrollFactorX); + + tileWidth *= tilemapLayer.scaleX; + } + + return layerWorldX + tileX * tileWidth; +}; + +module.exports = TileToWorldX; diff --git a/v3/src/gameobjects/tilemap/components/TileToWorldY.js b/v3/src/gameobjects/tilemap/components/TileToWorldY.js new file mode 100644 index 000000000..ddb3d443c --- /dev/null +++ b/v3/src/gameobjects/tilemap/components/TileToWorldY.js @@ -0,0 +1,19 @@ +var TileToWorldY = function (tileY, camera, layer) +{ + var tileHeight = layer.tileHeight; + var tilemapLayer = layer.tilemapLayer; + var layerWorldY = 0; + + if (tilemapLayer) + { + if (camera === undefined) { camera = tilemapLayer.scene.cameras.main; } + + layerWorldY = tilemapLayer.y - (camera.scrollY * tilemapLayer.scrollFactorY); + + tileHeight *= tilemapLayer.scaleY; + } + + return layerWorldY + tileY * tileHeight; +}; + +module.exports = TileToWorldY; diff --git a/v3/src/gameobjects/tilemap/components/index.js b/v3/src/gameobjects/tilemap/components/index.js index 686dda971..76044beb0 100644 --- a/v3/src/gameobjects/tilemap/components/index.js +++ b/v3/src/gameobjects/tilemap/components/index.js @@ -8,6 +8,7 @@ module.exports = { GetTileAt: require('./GetTileAt'), GetTileAtWorldXY: require('./GetTileAtWorldXY'), GetTilesWithin: require('./GetTilesWithin'), + GetTilesWithinShape: require('./GetTilesWithinShape'), GetTilesWithinWorldXY: require('./GetTilesWithinWorldXY'), HasTileAt: require('./HasTileAt'), HasTileAtWorldXY: require('./HasTileAtWorldXY'), diff --git a/v3/src/gameobjects/tilemap/dynamiclayer/DynamicTilemapLayer.js b/v3/src/gameobjects/tilemap/dynamiclayer/DynamicTilemapLayer.js index 31faef9e0..8c6af2d11 100644 --- a/v3/src/gameobjects/tilemap/dynamiclayer/DynamicTilemapLayer.js +++ b/v3/src/gameobjects/tilemap/dynamiclayer/DynamicTilemapLayer.js @@ -108,6 +108,11 @@ var DynamicTilemapLayer = new Class({ return TilemapComponents.GetTilesWithin(tileX, tileY, width, height, filteringOptions, this.layer); }, + getTilesWithinShape: function (shape, filteringOptions, camera) + { + return TilemapComponents.GetTilesWithinShape(shape, filteringOptions, camera, this.layer); + }, + getTilesWithinWorldXY: function (worldX, worldY, width, height, filteringOptions, camera) { return TilemapComponents.GetTilesWithinWorldXY(worldX, worldY, width, height, filteringOptions, camera, this.layer); diff --git a/v3/src/gameobjects/tilemap/staticlayer/StaticTilemapLayer.js b/v3/src/gameobjects/tilemap/staticlayer/StaticTilemapLayer.js index 0e0567886..c9528e76f 100644 --- a/v3/src/gameobjects/tilemap/staticlayer/StaticTilemapLayer.js +++ b/v3/src/gameobjects/tilemap/staticlayer/StaticTilemapLayer.js @@ -254,6 +254,11 @@ var StaticTilemapLayer = new Class({ return TilemapComponents.GetTilesWithinWorldXY(worldX, worldY, width, height, filteringOptions, camera, this.layer); }, + getTilesWithinShape: function (shape, filteringOptions, camera) + { + return TilemapComponents.GetTilesWithinShape(shape, filteringOptions, camera, this.layer); + }, + hasTileAt: function (tileX, tileY) { return TilemapComponents.HasTileAt(tileX, tileY, this.layer);