producerism Posted January 13, 2015 Share Posted January 13, 2015 I'm porting a Flash/AIR project to Phaser, and everything is going pretty smoothly so far, but I hit a really odd performance issue which I'm hoping someone can explain to me. When using a forEach loop on a phaser Group (called desksGroup) which only has 9 items in it, I get ~40fps on mobile. But if I get rid of that forEach loop, and instead put the code into the built-in update() function of each of the 9 sprites within the group, FPS drops to ~4. Here is the game loop with a forEach going through a group of sprites (desksGroup):GameState.prototype.update = function() { // kill any pencils that hit a wall game.physics.arcade.overlap(layerMap, pencilsGroup, pencilHitWall); // TODO: subclass "Sprite" so that these update functions call themselves desksGroup.forEach(function(desk) { clientsGroup.forEach(function(theClient) { // get closest client if (!desk.currentTarget || desk.currentTarget && game.physics.arcade.distanceBetween(desk, theClient) < game.physics.arcade.distanceBetween(desk, desk.currentTarget)) desk.currentTarget = theClient; // other ideas for targeting: least health, most health, slowest, fastest }); if (desk.currentTarget && desk.currentTarget.health > 0 && game.physics.arcade.distanceBetween(desk, desk.currentTarget) < desk.range) { if (game.time.now > desk.nextFire) { desk.nextFire = game.time.now + desk.fireRate; var pencil = pencilsGroup.getFirstExists(false); pencil.reset(desk.x, desk.y); pencil.lifespan = desk.fireLife; pencil.rotation = game.physics.arcade.moveToObject(pencil, desk.currentTarget, desk.fireSpeed); } } }); game.physics.arcade.overlap(clientsGroup, pencilsGroup, pencilHitClient); if(defaults.showHealth) { clientsGroup.forEach(function(client) { game.debug.geom(new Phaser.Rectangle(client.x, client.y - 16, Math.max(0, tilemap.tileWidth * (client.health / defaults.health)), 7), '#00ff00', true); }); }}And here is a slightly modified version, with that forEach removed:GameState.prototype.update = function() { // kill any pencils that hit a wall game.physics.arcade.overlap(layerMap, pencilsGroup, pencilHitWall); game.physics.arcade.overlap(clientsGroup, pencilsGroup, pencilHitClient); if(defaults.showHealth) { clientsGroup.forEach(function(client) { game.debug.geom(new Phaser.Rectangle(client.x, client.y - 16, Math.max(0, tilemap.tileWidth * (client.health / defaults.health)), 7), '#00ff00', true); }); }}With the desk update functions moved to this:var deskUpdate = function() { var desk = this; clientsGroup.forEachAlive(function(theClient) { // get closest client if (!desk.currentTarget || desk.currentTarget && game.physics.arcade.distanceBetween(desk, theClient) < game.physics.arcade.distanceBetween(desk, desk.currentTarget)) desk.currentTarget = theClient; // other ideas for targeting: least health, most health, slowest, fastest }); if (desk.currentTarget && desk.currentTarget.health > 0 && game.physics.arcade.distanceBetween(desk, desk.currentTarget) < desk.range) { if (game.time.now > desk.nextFire) { desk.nextFire = game.time.now + desk.fireRate; var pencil = pencilsGroup.getFirstExists(false); pencil.reset(desk.x, desk.y); pencil.lifespan = desk.fireLife; pencil.rotation = game.physics.arcade.moveToObject(pencil, desk.currentTarget, desk.fireSpeed); } }}As you can see, the only difference is that I've removed the desksGroup.forEach() code block, and created an updateDesk function. Elsewhere in the code, where I instantiate the "desk" Sprites, I have the following line:desk.update = deskUpdate;I was thinking that by adding the deskUpdate function to each desk Sprite, it would save some overhead (since the .update() gets called automatically, instead of my explicitly looping through the group via forEach). However, when I use this deskUpdate function, my FPS on mobile drops from ~40fps to ~4fps. I also tried using an regular function declaration (function deskUpdate() {...}), and had the same performance loss. Link to comment Share on other sites More sharing options...
msha Posted January 14, 2015 Share Posted January 14, 2015 desk.update = deskUpdate;I was thinking that by adding the deskUpdate function to each desk Sprite, it would save some overhead (since the .update() gets called automatically, instead of my explicitly looping through the group via forEach). However, when I use this deskUpdate function, my FPS on mobile drops from ~40fps to ~4fps. I also tried using an regular function declaration (function deskUpdate() {...}), and had the same performance loss. I'm not new to game design, but I'm new to Phaser.io and dealing with browser-based game performance, so any insight would be greatly appreciated! That function gets called for each desk, but it still loops through all desks - "desksGroup.forEach(function(desk) { ...". You need to check for collisions only between "this" and the clients. tralf 1 Link to comment Share on other sites More sharing options...
producerism Posted January 14, 2015 Author Share Posted January 14, 2015 Thanks for the response -- I actually made sure that I wasn't going through that loop for each desk, I must have copied/pasted incorrectly in my original post. I've updated the original post to show the correct deskUpdate function I added. Link to comment Share on other sites More sharing options...
xerver Posted January 14, 2015 Share Posted January 14, 2015 I wouldn't use ".forEach" in a hot function, definitely don't create closures. Just use a normal for-loop. Link to comment Share on other sites More sharing options...
producerism Posted January 14, 2015 Author Share Posted January 14, 2015 Thanks, I replaced all forEach with standard for loops.GameState.prototype.update = function() { // kill any pencils that hit a wall game.physics.arcade.overlap(layerMap, pencilsGroup, pencilHitWall); game.physics.arcade.overlap(clientsGroup, pencilsGroup, pencilHitClient); for (var deskIndex = 0; deskIndex < desksGroup.length; deskIndex++) { var desk = desksGroup.children[deskIndex]; for (var clientIndex = 0; clientIndex < clientsGroup.length; clientIndex++) { var client = clientsGroup.children[clientIndex]; // get closest client if (!desk.currentTarget || desk.currentTarget && game.physics.arcade.distanceBetween(desk, client) < game.physics.arcade.distanceBetween(desk, desk.currentTarget)) desk.currentTarget = client; // other ideas for targeting: least health, most health, slowest, fastest } if (desk.currentTarget && desk.currentTarget.health > 0 && game.physics.arcade.distanceBetween(desk, desk.currentTarget) < desk.range) { if (game.time.now > desk.nextFire) { desk.nextFire = game.time.now + desk.fireRate; var pencil = pencilsGroup.getFirstExists(false); pencil.reset(desk.x, desk.y); pencil.lifespan = desk.fireLife; pencil.rotation = game.physics.arcade.moveToObject(pencil, desk.currentTarget, desk.fireSpeed); } } }}But I'm still curious why I have such a huge performance hit, if I remove the for-loop in the game loop, and instead put the code into the update function of each sprite. e.g.desk.update = deskUpdate; (this is actually placed in game.create() where each desk is instantiated)function deskUpdate() { var desk = this; for (var clientIndex = 0; clientIndex < clientsGroup.length; clientIndex++) { var client = clientsGroup.children[clientIndex]; // get closest client if (!desk.currentTarget || desk.currentTarget && game.physics.arcade.distanceBetween(desk, client) < game.physics.arcade.distanceBetween(desk, desk.currentTarget)) desk.currentTarget = client; // other ideas for targeting: least health, most health, slowest, fastest } if (desk.currentTarget && desk.currentTarget.health > 0 && game.physics.arcade.distanceBetween(desk, desk.currentTarget) < desk.range) { if (game.time.now > desk.nextFire) { desk.nextFire = game.time.now + desk.fireRate; var pencil = pencilsGroup.getFirstExists(false); pencil.reset(desk.x, desk.y); pencil.lifespan = desk.fireLife; pencil.rotation = game.physics.arcade.moveToObject(pencil, desk.currentTarget, desk.fireSpeed); } }} Link to comment Share on other sites More sharing options...
clark Posted January 14, 2015 Share Posted January 14, 2015 So is there never really a good time to use forEach? I also avoided them in AS3. Link to comment Share on other sites More sharing options...
kishigo Posted February 7, 2015 Share Posted February 7, 2015 I'm just a newbie but could it be that the number of times the tweening engine has to be called is now increased?Just a guess but your original code looks something like:tween calls update, update processes all sprites. New code:tween computes a tween and calls each sprite's update. Suppose you have 100 sprites. Now there are 100 more callbacks. Link to comment Share on other sites More sharing options...
valueerror Posted February 8, 2015 Share Posted February 8, 2015 is there a general conclusion you can make for all phaser users like : don't do this or don't do that in your update loop to not kill the game performance .. ??? thx Link to comment Share on other sites More sharing options...
Recommended Posts