producerism

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

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.

Share this post


Link to post
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.

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
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);		}	}}

Share this post


Link to post
Share on other sites

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.

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.