RedPanduzer Posted November 12, 2015 Share Posted November 12, 2015 We are making a space shooter that should work on desktop browsers. However, this project has now came to face some serious performance issues that shouldn't exist. Game isn't exatly the smallest one but not that large either and yet fps can drop to 20-30fps which really makes our game sort of unplayable. After debugging and performance testing, it seems that there are some issues with memory and GC. Firefox waterfall chart, red ones are all GCAnd this one is from Chrome, white pie is idling and yellow scripting After reading some performance tips, a few things has come to my mind. 1. precached functions.Apparently prototypng is not exactly the fastest way of doing things. However, as we are using Phaser state manager all of our code makes use of prototyping. Can precache functions be used with state manager and if so how? (whole usage of precached functions was a bit over my head). 2. Bullet collisionAs we are using P2 physics for all enemies and payer but wanted to have arcade physics for bullets because it makes coding easier and we thought it would be ver heavy to have every bullet using P2 for no reason. Problem however, was how to test arcade bullet collision with P2 body. To overcome this I used P2 hitTest to test if bullet (for every bullet alive and for all targets) is over body. It worked fine but I heard creating arrays can be memory hog. Is this a totally broken way?for (var iter = 0; iter < 3; iter++) {this.enemies.getChildAt(this.lap-1).getChildAt(iter).forEachAlive(function (en) {bullets.forEachAlive(function ( {boundsBullet = b.world;this.bulletArray = this.game.physics.p2.hitTest(boundsBullet, [en]);if (this.bulletArray.length != 0) {hitDetector(b, en, self.enemyAmount, self.lap,en.getChildAt(0));}});});}3. Local vs thisThere are not that many globals in our game but even more this. defined variables. We use this when we need some value to be kept between update loops. Then there are local variables in update loop that are defined in every time loop runs. From what I have undestood all locals are freed for GC at the end of the loop where this variables of course are not. So, is it bad or good thing to give something to be freed for GC after every update loop or is it this. defined variables that could be reserving so much memory that GC is in trouble? 4. Tween performanceHow heavy tweens are? We have made a lot of our animations with tweens and I started to wonder if that could cause some issues too. Here's a link for git repo if you want take a look on code. All commenting is finnish though..https://github.com/ProjectGenericSpaceGame/RavingSpaceAnd here is a current live versionhttp://student.labranet.jamk.fi/~H3492/RavingSpace/I'm also very aware that current ship controlling is horribly ineffective, might just drop that totally out in future.. Link to comment Share on other sites More sharing options...
goyeneche Posted November 12, 2015 Share Posted November 12, 2015 As we are using P2 physics for all enemies and payer but wanted to have arcade physics for bullets because it makes coding easier and we thought it would be ver heavy to have every bullet using P2 for no reason. Problem however, was how to test arcade bullet collision with P2 body. To overcome this I used P2 hitTest to test if bullet (for every bullet alive and for all targets) is over body. It worked fine but I heard creating arrays can be memory hog. Is this a totally broken way? I have a doubt that maybe someone can answer:I don't know if it's performant to have two physics working, maybe for bullets it's a good idea but you load one more physic engine in the game. Is this better?. One important thing that I can see is that your update() function is big, and have some forEach() with a lot of calculations. The game do that in each frame!I have a similar game may I'll upload the source but it's in a sort of Spanglish.. Link to comment Share on other sites More sharing options...
RedPanduzer Posted November 12, 2015 Author Share Posted November 12, 2015 Thanks for reply. From richs post (http://www.html5gamedevs.com/topic/4518-explaining-phaser-2s-multiple-physics-systems/) I understood that having both should at least to some extend work fine. Update loop really is big indeed. About 100 lines of that is actually about rotating ship correctly (and that's why I mentioned it in original post). For other parts, I really don't know how it could be made smaller AND keep all features in. Next, I'm probably going to limit how often AI function run and see if that has any effect. Btw, average run-time of update-loop per frame is about 2-0.5ms Link to comment Share on other sites More sharing options...
Skeptron Posted November 12, 2015 Share Posted November 12, 2015 Stupid question, but did you trying forcing CANVAS or WEBGL? Canvas is probably faster. Also, you could try to do game.forceSingleUpdate = true. Sometimes this really does some good (doc : http://phaser.io/docs/2.4.4/Phaser.Game.html#forceSingleUpdate ) It seems from the graphs that you have some kind of memory leak : I would suspect a bad management of your objects. Do you keep them in groups and recycle them? What do you do with offscreen objects? Do you kill or destroy them? Link to comment Share on other sites More sharing options...
CtlAltDel Posted November 12, 2015 Share Posted November 12, 2015 Your create functions each foreach and in that again each foreach. Maybe define those functions outside the loop and call them instead of using anonymous functions.You also create an array [en] inside the foreach. You might want to do have one array this.hitTestArray and just this.hitTestArray[0] = en, and pass this.hitTestArray to the p2 check. Both might account for some garbage. Link to comment Share on other sites More sharing options...
RedPanduzer Posted November 12, 2015 Author Share Posted November 12, 2015 I forced CANVAS a while ago when trying to improve performance on FF. That worked a bit but it's now really bad again. Performance is better on chrome (both canvas and webGL) but still not exactly what it should be. I fixed all implicitly declared variables, removed some duplicate declarations and commented out all unused test variables. Also tried reducing AI runs but run into some issues. I'm going to try given ideas tomorrow and report back then. Thank for replies. Link to comment Share on other sites More sharing options...
Mauricio Fidalgo Posted November 13, 2015 Share Posted November 13, 2015 I think you may be creating and destroying objects all over the place on your project, you should consider object pooling so the GC doesn't kill your game FPS this often.Here a nice presentation with the problem and the solution that you are having. Link to comment Share on other sites More sharing options...
RedPanduzer Posted November 13, 2015 Author Share Posted November 13, 2015 One of the problems with this issue has been that there hasn't been a clear reason why objects are getting created as almost all objects are created before starting an actual game. However, there has been a few things that are now fixed.Most important one seemed to be that one the enemies made use of graphics and LineTo method to simulate laser. Though laser got cleared every time new line was drawn, apparently lines and all objects that had something to do with them were kept in memory. This in long run caused rapid drop in fps after certain amount of time had passed. This of course wasn't the only problem is only started to cause issues after enoung lines were drawn. One weird thing was usage of getFirstExists(false) with bullet pool. As bullets are killed when out of bounds instead of destroyed, it made more sense to use getFirstDead(). I don't know if this had any effect on performance though.Also, I tried to make sure that all variables that are no more used are set to be null. Next I will move bullet hit testing inside enemy AI so it won't first go through all enemies and every bullet just go through all enemies to apply AI later. Currently big chunks of code are commented isolate problematic areas. Link to comment Share on other sites More sharing options...
Mauricio Fidalgo Posted November 13, 2015 Share Posted November 13, 2015 One of the reasons that your game may be leaking memory is becouse of anonymous functions inside loops and such things.This piece of code is generating 3 function objects, at every iteration it creates a new one.for (var iter = 0; iter < 3; iter++) {this.enemies.getChildAt(this.lap-1).getChildAt(iter).forEachAlive(function (en) {bullets.forEachAlive(function ( {boundsBullet = b.world;this.bulletArray = this.game.physics.p2.hitTest(boundsBullet, [en]);if (this.bulletArray.length != 0) {hitDetector(b, en, self.enemyAmount, self.lap,en.getChildAt(0));}});});}the same code could be rewriten this way without losing functionality and generating the function only one time.var eachBulletAliveFn = function( { boundsBullet = b.world; this.bulletArray = this.game.physics.p2.hitTest(boundsBullet, [en]); if (this.bulletArray.length != 0) { hitDetector(b, en, self.enemyAmount, self.lap, en.getChildAt(0)); }};var eachEnemyAliveFn = function(en) { bullets.forEachAlive(eachBulletAliveFn);};for (var iter = 0; iter < 3; iter++) { this.enemies.getChildAt(this.lap - 1).getChildAt(iter).forEachAlive(eachEnemtAliveFn);} I hope you find out what is leaking the memory RedPanduzer 1 Link to comment Share on other sites More sharing options...
RedPanduzer Posted November 14, 2015 Author Share Posted November 14, 2015 Thanks for great post. I modified it a bit so that player hit detection can use the same functions and then changed AI functions so that now they are defined only once too. This is what I have now:var eachEnemyAliveFn;var commanderAI;var hunterAI;var destroyerAI;var boundsBullet;if (this.frameSkip == 0) {eachEnemyAliveFn = function(){};commanderAI = function(){};hunterAI = function(){};destroyerAI = function(){};this.frameSkip = 1;} else {this.frameSkip = 2;var enm;var eachBulletAliveFn = function( {boundsBullet = b.world;self.bulletArray = self.game.physics.p2.hitTest(boundsBullet, [enm]);if (self.bulletArray.length != 0 && self.timers[4] == 1) {hitDetector(b, enm, self.enemyAmount, self.lap, enm.getChildAt(0));} else if(self.bulletArray.length != 0 && self.timers[4] == 2){//jos pelaajaan osumista tutkitaanhitDetector(b, enm, null, self.lap, self.HPbar);}};eachEnemyAliveFn = function(en) {enm = en;if(self.timers[4] == 1){self.bullets.forEachAlive(eachBulletAliveFn);} else {self.enemyBullets.forEachAlive(eachBulletAliveFn);}};destroyerAI = function(enemy){eachEnemyAliveFn(enemy);//rest of the AI logic};hunterAI = function(enemy) {eachEnemyAliveFn(enemy);//rest of AI logic};commanderAI = function(enemy) {eachEnemyAliveFn(enemy);//rest of AI logic};}if(this.frameSkip == 2) {// Asteroidin jahtaajan tekoälythis.enemy1.forEachAlive(destroyerAI, this);this.enemy2.forEachAlive(hunterAI, this);this.enemy3.forEachAlive(commanderAI,this);this.frameSkip = 0;}self.timers[4]++;//checking enemy bulletsif(!this.ship.dying) {eachEnemyAliveFn(this.ship);}eachBulletAliveFn = null;eachEnemyAliveFn = null;self.timers[4] = 1;I can't tell exact effects of this as I didn't do performance tests between this and previous way. However, after all these changes it seems that GC can now clean variables way more efficiently and the game is even playable now I would still like to know if anyone has used precached functions with phaser statemanager? It seems like a promising idea. Link to comment Share on other sites More sharing options...
jmp909 Posted November 14, 2015 Share Posted November 14, 2015 Surely doing a hitTest on every bullet separately is going to negate any inherent spatial optimisations that the engine uses to avoid unnecessary hit tests? do you need your collision to be pixel perfect. ? Eg what about setting an invisible rectangular arcade body to move along with the p2 ship position,malthough it does mean you've got *more* physics. That doesn't mean it isn't worth trying though . But yes it would only be AABB collision, so may look wrong depending on the size of body you use and the shape of your ships Link to comment Share on other sites More sharing options...
Recommended Posts