From d8f5832fa2b8f96abfb5b1b32ce72966fcd848b3 Mon Sep 17 00:00:00 2001 From: photonstorm Date: Mon, 3 Mar 2014 05:19:46 +0000 Subject: [PATCH] Completely empty Tilemaps can now be created. This allows for dynamic map generation at runtime. Loads of updates across most the Tilemap files. Not finished yet, still CSV loading to do and a multi-tileset issue to resolve, but it's a lot more flexible now. --- README.md | 1 + examples/tilemaps/paint tiles.js | 24 +++++- examples/wip/tilemap blank.js | 68 ++++++++++++++++ examples/wip/tween var.js | 39 +++++++++ src/gameobjects/GameObjectCreator.js | 7 +- src/gameobjects/GameObjectFactory.js | 7 +- src/physics/World.js | 5 +- src/tilemap/Tilemap.js | 85 ++++++++++++++++---- src/tilemap/TilemapLayer.js | 114 +++++++++++++++++++++++++-- src/tilemap/TilemapParser.js | 7 +- src/tilemap/Tileset.js | 114 +++++++++++++++------------ src/utils/Debug.js | 10 +++ 12 files changed, 396 insertions(+), 85 deletions(-) create mode 100644 examples/wip/tilemap blank.js create mode 100644 examples/wip/tween var.js diff --git a/README.md b/README.md index b72d4758..d3d6ad5f 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ Updates: * ScaleManager has 2 new events: ScaleManager.enterFullScreen and ScaleManager.leaveFullScreen, so you can respond to fullscreen changes directly. * RandomDataGenerator.integerInRange(min, max) now includes both `min` and `max` within its range (#501) * Tween no longer copies all the object properties into the `_valuesStart` object on creation. +* Completely empty Tilemaps can now be created. This allows for dynamic map generation at runtime. Bug Fixes: diff --git a/examples/tilemaps/paint tiles.js b/examples/tilemaps/paint tiles.js index fa2748de..2c06f6ba 100644 --- a/examples/tilemaps/paint tiles.js +++ b/examples/tilemaps/paint tiles.js @@ -13,6 +13,7 @@ var layer; var marker; var currentTile; +var cursors; function create() { @@ -30,6 +31,8 @@ function create() { marker.lineStyle(2, 0x000000, 1); marker.drawRect(0, 0, 32, 32); + cursors = game.input.keyboard.createCursorKeys(); + } function update() { @@ -52,11 +55,28 @@ function update() { } } + if (cursors.left.isDown) + { + game.camera.x -= 4; + } + else if (cursors.right.isDown) + { + game.camera.x += 4; + } + + if (cursors.up.isDown) + { + game.camera.y -= 4; + } + else if (cursors.down.isDown) + { + game.camera.y += 4; + } + } function render() { - game.debug.text('Left-click to paint. Shift + Left-click to select tile.', 32, 32, 'rgb(0,0,0)'); - game.debug.text('Tile: ' + map.getTile(layer.getTileX(marker.x), layer.getTileY(marker.y)), 32, 48, 'rgb(0,0,0)'); + game.debug.text('Left-click to paint. Shift + Left-click to select tile. Arrows to scroll.', 32, 32, '#efefef'); } diff --git a/examples/wip/tilemap blank.js b/examples/wip/tilemap blank.js new file mode 100644 index 00000000..a5a05deb --- /dev/null +++ b/examples/wip/tilemap blank.js @@ -0,0 +1,68 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); +// var game = new Phaser.Game(800, 600, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.image('ground_1x1', 'assets/tilemaps/tiles/ground_1x1.png'); + +} + +var map; +var tileset; +var layer; +var cursors; + +function create() { + + game.stage.backgroundColor = '#787878'; + + // Creates a blank tilemap + map = game.add.tilemap(); + + // Creates a layer and sets-up the map dimensions. + // In this case the map is 30x30 tiles in size and the tiles are 32x32 pixels in size. + map.create('level1', 30, 30, 32, 32); + + // Add a Tileset image to the map + map.addTilesetImage('ground_1x1'); + + map.putTile(4, 1, 1) + map.putTile(10, 2, 1) + map.putTile(10, 3, 1) + map.putTile(10, 4, 1) + + // Create a layer. This is where the map is rendered to. + layer = map.createLayer('level1'); + + // layer.resizeWorld(); + // map.setCollisionBetween(1, 12); + // layer.debug = true; + + cursors = game.input.keyboard.createCursorKeys(); + +} + +function update() { + + if (cursors.left.isDown) + { + } + else if (cursors.right.isDown) + { + } + + if (cursors.up.isDown) + { + } + else if (cursors.down.isDown) + { + } + +} + +function render() { + + game.debug.cameraInfo(game.camera, 32, 32); + +} diff --git a/examples/wip/tween var.js b/examples/wip/tween var.js new file mode 100644 index 00000000..2287ac3b --- /dev/null +++ b/examples/wip/tween var.js @@ -0,0 +1,39 @@ + +var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render }); + +function preload() { + + game.load.spritesheet('mummy', 'assets/sprites/metalslug_mummy37x45.png', 37, 45, 18); + +} + +var mummy; + +function create() { + + game.stage.backgroundColor = 0x3d4d3d; + + mummy = game.add.sprite(300, 300, 'mummy', 5); + + var t = game.add.tween(mummy).to( { "x": 400, "y": 400 }, 5000, Phaser.Easing.Linear.None, true); + + // var t = game.add.tween(mummy).to( { "scale.x": 4, "scale.y": 4 }, 5000, Phaser.Easing.Linear.None, true); + + t.onComplete.add(tweenOver, this); + +} + +function tweenOver(a) { + + console.log('over'); + console.log(a); + +} + +function update() { + +} + +function render() { + +} \ No newline at end of file diff --git a/src/gameobjects/GameObjectCreator.js b/src/gameobjects/GameObjectCreator.js index da0d4805..c1d4ec61 100644 --- a/src/gameobjects/GameObjectCreator.js +++ b/src/gameobjects/GameObjectCreator.js @@ -273,13 +273,12 @@ Phaser.GameObjectCreator.prototype = { * Creates a new Tilemap object. * * @method Phaser.GameObjectCreator#tilemap - * @param {string} key - Asset key for the JSON or CSV map data in the cache. - * @param {object|string} tilesets - An object mapping Cache.tileset keys with the tileset names in the JSON file. If a string is provided that will be used. + * @param {string} [key] - The key of the tilemap data as stored in the Cache. If you're creating a blank map don't pass anything for this parameter. * @return {Phaser.Tilemap} The newly created tilemap object. */ - tilemap: function (key, tilesets) { + tilemap: function (key) { - return new Phaser.Tilemap(this.game, key, tilesets); + return new Phaser.Tilemap(this.game, key); }, diff --git a/src/gameobjects/GameObjectFactory.js b/src/gameobjects/GameObjectFactory.js index 4320eba6..f4c6aab8 100644 --- a/src/gameobjects/GameObjectFactory.js +++ b/src/gameobjects/GameObjectFactory.js @@ -304,13 +304,12 @@ Phaser.GameObjectFactory.prototype = { * Creates a new Tilemap object. * * @method Phaser.GameObjectFactory#tilemap - * @param {string} key - Asset key for the JSON or CSV map data in the cache. - * @param {object|string} tilesets - An object mapping Cache.tileset keys with the tileset names in the JSON file. If a string is provided that will be used. + * @param {string} [key] - The key of the tilemap data as stored in the Cache. If you're creating a blank map don't pass anything for this parameter. * @return {Phaser.Tilemap} The newly created tilemap object. */ - tilemap: function (key, tilesets) { + tilemap: function (key) { - return new Phaser.Tilemap(this.game, key, tilesets); + return new Phaser.Tilemap(this.game, key); }, diff --git a/src/physics/World.js b/src/physics/World.js index 2ca0a844..4f8143dc 100644 --- a/src/physics/World.js +++ b/src/physics/World.js @@ -367,7 +367,10 @@ Phaser.Physics.World.prototype = { if (this.bounds !== null) { - this.world.removeBody(this.bounds); + if (this.bounds.world) + { + this.world.removeBody(this.bounds); + } var i = this.bounds.shapes.length; diff --git a/src/tilemap/Tilemap.js b/src/tilemap/Tilemap.js index c594f9df..a46a9c1d 100644 --- a/src/tilemap/Tilemap.js +++ b/src/tilemap/Tilemap.js @@ -11,7 +11,7 @@ * @class Phaser.Tilemap * @constructor * @param {Phaser.Game} game - Game reference to the currently running game. -* @param {string} [key] - The key of the tilemap data as stored in the Cache. +* @param {string} [key] - The key of the tilemap data as stored in the Cache. If you're creating a blank map don't pass anything for this parameter. */ Phaser.Tilemap = function (game, key) { @@ -155,37 +155,52 @@ Phaser.Tilemap.prototype = { * Creates an empty map of the given dimensions. * * @method Phaser.Tilemap#create - * @param {string} name - The name of the map (mostly used for debugging) + * @param {string} name - The name of the default layer of the map * @param {number} width - The width of the map in tiles. * @param {number} height - The height of the map in tiles. + * @param {number} tileWidth - The width of the tiles the map uses for calculations. + * @param {number} tileHeight - The height of the tiles the map uses for calculations. */ - create: function (name, width, height) { + create: function (name, width, height, tileWidth, tileHeight) { - var data = []; + this.width = width; + this.height = height; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.widthInPixels = width * tileWidth; + this.heightInPixels = height * tileHeight; + + var row; + var output = []; for (var y = 0; y < height; y++) { - data[y] = []; + row = []; for (var x = 0; x < width; x++) { - data[y][x] = 0; + row.push(null); } + + output.push(row); } this.layers.push({ name: name, + x: 0, + y: 0, width: width, height: height, + widthInPixels: this.widthInPixels, + heightInPixels: this.heightInPixels, alpha: 1, visible: true, - tileMargin: 0, - tileSpacing: 0, - format: Phaser.Tilemap.CSV, - data: data, + properties: {}, indexes: [], - dirty: true + callbacks: [], + bodies: [], + data: output }); @@ -200,8 +215,17 @@ Phaser.Tilemap.prototype = { * @method Phaser.Tilemap#addTilesetImage * @param {string} tileset - The name of the tileset as specified in the map data. * @param {string} [key] - The key of the Phaser.Cache image used for this tileset. If not specified it will look for an image with a key matching the tileset parameter. + * @param {number} [tileWidth] - The width of the tiles in the Tileset Image. If not given it will default to the map.tileWidth value. + * @param {number} [tileHeight] - The height of the tiles in the Tileset Image. If not given it will default to the map.tileHeight value. + * @param {number} [tileMargin=0] - The width of the tiles in the Tileset Image. If not given it will default to the map.tileWidth value. + * @param {number} [tileSpacing=0] - The height of the tiles in the Tileset Image. If not given it will default to the map.tileHeight value. */ - addTilesetImage: function (tileset, key) { + addTilesetImage: function (tileset, key, tileWidth, tileHeight, tileMargin, tileSpacing) { + + if (typeof tileWidth === 'undefined') { tileWidth = this.tileWidth; } + if (typeof tileHeight === 'undefined') { tileHeight = this.tileHeight; } + if (typeof tileMargin === 'undefined') { tileMargin = 0; } + if (typeof tileSpacing === 'undefined') { tileSpacing = 0; } if (typeof key === 'undefined') { @@ -222,10 +246,17 @@ Phaser.Tilemap.prototype = { if (this.tilesets[tileset]) { - this.tilesets[tileset].image = this.game.cache.getImage(key); - + this.tilesets[tileset].setImage(this.game.cache.getImage(key)); return true; } + else + { + var newSet = new Phaser.Tileset(key, 0, tileWidth, tileHeight, tileMargin, tileSpacing, {}); + + newSet.setImage(this.game.cache.getImage(key)); + + this.tilesets.push(newSet); + } return false; @@ -920,6 +951,12 @@ Phaser.Tilemap.prototype = { }, + hasTile: function (x, y, layer) { + + return (this.layers[layer].data[y] !== null && this.layers[layer].data[y][x] !== null); + + }, + /** * Puts a tile of the given index value at the coordinate specified. * @@ -937,14 +974,30 @@ Phaser.Tilemap.prototype = { { if (tile instanceof Phaser.Tile) { - this.layers[layer].data[y][x].copy(tile); + if (this.hasTile(x, y, layer)) + { + this.layers[layer].data[y][x].copy(tile); + } + else + { + //Phaser.Tile = function (layer, index, x, y, width, height) { + this.layers[layer].data[y][x] = new Phaser.Tile(layer, tile.index, x, y, tile.width, tile.height); + } } else { - this.layers[layer].data[y][x].index = tile; + if (this.hasTile(x, y, layer)) + { + this.layers[layer].data[y][x].index = tile; + } + else + { + this.layers[layer].data[y][x] = new Phaser.Tile(layer, tile, x, y, this.tileWidth, this.tileHeight); + } } this.layers[layer].dirty = true; + this.calculateFaces(layer); } diff --git a/src/tilemap/TilemapLayer.js b/src/tilemap/TilemapLayer.js index ad7ef6a1..ed92a2a8 100644 --- a/src/tilemap/TilemapLayer.js +++ b/src/tilemap/TilemapLayer.js @@ -62,7 +62,8 @@ Phaser.TilemapLayer = function (game, tilemap, index, width, height) { */ this.textureFrame = new Phaser.Frame(0, 0, 0, width, height, 'tilemapLayer', game.rnd.uuid()); - Phaser.Sprite.call(this, this.game, 0, 0, this.texture, this.textureFrame); + // Phaser.Sprite.call(this, this.game, 0, 0, this.texture, this.textureFrame); + Phaser.Image.call(this, this.game, 0, 0, this.texture, this.textureFrame); /** * @property {string} name - The name of the layer. @@ -280,8 +281,8 @@ Phaser.TilemapLayer = function (game, tilemap, index, width, height) { }; -Phaser.TilemapLayer.prototype = Object.create(Phaser.Sprite.prototype); -Phaser.TilemapLayer.prototype = Phaser.Utils.extend(true, Phaser.TilemapLayer.prototype, Phaser.Sprite.prototype, PIXI.Sprite.prototype); +Phaser.TilemapLayer.prototype = Object.create(Phaser.Image.prototype); +// Phaser.TilemapLayer.prototype = Phaser.Utils.extend(true, Phaser.TilemapLayer.prototype, Phaser.Sprite.prototype, PIXI.Sprite.prototype); Phaser.TilemapLayer.prototype.constructor = Phaser.TilemapLayer; /** @@ -292,7 +293,8 @@ Phaser.TilemapLayer.prototype.constructor = Phaser.TilemapLayer; */ Phaser.TilemapLayer.prototype.postUpdate = function () { - Phaser.Sprite.prototype.postUpdate.call(this); + // Phaser.Sprite.prototype.postUpdate.call(this); + Phaser.Image.prototype.postUpdate.call(this); // Stops you being able to auto-scroll the camera if it's not following a sprite this.scrollX = this.game.camera.x * this.scrollFactorX; @@ -554,7 +556,7 @@ Phaser.TilemapLayer.prototype.updateMax = function () { */ Phaser.TilemapLayer.prototype.render = function () { - if (this.layer.dirty) + if (this.layer.dirty) { this.dirty = true; } @@ -587,6 +589,102 @@ Phaser.TilemapLayer.prototype.render = function () { this.context.globalAlpha = this.debugAlpha; } + for (var y = this._startY, lenY = this._startY + this._maxY; y < lenY; y++) + { + this._column = this.layer.data[y]; + + for (var x = this._startX, lenX = this._startX + this._maxX; x < lenX; x++) + { + if (this._column[x]) + { + tile = this._column[x]; + + // this needs to know which set the index is from + // set = this.map.tilesets[this.map.tiles[tile.index][2]] + set = this.map.tilesets[0]; + + if (this.debug === false && tile.alpha !== this.context.globalAlpha) + { + this.context.globalAlpha = tile.alpha; + } + + set.draw(this.context, Math.floor(this._tx), Math.floor(this._ty), tile.index); + + if (tile.debug) + { + this.context.fillStyle = 'rgba(0, 255, 0, 0.4)'; + this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight); + } + } + + this._tx += this.map.tileWidth; + + } + + this._tx = this._dx; + this._ty += this.map.tileHeight; + + } + + if (this.debug) + { + this.context.globalAlpha = 1; + this.renderDebug(); + } + + if (this.game.renderType === Phaser.WEBGL) + { + // PIXI.updateWebGLTexture(this.baseTexture, renderSession.gl); + PIXI.updateWebGLTexture(this.baseTexture, this.game.renderer.gl); + } + + this.dirty = false; + this.layer.dirty = false; + + return true; + +} + +/** +* Renders the tiles to the layer canvas and pushes to the display. +* @method Phaser.TilemapLayer#render +* @memberof Phaser.TilemapLayer +*/ +Phaser.TilemapLayer.prototype.OLDrender = function () { + + if (this.layer.dirty) + { + this.dirty = true; + } + + if (!this.dirty || !this.visible) + { + // return; + } + + this._prevX = this._dx; + this._prevY = this._dy; + + this._dx = -(this._x - (this._startX * this.map.tileWidth)); + this._dy = -(this._y - (this._startY * this.map.tileHeight)); + + this._tx = this._dx; + this._ty = this._dy; + + this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); + + this.context.fillStyle = this.tileColor; + + var tile; + var set; + var ox = 0; + var oy = 0; + + if (this.debug) + { + this.context.globalAlpha = this.debugAlpha; + } + for (var y = this._startY, lenY = this._startY + this._maxY; y < lenY; y++) { this._column = this.layer.data[y]; @@ -638,11 +736,11 @@ Phaser.TilemapLayer.prototype.render = function () { ); } - if (tile.debug) - { + // if (tile.debug) + // { this.context.fillStyle = 'rgba(0, 255, 0, 0.4)'; this.context.fillRect(Math.floor(this._tx), Math.floor(this._ty), this.map.tileWidth, this.map.tileHeight); - } + // } } else { diff --git a/src/tilemap/TilemapParser.js b/src/tilemap/TilemapParser.js index e6b1e12a..8ff6470e 100644 --- a/src/tilemap/TilemapParser.js +++ b/src/tilemap/TilemapParser.js @@ -74,6 +74,11 @@ Phaser.TilemapParser = { */ parse: function (game, key) { + if (typeof key === 'undefined') + { + return this.getEmptyData(); + } + var map = game.cache.getTilemapData(key); if (map) @@ -89,7 +94,7 @@ Phaser.TilemapParser = { } else { - return this.getEmptyData(); + console.warn('Phaser.TilemapParser.parse - No map data found for key ' + key); } }, diff --git a/src/tilemap/Tileset.js b/src/tilemap/Tileset.js index 1737a6cf..1ddb96e1 100644 --- a/src/tilemap/Tileset.js +++ b/src/tilemap/Tileset.js @@ -11,7 +11,7 @@ * @class Phaser.Tileset * @constructor * @param {string} name - The name of the tileset in the map data. -* @param {number} firstgid - The Tiled firstgid value. +* @param {number} firstgid - The Tiled firstgid value. In non-Tiled data this should be considered the starting index value of the first tile in this set. * @param {number} width - Width of each tile in pixels. * @param {number} height - Height of each tile in pixels. * @param {number} margin - The amount of margin around the tilesheet. @@ -26,8 +26,7 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope this.name = name; /** - * @property {number} firstgid - The Tiled firstgid value. - * @default + * @property {number} firstgid - The Tiled firstgid value. In non-Tiled data this should be considered the starting index value of the first tile in this set. */ this.firstgid = firstgid; @@ -42,12 +41,12 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope this.tileHeight = height; /** - * @property {number} tileMargin - The margin around the tiles in the sheet. + * @property {number} tileMargin - The margin around the tiles in the tileset. */ this.tileMargin = margin; /** - * @property {number} tileSpacing - The margin around the tiles in the sheet. + * @property {number} tileSpacing - The spacing in pixels between each tile in the tileset. */ this.tileSpacing = spacing; @@ -56,11 +55,6 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope */ this.properties = properties; - /** - * @property {object} tilePproperties - Tile specific properties (typically defined in the Tiled editor). - */ - // this.tileProperties = {}; - /** * @property {object} image - The image used for rendering. This is a reference to the image stored in Phaser.Cache. */ @@ -81,48 +75,81 @@ Phaser.Tileset = function (name, firstgid, width, height, margin, spacing, prope */ this.total = 0; + /** + * @property {array} draw - The tile drawImage look-up table + * @private + */ + this.drawCoords = []; + }; Phaser.Tileset.prototype = { /** - * Gets a Tile from this set. + * Draws a tile from this Tileset at the given coordinates on the context. * - * @method Phaser.Tileset#getTile - * @param {number} index - The index of the tile within the set. - * @return {object} The tile object. - getTile: function (index) { + * @method Phaser.Tileset#draw + * @param {HTMLCanvasContext} context - The context to draw the tile onto. + * @param {number} x - The x coordinate to draw to. + * @param {number} y - The y coordinate to draw to. + * @param {number} index - The index of the tile within the set to draw. + */ + draw: function (context, x, y, index) { - return this.tiles[index]; + if (!this.image || !this.drawCoords[index]) + { + return; + } + + context.drawImage( + this.image, + this.drawCoords[index][0], + this.drawCoords[index][1], + this.tileWidth, + this.tileHeight, + x, + y, + this.tileWidth, + this.tileHeight + ); }, - */ /** - * Gets a Tile from this set. + * Adds a reference from this Tileset to an Image stored in the Phaser.Cache. * - * @method Phaser.Tileset#getTileX - * @param {number} index - The index of the tile within the set. - * @return {object} The tile object. - getTileX: function (index) { + * @method Phaser.Tileset#setImage + * @param {Image} image - The image this tileset will use to draw with. + */ + setImage: function (image) { - return this.tiles[index][0]; + this.image = image; + + this.rows = Math.round((image.height - this.tileMargin) / (this.tileHeight + this.tileSpacing)); + this.columns = Math.round((image.width - this.tileMargin) / (this.tileWidth + this.tileSpacing)); + this.total = this.rows * this.columns; + + // Create the index look-up + this.drawCoords.length = 0; + + var tx = this.tileMargin; + var ty = this.tileMargin; + var i = this.firstgid; + + for (var y = 0; y < this.rows; y++) + { + for (var x = 0; x < this.columns; x++) + { + this.drawCoords[i] = [ tx, ty ]; + tx += this.tileWidth + this.tileSpacing; + i++; + } + + tx = this.tileMargin; + ty += this.tileHeight + this.tileSpacing; + } }, - */ - - /** - * Gets a Tile from this set. - * - * @method Phaser.Tileset#getTileY - * @param {number} index - The index of the tile within the set. - * @return {object} The tile object. - getTileY: function (index) { - - return this.tiles[index][1]; - - }, - */ /** * Sets tile spacing and margins. @@ -136,20 +163,9 @@ Phaser.Tileset.prototype = { this.tileMargin = margin; this.tileSpacing = spacing; - }, - - /** - * Checks if the tile at the given index exists. - * - * @method Phaser.Tileset#checkTileIndex - * @param {number} index - The index of the tile within the set. - * @return {boolean} True if a tile exists at the given index otherwise false. - checkTileIndex: function (index) { - - return (this.tiles[index]); + this.setImage(this.image); } - */ }; diff --git a/src/utils/Debug.js b/src/utils/Debug.js index cc04125a..949d2381 100644 --- a/src/utils/Debug.js +++ b/src/utils/Debug.js @@ -90,6 +90,11 @@ Phaser.Utils.Debug = function (game) { */ this.currentAlpha = 1; + /** + * @property {boolean} dirty - Does the canvas need re-rendering? + */ + this.dirty = false; + }; Phaser.Utils.Debug.prototype = { @@ -142,6 +147,11 @@ Phaser.Utils.Debug.prototype = { this.currentAlpha = this.context.globalAlpha; this.columnWidth = columnWidth; + if (this.sprite && this.dirty) + { + // this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); + } + this.context.save(); this.context.setTransform(1, 0, 0, 1, 0, 0); this.context.strokeStyle = color;