Jump to content

is it possible to merge down the images of 2 tilemap layers to gain performance?


valueerror
 Share

Recommended Posts

i have one little tiled background image and two layers in tiled .. all of them are only used as background images.. all the collisionareas are polyline objects on different layers..

 

the problem is always the same.. the performance is so poor - the game is almost unplayable on a nexus7 (this is no low end device you know?!) 

 

of course my levels are quite big but this shouldn't matter at all because it's always just the camera view (800x480) that's beeing rendered but hey.. it does matter.. because scrolling tilemap layers still sucks hard..  

 

so i am searching for a neat workaround..

 

i exported the whole map as jpg and every level got this hughe jpg as background - this works .. i've got almost 60fps on the nexus7 BUT every jpg eats up to 900kb so this is no good way to go.. right?  (imagine 40 levels would need 40Mb space on your device .. and additional 20 for cocoonjs.. :( )

 

therefore i want to merge down the tilesprite with the background layers from tiled on load and get rid of the layers - scroll only one very big image.. because thats a LOT faster.. 

 

or any other idea would be very appreciated !! (otherwise my game will never go mobile :( )

Link to comment
Share on other sites

i exported the whole map as jpg and every level got this hughe jpg as background - this works .. i've got almost 60fps on the nexus7 BUT every jpg eats up to 900kb so this is no good way to go.. right?  (imagine 40 levels would need 40Mb space on your device .. and additional 20 for cocoonjs.. :( )

 

You don't have to have every level loaded from start to finish. Just load the level that is needed when it is needed and remove the previous level that is no longer used. So you only have 1 level at a time.

Link to comment
Share on other sites

that isn't the problem ... when i'm concerned about the total size of my game i do not worry about someone trying it in a browser .. as i said in the text you quoted i worry about the space it will use on your device (that means phone/tablet of course) i don't want my little app to be as hughe as 60MB in the playstore .. but that's offtopic anyways..  

 

the problem still is performance.. therefore i was asking if it is possible to merge all tiled layers (on the fly) to one big image that resides in mem for as long as you play the level.. (because i know this would boost performance)  i've got no other idea yet :(

Link to comment
Share on other sites

Usually games designers resort to clever tiling and composition to create the backgrounds. Those huge beautiful backgrounds in Sonic the Hedgehog are just loads of repeated strips. I guess without seeing your backgrounds it's hard to say what the best method would be to compose them down, but if push comes to shove you may just have to simplify them to something basic like clouds or a tall horizontally-repeated TileSprite anchored to camera and scrolled in the opposite direction to the camera, something like this: http://jsfiddle.net/lewster32/tcxt51z1/

Link to comment
Share on other sites

well... i have two tilesets and create my levels in tiled

 

1) background

2) foreground

3) collision_areas

4) objects

 

on the background and the foreground i design the level.. there are lots of slants,hills, blocks, plants and so on...

in game i add a single tileSprite 278x408 behind those layers that covers up all the transparent areas with sky..

 

that's not tooo complicated..  but the performance is very poor..  if i remove the backgroundlayer it suddenly is oke...  if i use one single huge jpg instead of the tilesprite and the two layeres it is also really good.. 

 

 

(if i remove the collision_areas .. that means no collisions at all in my game - just scrolling it doesn't change a bit.. so it is not the code.. it's just the rendering of the tilemap layers) :(

Link to comment
Share on other sites

Tilemap layers are expensive to render - they're essentially screen-sized BitmapData instances upon which the tile images are composed together every frame. When you have several of these overlaid on top of each other you have both the rendering, compositing and with WebGL the uploading to GPU overhead to contend with.

 

Would it be possible for you to upload your work in progress or show some screenshots? We may be able to give some guidance on how you could optimise it.

Link to comment
Share on other sites

wow .. this would be really reall nice of you.. 

 

btw. i just compiled it with cocoonjs and hey..   60fps on my nexus with cocoonjs ..  forcing canvas.. i had only 20fps in a big level when testing directly in chrome on the nexus.. that's is a little releave at last.. but still .. the nexus is a very good device..  it should also work on less performant devices

 

this is how i initiate the level environment:

function setupPhysics(){                        //start physics system and enable "expensive" collision events    game.physics.startSystem(Phaser.Physics.P2JS);    game.physics.p2.setImpactEvents(true);    game.world.enableBodySleeping=true;  //allow bodies to not get calculated when there is nothing to do    groundMaterial = game.physics.p2.createMaterial('ground'); //define materials    playerMaterial = game.physics.p2.createMaterial('player');    iceMaterial = game.physics.p2.createMaterial('ice');    game.physics.p2.createContactMaterial(playerMaterial, groundMaterial, { friction: 2, restitution: 0  });   // define what happens when one material contacts the other    game.physics.p2.createContactMaterial(playerMaterial, iceMaterial, { friction: 0.1, restitution: 0 });    game.canvas.oncontextmenu = function (e) { e.preventDefault();}; //prevent rightclick contextmenu}function setupCollisionGroups() {    //for worldbounds to work the collision groups have to be defined after resizeWorld and setBoundsToWorld    enemyGroundCG = game.physics.p2.createCollisionGroup();    enemyStaticCG = game.physics.p2.createCollisionGroup();    groundCG = game.physics.p2.createCollisionGroup();    playerCG = game.physics.p2.createCollisionGroup();}function setupLayers(level){    game.add.tileSprite(0, 0,6400,1280 , 'ice');    map = game.add.tilemap(level);    map.addTilesetImage('cowsonice-tileset');    map.addTilesetImage('ice-terrain');    layerbackground = map.createLayer('background');    layerforeground = map.createLayer('foreground');    layerforeground.resizeWorld();  // this stretches the world to the size of the layer    game.physics.p2.setBoundsToWorld();    setupCollisionGroups();  //do this after resizeWorld and setBoundsToWorld otherwise bounds will not work    collisionareas =   game.physics.p2.convertCollisionObjects(map,"collisionareas");    enemyareas = game.physics.p2.convertCollisionObjects(map,"enemyareas");    //setup all tiles with collisiongroups or materials    for (i=0; i<enemyareas.length; i++){        enemyareas[i].setCollisionGroup(enemyGroundCG);        enemyareas[i].collides([playerCG]);        enemyareas[i].setMaterial(groundMaterial);    }    for (i=0; i<collisionareas.length; i++){        collisionareas[i].setCollisionGroup(groundCG);        collisionareas[i].collides([playerCG,enemyGroundCG]);        collisionareas[i].setMaterial(iceMaterial);    }}

and as attachment my tiled screenshot..  

 

i also use a little bit of text..  for now the fps are shown and the lives of two players... 

 cowText = game.add.text(game.camera.width/2-50, 32, ' cow: '+cow_health, { fill: '#666', font: '16px Arial' });    cowText.fixedToCamera = true;    tuxText = game.add.text(game.camera.width/2+20, 32, ' tux: '+tux_health, { fill: '#666', font: '16px Arial' });    tuxText.fixedToCamera = true;    game.time.advancedTiming = true;    fpsText = this.game.add.text(game.camera.width-120, 10, '', { font: '16px Arial', fill: '#666' } );         fpsText.fixedToCamera = true;

i've heard text rendering is expensive for some reason..  i will change this text to little images but i can't imagine those three lines would do something bad

post-6912-0-14281800-1407704605_thumb.jp

Link to comment
Share on other sites

So is the background represented by the ladders and plants? Is this one giant image? If so, I'd say you'd be better off making these individual images and positioning them manually within your world. Maybe you can create some simple data format so you can position these? That would make the game run a lot faster, as currently it's rendering a lot of empty space - those totally transparent pixels still get rendered unfortunately, and as you layer more on top of each other they'll kill the fill rate.

Link to comment
Share on other sites

as you can see in the screenshot i tried to work just with tiles this time.. no hughe images with transparent areas..  so every plant, ladder, iceterrain, woodblock, is part of the tileset and i place them wherever i need them..    on the backgroundlayer there are all the iceterrain tiles and ond the foreground layer there are the plants and the rest.. 

 

this creates a lot of big transparent areas.. thats right.. but these transparent areas are not part of a bigger image.. they are just areas in tiled where i place no tile.. these shouldn't get rendered at all.. thats crazy..

 

 

if i create one hughe image 6400x480 with the iceterrain and a LOT of transparent pixels in it and add it in my code as normal sprite or image (btw. what's the difference between game.add.image and game.add.sprite ?)  if i do so the performance is top !!!!  but the filesize sucks hard...

Link to comment
Share on other sites

this creates a lot of big transparent areas.. thats right.. but these transparent areas are not part of a bigger image.. they are just areas in tiled where i place no tile.. these shouldn't get rendered at all.. thats crazy..

 

Unfortunately under the hood this is pretty much how TileMaps work - each layer is a screen-sized BitmapData instance constructed from your individual tile images, and where there are no tiles, it just leaves it transparent. This is why having more than a few layers starts to kill mobile devices.

 

Images are just sprites without physics bodies and with a few other things stripped out to make them perform a bit better.

 

It's hard to say how best to proceed given you want a very specific layout, but I think TileMaps may not be a good choice for what you're trying to do. TileMaps are usually for heavily grid-based layouts where you can repeat small tiles to build up larger structures. What you're doing seems more suited to a custom implementation where you'd have your plants and so on as individual images/sprites laid out along the level much like Flappy Bird, with a custom data structure to determine where they appear. Combine that with manually importing the collision shapes you've set up into P2 and that should run a lot better - unfortunately that does probably mean a lot more work! :(

Link to comment
Share on other sites

This is a great thread!

Me too, I had terrible 2 weeks of performance improving, tweaks and workaround.

Lucky me, that's before I have a game, just testing tilemaps and layers with different devices.

 

Like you (Valueerror), I came to the point when exporting the layer as an image and not as a layer is much faster.

But as lewster32 mentioned, it has a lot of dead transparent pixels. You have also said:

I'd say you'd be better off making these individual images and positioning them manually within your world. Maybe you can create some simple data format so you can position these?

 

Can you please explain what do you mean by "create some simple data format so you can position these"? I understand the idea, but not sure how to do it. This way only relevant pixels should be rendered and it will be much faster, the thing is that positioning every image manually will take a lot of time. How can you create data format to position them as in Tiled in an automatic way (or even a semi automatic)?

 

Thanks.

Link to comment
Share on other sites

You can place tiles as objects in Tiled, on an object layer.

 

 

To valueerror :)

 

Does it make the performance better using only Object layers instead of Tile layers? Or you meant that if you already have an Object layer, so why not to combine tiles in it? 

Link to comment
Share on other sites

@ the object layer suggestion: 

well i guess he means that by placing everything on the object layer, i could use the function "createfromobjects" in my code to create (on the fly) several individual objects in my game (without the big transparent areas between them) 

 

that would be a bunch load of work but it would be a nice way to implement lewsters idea..  i still would have to create at least one layer (background) for my ice-terrain-forms because placing these in my game with "createfromobjects" on the exact right place would be really hard.. especially because i combine them in my levels in several different ways to create the forms ...

 

but i could use this to place the objects on my "foreground" layer.. plants, boxes, wood,....  they would be converted into sprites which is not that bad because then i could work with them in an easy to handle way (destroy them, move them around and so on..) 

 

i would need at least one line for every object and a spritesheet (or several) that contains all my objects

createFromObjects(name, gid, key, frame, exists, autoCull, group, CustomClass, adjustY)

i already do this in my game with one single object:

finishlines = game.add.group();map.createFromObjects('objects', 1, 'chain', 3, true, false, finishlines);finishlines.forEach(setupFinshline,this);

i would do this if i could be sure this will bring back perormance..  what do all you mean? 

Link to comment
Share on other sites

@ the object layer suggestion: 

well i guess he means that by placing everything on the object layer, i could use the function "createfromobjects" in my code to create (on the fly) several individual objects in my game (without the big transparent areas between them) 

 

that would be a bunch load of work but it would be a nice way to implement lewsters idea..  i still would have to create at least one layer (background) for my ice-terrain-forms because placing these in my game with "createfromobjects" on the exact right place would be really hard.. especially because i combine them in my levels in several different ways to create the forms ...

 

but i could use this to place the objects on my "foreground" layer.. plants, boxes, wood,....  they would be converted into sprites which is not that bad because then i could work with them in an easy to handle way (destroy them, move them around and so on..) 

 

i would need at least one line for every object and a spritesheet (or several) that contains all my objects

createFromObjects(name, gid, key, frame, exists, autoCull, group, CustomClass, adjustY)

i already do this in my game with one single object:

finishlines = game.add.group();map.createFromObjects('objects', 1, 'chain', 3, true, false, finishlines);finishlines.forEach(setupFinshline,this);

i would do this if i could be sure this will bring back perormance..  what do all you mean? 

 

It sounds very interesting way to explore...

It's a bit ugly (as you said, one line for each object since the functions gets only 1 frame at the time).

Are you gonna try that? If so, can you share your performance experience?

Link to comment
Share on other sites

It sounds very interesting way to explore...

It's a bit ugly (as you said, one line for each object since the functions gets only 1 frame at the time).

Are you gonna try that? If so, can you share your performance experience?

 

Well, I couldn't held myself and I tried it myself.

The performance are much better, probably the best I tried so far.

 

In my game, in the previous method (layer as an image with transparent pixels) with last cocoonjs and phaser I had 48-60 fps on low end devices (galaxy s2/s3) and now I'm getting fixed 60-64 fps.

 

The only downside is that the sprites are created on the fly, which means extra loading time on the start (but not so much).

 

This is a GREAT tip, thanks wayfinder.

Link to comment
Share on other sites

very nice..  thanks for testing this idea !!

 

another thing i can definitely recommend is https://tinypng.com/  ..  it reduces the size of your pngs for example from 1.2 MB to 250kb without ANY visible quality loss..  this is awesome..

 

and for linux users (for example ubuntu) there is a console program for that.. (so you don't have to give your work to a third party):

sudo apt-get install pngquantpngquant tileset.png   (this will reduce the filesize to one third of the original without quality loss)

that way it is getting interesting again to export a whole level as png and get rid of the tilemap layers completely..

 

but i think i'm going to try to get rid of one of the layers by using an object layer for all my game "decos" and report here about the performance gain :)

Link to comment
Share on other sites

very nice..  thanks for testing this idea !!

 

another thing i can definitely recommend is https://tinypng.com/  ..  it reduces the size of your pngs for example from 1.2 MB to 250kb without ANY visible quality loss..  this is awesome..

 

and for linux users (for example ubuntu) there is a console program for that.. (so you don't have to give your work to a third party):

sudo apt-get install pngquantpngquant tileset.png   (this will reduce the filesize to one third of the original without quality loss)

that way it is getting interesting again to export a whole level as png and get rid of the tilemap layers completely..

 

yeah. tinypng, smushit or any other command line (have a Mac though) to reduce your png files are great.

I'm facing the same problems that your have, multiple layers with Tiled, exporting each layer as an image and it's really painful.

 

Hopefully, you could combine these two methods and get even a better performance (actually I'm gonna do this myself).

Link to comment
Share on other sites

yeah. tinypng, smushit or any other command line (have a Mac though) to reduce your png files are great.

I'm facing the same problems that your have, multiple layers with Tiled, exporting each layer as an image and it's really painful.

 

Hopefully, you could combine these two methods and get even a better performance (actually I'm gonna do this myself).

 

Same here, couldn't held myself :)

performance much better with the tinypng and createfromobject method.

time.msMax droped from around 700ms to 300ms on galaxy s2 (last cocoonjs/phaser)

Link to comment
Share on other sites

well.. i have to investigate and test a little bit more but it seems that replacing one layer with objects did nothing perfomancewise.. :(

while replacing one layer with a huge image did last time i tried..

i now have 5 well structured tilesets to make it work instead of one but i dont think this is responsible for any performance drain...(or at least i dont know how)

tested in chrome..

Link to comment
Share on other sites

oke..   i converted the wrong layer..  i absolutely makes no difference if i add all the items as objects or as layer in my game..   BUT ... removing the background layer (way more tiles in bigger groups) does the trick..  so to summarize it..  it does make a difference if you render one layer or no but it depends (of course) on how many tiles are painted on it..  the transparent areas do not seem to matter that much...

 

so i got rid of ALL layers..  i export the backgroundlayer as png (sized down with "pngquant" which does a wonderfull job in preserving the quality while reducing the size of the file to one third of the original)  and then add it in my code as normal image.. along with one tiled background image under it...

 

i killed the foreground layer and use an object layer instead and the createfromobjects method to populate my game world with items, plants, and so on..

 

the collision data (all my shapes painted around the terrain of the background) is now the only other thing (besides the objects) imported directly from the tilemap....

 

because i have no layers anymore i resize my gameworld with this line instead of layer.resizeWorld();

game.world.setBounds(0, 0, map.widthInPixels,map.heightInPixels, true,true,true,true);

i now have 57-60 fps on my tablet tested directly in chrome..   60fps straight in cocoonjs ^^  ( instead of 27- 50 fps with tilemap-layers)

 

:D

(i mark this post as "solved" even if the original question was not answered and the right answer lies between several other posts of you :) ) many thx to you all..  this does the trick for me and i can continue working on the game

Link to comment
Share on other sites

yes.. i am glad too.. unfortunately this makes things more complicated.. i really hope this layer performance thing can be sorted out in phaser somehow sometime in the future because it makes using tiled and layers a nogo for mobile development.. my new workstation, an i7 quadcore with 16 gb ram manages to render 8 layers without framedrops..(one or two occasionally) we cant expect people to use such highend devices...

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...