saltysquid Posted August 26, 2016 Share Posted August 26, 2016 I'm having a very hard time trying to draw a complex image using Phaser. My game has a procedurally generated world (very large as well) which when a user presses ESC should bring up the world-map view. This view is a collection of colors which represent the actual map.... blue for water, green for grass, etc. Because every "square" of my world can result in a different colored pixel on the world map I'm at a loss as to how to build the map. I've tried using sprites.... creating a sprite for every pixel and placing it on the screen in the correct position. This actually works but I end up with a huge collection of sprites which is a memory hog. Also, every time the user moves I need to move all of these sprites individually OR I need to delete the entire group and rebuild the whole map. Again, this works but it's far from practical and after 5-10 minutes of messing with the world map it finally crashes due to memory issues. I've tried using the graphics object.... I create a graphics object and moveto() and lineto() using the correct color. Then I create a sprite using the graphics object and use this for display. This also works but takes roughly 1.5 minutes to render the world map for a medium sized world. Not an option I've tried creating BitMapData.... I used beginPath(), rect(), fillStyle and fill() to populate the bitmap data, then I create a sprite using the bitmap data. This does not work at all. After 20+ minutes of waiting I'd say the algorithm was about 60% complete. Definitely not an option. Here's my simple routine using the graphics approach... (I removed some of the if/then picking colors for terrain types) private generateWorldMap() { var penWidth = 5; var graphics = this.game.add.graphics(this.worldMap.BaseLayer.length * 5, this.worldMap.BaseLayer[0].length * 5); for (var x = 0; x < this.worldMap.BaseLayer.length; x++) { for (var y = 0; y < this.worldMap.BaseLayer[0].length; y++) { var location = new Phaser.Point(x * penWidth, y * penWidth); if (this.worldMap.BaseLayer[x][y] == TerrainType.MirrorQuest.TerrainType.Grass) { graphics.lineStyle(5, 0x000000, 0.5); } else { graphics.lineStyle(5, 0xDBDBDB, 0.9); } graphics.moveTo(location.x, location.y); graphics.lineTo(location.x + 5, location.y); } } this.worldmapMap = new Phaser.Sprite(this.game, 0, 0, graphics.generateTexture()); graphics.destroy(); } My question is - If I can't use the above 3 options, what other options do I have? I'm willing to consider options outside of the box as well I haven't even mentioned the issues I'm having with the dungeon maps lol... but I'll save that for another day Link to comment Share on other sites More sharing options...
HappinessSam Posted August 26, 2016 Share Posted August 26, 2016 Just to check, when you were trying to use bitmapData did you try just using setPixel? That would seem the most straightforward method if each square is represented by a single pixel. How big would the image need to be, because even a massive image, say 2048x2048 setting every pixel individually with rect shouldn't be taking 20 minutes. Link to comment Share on other sites More sharing options...
saltysquid Posted August 26, 2016 Author Share Posted August 26, 2016 (edited) setPixel.... I didn't know about that. I was trying to make every "pixel" 5x5 so I'm not sure if I can do that. Maybe I can display it larger once created? Let me revert my code back to using bitmapData and test using setPixel. I will post my code and results shortly. Thanks! Edit: Here is my original bitmapData code which is incredibly slow: private generateWorldMap() { var penWidth = 5; var bmd = this.game.add.bitmapData(this.worldMap.BaseLayer.length * 5, this.worldMap.BaseLayer[0].length * 5); bmd.ctx.beginPath(); for (var x = 0; x < this.worldMap.BaseLayer.length; x++) { for (var y = 0; y < this.worldMap.BaseLayer[0].length; y++) { var location = new Phaser.Point(x * penWidth, y * penWidth); bmd.ctx.rect(location.x, location.y, location.x + 5, location.y + 5); if (this.worldMap.BaseLayer[x][y] == TerrainType.MirrorQuest.TerrainType.Grass) { bmd.ctx.fillStyle = '#FF0000'; } else { bmd.ctx.fillStyle = '#000000'; } bmd.ctx.fill(); } } this.worldmapMap = new Phaser.Sprite(this.game, 0, 0, bmd); } Edited August 26, 2016 by saltysquid code added Link to comment Share on other sites More sharing options...
saltysquid Posted August 26, 2016 Author Share Posted August 26, 2016 Ok, here is my updated code using setPixel(). The array was 1,025x530.... not crazy. I gave up waiting for it to complete after 5 minutes. That's an unacceptable amount of time I used a very similar method to draw the world map when I still coded using XNA and the method would complete nearly instantaneously. Not trying to compare the 2 as I do love both, but a simple task such as drawing dots on the screen really has me stumped. Here's the updated method which was so slow: private generateWorldMap() { var penWidth = 5; var bmd = this.game.add.bitmapData(this.worldMap.BaseLayer.length * 5, this.worldMap.BaseLayer[0].length * 5); for (var x = 0; x < this.worldMap.BaseLayer.length; x++) { for (var y = 0; y < this.worldMap.BaseLayer[0].length; y++) { var location = new Phaser.Point(x * penWidth, y * penWidth); if (this.worldMap.BaseLayer[x][y] == TerrainType.MirrorQuest.TerrainType.Grass) { bmd.setPixel(location.x, location.y, 255, 0, 0); } else { bmd.setPixel(location.x, location.y, 0, 0, 0); } } } this.worldmapMap = new Phaser.Sprite(this.game, 0, 0, bmd); } Any help is appreciated! Link to comment Share on other sites More sharing options...
symof Posted August 26, 2016 Share Posted August 26, 2016 Check this topic out. It has 2 examples on how to do what you want, one with bitmapdata and one with texture This is assuming you already have the map array. Link to comment Share on other sites More sharing options...
HappinessSam Posted August 26, 2016 Share Posted August 26, 2016 Try changing it to bmd.setPixel(location.x, location.y, 255, 0, 0, false); in the main loop then at the end call the correct value for the last pixel. The false is the flag for whether to update the image context immediately and it defaults to true. This mean it's calling this.context.putImageData thousands of times, hence very slow. If you only call it at the end it should be much faster. I checked the Bitmapdata source and I couldn't see a proper function to call this.context.putImageData, hence calling setPixel with immediate true so it updates the image. There is probably a better, correct way to do this, but it's a bit late and I need to head to bed. Link to comment Share on other sites More sharing options...
saltysquid Posted August 27, 2016 Author Share Posted August 27, 2016 Thanks for the link symof, I'll have to play around with those ideas. HappinessSam - That worked! Deferring the putImageData call by setting immediate to false did the trick. I can now execute this method nearly instantly... it was actually too fast to time Now to get this working from within my hud class. Well, back to coding. Thanks again! Link to comment Share on other sites More sharing options...
Recommended Posts