saltysquid

Trouble drawing a complex image in Phaser

Recommended Posts

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 ;):D 

 

I haven't even mentioned the issues I'm having with the dungeon maps lol... but I'll save that for another day :) 

 

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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 by saltysquid
code added

Share this post


Link to post
Share on other sites

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!

Share this post


Link to post
Share on other sites

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.

 

 

Share this post


Link to post
Share on other sites

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!

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.