Jump to content

Anonymous vs Inline vs Declared Functions (huge difference in FPS performance)


producerism
 Share

Recommended Posts

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

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.

Link to comment
Share on other sites

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

  • 4 weeks later...

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

 Share

  • Recently Browsing   0 members

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