Jump to content

Help with poor mobile browser performance


Recommended Posts

Hi.

 

I'm still a newbie so please forgive the vagueness of this.  I have been testing my work-in-progress game on various PC browsers and it seems to perform well enough on most.  Chrome is best followed by FF.  Even IE has reasonable performance so I am fairly happy that things are OK with PC users.  (Obviously depending on the users graphics capability etc). However last night I tried it on a mobile (Saumsung Note 3 with Chrome) and a tablet (Samsung Galaxy Tab with Chrome).  The performance dreadfully slow (unusable actually) but worse still the physics was broken, specifically the collision detection simply wasn't happening and sprites were falling through floors and walls etc.

 

On the plus side I got Phaser to easily detect touch screen and implemented some on-screen control buttons very nicely.

 

Has anybody encountered similar issues?  Is there anything I should be doing to improve performance when running the game on a mobile device?  

 

For example should I use CANVAS instead of AUTO?

 

Thanks,

Owen

Link to post
Share on other sites

Hi Owen,
 
I presume it's Mr. Goggles that you're having issues with? Just stuck the game on my Samsung Galaxy S4, and ran it through the Chrome profiler. Each frame is indeed taking around 500ms (2 fps). The CPU profiler shows a lot of QuadTree operations (especially insert()), which sounded like expensive collision detection.

 

Sure enough, you have several tens of game.physics.arcade.collide() calls in your gaming loop, which is the cause of your performance problem. Perhaps you could use a lighter weight collision detection system, rather than a full-blown physics engine, and also make an intelligent decision about which objects really need collision detection.

 

I believe update() in Phaser is tied to the requestAnimationFrame() render loop. In other words, you have tied your physics engine update/collision detection to your render frame rate. Physics engines tend to operate most reliably at a fixed, short time step; when the physics update rate falls (such as when you have performance issues), the collision detection can start to go wonky, as it has done in your case. If you determine that the physics engine is still best for your needs, you can use an accumulator to separate the physics time step from the framerate.

 

A developer more familar with Phaser may be able to suggest a more Phaser-like way of achieving this.

 

Hope this helps,

 

Vince.

 

Link to post
Share on other sites

Yes - thanks guys that does give me somewhere to look, however I am pretty sure I definitely have only the collision checks that I actually need.  At present I have 4 types of enemy sprite and 1 player, plus various tiles and scenery objects - all of which need a collision check.   There are some checks I could lose but would slightly affect gameplay.  For example I want certain baddies to 'bump into' each other (Lewster these are my group vs group and group vs themselves checks).  I could easily sacrifice that behaviour to gain performance.

 

The constant re-checking of collisions in update() is my prime suspect though.  What if I reduce the frequency so it only checks in every Nth iteration of update()?  AND maybe I could only check the 'on screen' collisions and exclude off-camera sprites in the checks.  (Is that easy to code?).

 

I'll post again when I've made a few tweaks.  Thanks again for the suggestions, very useful.

 

 

O.

Link to post
Share on other sites

You could put all four enemy types into a single array. You can still keep them in separate groups or arrays as needed, but also create another array of 'enemies' which is tested against whatever it is enemies need to collide with.

 

As for updating at a lower rate, this will probably cause a lot of problems with things failing to collide and will probably just mean your game gets choppy in a different way. Also, if you don't update objects offscreen, they'll fall through the floor and do other crazy stuff - however, if you're okay with objects that are well out of view going into a kind of stasis, you could probably set body.enable = false on them when they're outside of the viewport + a decent amount of padding.

Link to post
Share on other sites

Wow, I've just checked your code - that is a LOT of collides and overlaps! I rarely exceed two in my games! I'd definitely consider organising your objects into arrays which can then be checked with a single call - for instance, all objects which need to collide with the TileMap are put into a single array, all objects which collide with one another are put into another array and so on. You could then put the logic regarding what happens when certain things collide/overlap with one another in the collision callback, by checking the two colliding objects and performing the relevant action as a result.

Link to post
Share on other sites

Regarding. Canvas vs Auto:

This will be an order of magnitude of less importance than the advice given to you in the other answers but now iOS8 detects webGL use atlases as much as possible, if you aren't already. My much more simple games than yours were unplayable on this compared to Canvas until I consolidated all images into a single atlas. Switching to canvas did help as well but then performance suffered on some laptops as they worked better with webGL.

I found that the best results across all devices come with Auto but only if you consider the limitations of webGL and canvas. Otherwise you need to check for nearly every device and make a manual switch to webGL or Canvas and even then there's bound to be some devices that buck the trend.

Link to post
Share on other sites

The constant re-checking of collisions in update() is my prime suspect though.  What if I reduce the frequency so it only checks in every Nth iteration of update()?

 

 

As for updating at a lower rate, this will probably cause a lot of problems with things failing to collide and will probably just mean your game gets choppy in a different way.

@lewster32 said it  :) . The issue isn't that collisions are being checked in update(); it's more the fact that there is a fixed relationship between the number of update() calls, and the number of collision checks (even if that's, say, one check every 10 calls). That fixed relationship means that, if the frame rate drops, players will fall more slowly, etc. (and, as mentioned previously, may also fail to collide).

 

The so-called "accumulator" works by breaking that fixed relationship. Let's say your physics engine wants 60 updates a second to be realistic/accurate, but your update() is only being called 20 times a second. The accumulator will realise that 1/20th of a second has passed since the last update() call, and call your physics engine 3 times. A good accumulator will even deal with any "left over" time appropriately e.g. if update() is called after 1/19th of a second.

 

Hopefully, you'll achieve 60 frames per second in the end, so an accumulator would just have to mop up any slight glitches in framerate  :) . Best of luck with the optimisations!

Link to post
Share on other sites

Wow, I've just checked your code - that is a LOT of collides and overlaps! I rarely exceed two in my games! I'd definitely consider organising your objects into arrays which can then be checked with a single call - for instance, all objects which need to collide with the TileMap are put into a single array, all objects which collide with one another are put into another array and so on. You could then put the logic regarding what happens when certain things collide/overlap with one another in the collision callback, by checking the two colliding objects and performing the relevant action as a result.

 

Thanks I will do some reorganising then.  But I was under the (wrong) impression that one should use groups instead of arrays?   

Link to post
Share on other sites

Groups are not as flexible as arrays, as they are display objects and so their children can only be in one group at any one time. Unfortunately, using arrays you don't get the QuadTree optimisation that you do with groups, but I think in your case that won't matter too much. If I were you, at this stage I'd maybe add in some kind of a counter to detect how many individual collision/overlap checks you're doing each frame, and then work on getting that number down as low as possible.

 

I've put together a little fiddle with a basic implementation just so you get some numbers to work with: http://jsfiddle.net/lewster32/jd3tdp67/

 

I'd also highly recommend this plug-in for much better metrics: https://github.com/englercj/phaser-debug

Link to post
Share on other sites

Wow, I've just checked your code - that is a LOT of collides and overlaps! I rarely exceed two in my games! I'd definitely consider organising your objects into arrays which can then be checked with a single call - for instance, all objects which need to collide with the TileMap are put into a single array, all objects which collide with one another are put into another array and so on. You could then put the logic regarding what happens when certain things collide/overlap with one another in the collision callback, by checking the two colliding objects and performing the relevant action as a result.

 

Right I've just started to attempt use of arrays here.  I'm trying to put all my enemy sprites into an array called gEnemies and check them in a single call.  It's not working so far - the enemies are slipping past tiles in the tile layer instead of colliding.

 

I am declaring the array with a simple declaration:

var gEnemies = new Array();

I made myself a function to populate an array from a group (or rather, add the sprites from a group into an array):

 

function createSpriteArrayFromGroup(spriteGroup,spriteArray) {    spriteGroup.forEach(function (s) {        spriteArray.push(s);    }, this);}
..so then within the create() function I'm then doing this to populate the array with enemies:
 
    // populate arrays of sprites (to be used for quicker collision detection)    createSpriteArrayFromGroup(honkers, gEnemies);    createSpriteArrayFromGroup(goblins, gEnemies);    createSpriteArrayFromGroup(troopers, gEnemies);    createSpriteArrayFromGroup(generals, gEnemies);    createSpriteArrayFromGroup(bossgenerals, gEnemies);

EDIT: After calling this I have confirmed that gEnemies contains the expected sprites.

 

And then within update() I've commented out the old group-based checks and replaced with quicker array-based checks:

game.physics.arcade.collide(gEnemies, tileLayer);

The problem? Collision is not happening at all.  No errors are shown in the console so I'm not sure where I'm going wrong.

 

(By not working I mean the enemy sprites are walking through walls and falling through floors)

 

Is there anything obviously wrong with the above?  I'm stumped.

 

Is it possible that I cannot use an array for collision of sprites with the tile layer?

 

 

Thanks

Owen

Link to post
Share on other sites

can't you just use groups of groups? add all groups that should collide with the tilemap to another (parent) group and collide with this group then..

 

edit:

@lewster: what i don't really get is why using less collision calls in the update loop is better..  in the end every object still needs to be checked against every object otherwise overlaps would happen...

Link to post
Share on other sites

Collide/overlap are not recursive, so groups of groups won't work. Also, the problem with more collision/overlap calls is there will be overhead, duplicates and wasted calls all of which gets magnified with every successive call. This is why I've provided a way to check the numbers, as it's the number of intersection and separation calls which impacts performance.

Link to post
Share on other sites

can't you just use groups of groups? add all groups that should collide with the tilemap to another (parent) group and collide with this group then..

 

 

Well according to...

 

http://docs.phaser.io/Phaser.Physics.Arcade.html#collide

 

"You can perform Sprite vs. Sprite, Sprite vs. Group, Group vs. Group, Sprite vs. Tilemap Layer or Group vs. Tilemap Layer collisions. Both the first and second parameter can be arrays of objects, of differing types. If two arrays are passed, the contents of the first parameter will be tested against all contents of the 2nd parameter"

 

So I'm not 100% clear but it looks like the params cannot be groups of groups.  (EDIT: As Lewster said!)

 

I'm still looking at using arrays but having problems (see above post).  

 

Another idea, why not just have 1 sprite in 2 groups at once?  For example I could keep my groups for goblins, troopers, honkers and generals but put their sprites also into another group called enemies and then do all my collision check on enemies.  So each 1 goblin would be referenced in 2 groups (goblins and enemies) and use enemies for the collision checks.  Is such a thing normal practice and/or possible?

Link to post
Share on other sites

Collide/overlap are not recursive, so groups of groups won't work. Also, the problem with more collision/overlap calls is there will be overhead, duplicates and wasted calls all of which gets magnified with every successive call. This is why I've provided a way to check the numbers, as it's the number of intersection and separation calls which impacts performance.

 

Yep and I'm going to use that.  However first of all I need the collision to actually work :-)

 

EDIT: Colliding enemies array with tile layer isn't working for me right now but I'm probably making a newbie error somewhere.

Link to post
Share on other sites

Another idea, why not just have 1 sprite in 2 groups at once?

 

Afraid you can't do this - this is why you should use arrays. Groups are display objects, and so a child of a group cannot be in two groups, as then it'd have to potentially be in two places at once. Arrays on the other hand are just abstract lists of references to objects, so a single object can be in any number of arrays.

Link to post
Share on other sites

I think a good first step would be to just dump every Sprite in your game into a single array, and then just do:

// Collide everything with everything elsegame.physics.arcade.collide(this.allSprites);// Collide everything with the tilemapgame.physics.arcade.collide(this.allSprites, tileLayer)

And ensure that all works, check the numbers and take it from there. If you could get all of your collidable sprites into a single group, then further optimisation would happen as group collision uses a QuadTree to work out what objects could potentially collide with one another to reduce the number of intersection calls, but that may be difficult given how far you've come with your game now.

Link to post
Share on other sites

Afraid you can't do this - this is why you should use arrays. Groups are display objects, and so a child of a group cannot be in two groups, as then it'd have to potentially be in two places at once. Arrays on the other hand are just abstract lists of references to objects, so a single object can be in any number of arrays.

 

Right. That makes sense. Got it.

Link to post
Share on other sites

I think a good first step would be to just dump every Sprite in your game into a single array, and then just do:

// Collide everything with everything elsegame.physics.arcade.collide(this.allSprites);// Collide everything with the tilemapgame.physics.arcade.collide(this.allSprites, tileLayer)

And ensure that all works, check the numbers and take it from there. If you could get all of your collidable sprites into a single group, then further optimisation would happen as group collision uses a QuadTree to work out what objects could potentially collide with one another to reduce the number of intersection calls, but that may be difficult given how far you've come with your game now.

 

Yes thanks, good advice and I think you are right I need to strip it down and start from scratch with the collisions.  I'll start with a single array "tileLayerCollidableSprites" and go from there.  If I can at least get that working I will be getting somewhere.

 

One thing I noticed in your example you used "this.allSprites" instead of just "allSprites".  Is that significant?  I'm just using "gEnemies" instead of "this.gEnemies".

 

Owen.

Link to post
Share on other sites

Not sure if I'm populating the array the best way.  Do you think this is an OK way to populate an array from a sprite group?

function createSpriteArrayFromGroup(spriteGroup,spriteArray) {    spriteGroup.forEach(function (s) {        spriteArray.push(s);    }, this);}
Link to post
Share on other sites

Your population function will work fine, though I'd probably write some function which creates the sprites, similar to the game.add.sprite function, and then add them to your array there - at least then you know every time you create a sprite it's being added there and then, and you're not likely to end up with duplicates:

addSprite(x, y, key, frame, group) {  var sprite = game.add.sprite(x, y, key, frame, group);  spriteArray.push(sprite);}

Obviously if you're using group.createMultiple or other ways of doing it then you'll need to do something different for those, but definitely adding them to the array at the time of creation will be easier to track and manage.

Link to post
Share on other sites

Hmmm I seem to have a fundamental problem here, when I use collide() with an array of sprites the collision with tile layer simply does not work.  If I pick out a group of sprites then it works fine but not with an array.  I must be getting something wrong here.  I have double checked the array contains a load of sprites so that should be fine.

 

(In the snippet below gAllSprites is an array whereas redApples is a group).

        // collide all sprites with each other        game.physics.arcade.collide(gAllSprites);        // collide sprites with the tile layer                game.physics.arcade.collide(gAllSprites, tileLayer);  // THIS DOES NOTHING???        game.physics.arcade.collide(redApples, tileLayer); // this works        game.physics.arcade.collide(redApples, redApples); // this works

What could I be missing?

 

EDIT:  I think I found the answer  :)

 

Simply had to swap the paramaters around the other way, so tileLayer comes first:

 

Instead of 

        game.physics.arcade.collide(gAllSprites, tileLayer);  

I changed it to this, and it worked:

        game.physics.arcade.collide(tileLayer, gAllSprites);  

Now I am getting somewhere.... Will post again when I've got there.  

 

Cheers.

O.

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.

×
×
  • Create New...