Sign in to follow this  
gregkarber

Game lags over time: help with efficiency!

Recommended Posts

I made a game called The Space Beyond and it starts out really fluid and smooth but if you play for a couple minutes the framerate drops and it gets really laggy.

 

I'm already (1) killing and reseting the asteroids from a pool, (2) killing any asteroids that get too far away, and (3) there's never more than like 30 sprites at any given time. But still it goes so slow.

 

I can't figure out why that is. I'm sure there's something I'm missing, but I am an amateur coder and don't really know what I'm doing. I have copy-pasted the complete code for the game below.

 

If anybody could figure this out, I would be very very very grateful. Thank you very much.

 

GgQOKkI.png

var displayGroup;var sprite;var shapeSprite;var cursors;var fuelRatio = {"value":1};var fuelBar;var beenhere = 0;var shipCollisionGroup;var asteroidCollisionGroup;var asteroid;var spacerocks;var threshold = {"x": 0, "y":0};var loss = false;var reset = {"x":0, "y":0};var textdisplay;var fuelLoc = {"x":0, "y":0};var dial;var fuelPodSprite;var refilling = false;var asteroidCircle;var asteroidGroup;var fuelPodCircle;var storyArray;var storyBoard;var score = 0;var newMessage = "";var returnToNormal = true;var music;var game = new Phaser.Game(700, 500, Phaser.CANVAS, 'game_div', this);var game_state = {};game_state.preload = function() { };game_state.preload.prototype = {preload: function() {	game.world.setBounds(0, 0, 800*100, 600*100);	game.stage.backgroundColor = "#000000";	game.load.image('title', 'title.png');},create: function () {		game.state.start('load'); }}game_state.load = function() { };game_state.load.prototype = {preload: function() {	this.titleSprite = game.add.sprite(53, 170, 'title');	this.game.load.setPreloadSprite(this.titleSprite);    this.game.load.image('ship', 'ship.png');    this.game.load.audio('music', ['LOVELOST.ogg', 'LOVELOST.mp3']);    this.game.load.image('square', 'square.png');    this.game.load.image('dial', 'dial.png');},create: function () {    music = game.add.audio('music');    music.play();	game.state.start('menu'); }}game_state.menu = function() { };game_state.menu.prototype = {	preload: function() {			},		create: function() {				this.game.input.keyboard.callbackContext = this;		this.storyStyle =  { font: "30px Courier", fill: "#FFFFFF" };		this.loadinstructions = game.add.text(155, 300, "Press any key to play", this.storyStyle);		game.input.keyboard.addKeyCapture(Phaser.Keyboard.SPACEBAR);		this.loadinstructions.alpha = 0;		game.add.tween(this.loadinstructions).to({"alpha": 1}, 1500, Phaser.Easing.Linear.None, true);		this.game.input.keyboard.onDownCallback = this.fadeOut;		this.titleSprite = game.add.sprite(53, 170, 'title');		},		fadeOut: function() {			this.game.input.keyboard.onDownCallback = null;			this.fade = game.add.tween(this.game.world).to({"alpha": 0}, 500, Phaser.Easing.Linear.None, true);			this.fade.onComplete.add(this.runGame);	},		runGame: function() {		game.state.start('main'); 	}}game_state.main = function() { };game_state.main.prototype = {preload: function() {	},	create: function() {	this.justonce = 0;    game.physics.startSystem(Phaser.Physics.P2JS);    //spacerocks = game.add.group();    displayGroup = game.add.group();        game.physics.p2.restitution = .01;    //game.physics.p2.applyDamping = true;    	shipCollisionGroup = game.physics.p2.createCollisionGroup();    asteroidCollisionGroup = game.physics.p2.createCollisionGroup();    fuelPodCollisionGroup = game.physics.p2.createCollisionGroup();		beenhere = 0;    //  This will run in Canvas mode, so let's gain a little speed and display    game.renderer.clearBeforeRender = true;    game.renderer.roundPixels = true;    	fuelBar = game.add.graphics(0, 0);    fuelBar.fixedToCamera = true;    displayGroup.add(fuelBar);    //  Our player ship    this.sprite = this.game.add.sprite(400*100, 300*100, 'ship');    this.sprite.anchor.set(0.5);    this.sprite.angle = 90;    this.sprite.scale.x = .5;    this.sprite.scale.y = .5;        game.physics.enable(this.sprite, Phaser.Physics.P2JS);    	this.sprite.body.clearShapes();    this.sprite.body.addPolygon({}, [25, 15, 12, 50, 38, 50]);        this.sprite.body.setCollisionGroup(shipCollisionGroup);	    this.sprite.body.damping = .1;    this.sprite.body.angularDamping = .99;    this.sprite.anchor.setTo(0.5,0.65);        this.sprite.name = 'sprite';        //sprite.body.debug = true;        this.sprite.body.onBeginContact.add(this.gotHit, this);        this.sprite.body.collides(asteroidCollisionGroup);    //sprite.body.collides(fuelPodCollisionGroup);    threshold.x = this.sprite.x;    threshold.y = this.sprite.y;    cursors = game.input.keyboard.createCursorKeys();	spacebar = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);	game.input.keyboard.addKeyCapture(Phaser.Keyboard.SPACEBAR);        game.camera.follow(this.sprite);            this.circleCreate();        this.asteroidGen();        	fuelPodCircle = game.add.graphics(0, 0);    this.fuelPod(this.sprite.x+15, this.sprite.y+15);            this.fuelArrowSpawn();    this.fuelRemaining();        this.storyInit();    this.boardInit();        game.physics.p2.setPostBroadphaseCallback(this.checkOverlap, this);        game.add.tween(game.world).to({"alpha": 1}, 500, Phaser.Easing.Linear.None, true);},checkOverlap: function(body1, body2) {    if ((body1.sprite.name === 'sprite' && body2.sprite.name === 'fuelPodSprite') || (body2.sprite.name === 'fuelPodSprite' && body1.sprite.name === 'sprite')){        this.fuelGet();  //whatever you need on overlap        return false;    }    return true;},asteroidGen: function(dir) {	console.log("dir: " + dir);	var xer = 0;	var yer = 0;		// WRITE CODE TO MAP AN ARRAY AND THEN PLACE THEM IN THE ARRAY!		var doubleCheck = [];	var newPoint = "";		for (var b = 0; b < 10; b++) {		if (dir == "right") {			xer = this.sprite.x + 400 + Math.floor(Math.random() * 800/100)*100;			yer = this.sprite.y - 300 +  Math.floor(Math.random() * 1200/100)*100;		}		else if (dir == "down") {			xer = this.sprite.x - 1200 + Math.floor( Math.random() * 1600/100)*100;			yer = this.sprite.y + 300 +  Math.floor(Math.random() * 600/100)*100;		}		else if (dir == "left") {			xer = this.sprite.x - 400 -  Math.floor(Math.random() * 800/100)*100;			yer = this.sprite.y - 900 +  Math.floor(Math.random() * 1200/100)*100;		}		else if (dir == "up") {			xer = this.sprite.x - 400 +  Math.floor(Math.random() * 1600/100)*100;			yer = this.sprite.y - 300 -  Math.floor(Math.random() * 600/100)*100;		}		else {			xer = this.sprite.x - 800 +  Math.floor(Math.random() * 1600/100)*100;			yer = this.sprite.y - 600 +  Math.floor(Math.random() * 1200/100)*100;			newPoint = xer + "," + yer;			if ((doubleCheck.indexOf(newPoint) == -1) && ( (Math.abs(xer - this.sprite.x) > 50) && (Math.abs(yer - this.sprite.y) > 50) )) {				this.asteroids(xer, yer);				doubleCheck.push(newPoint);			}			xer = this.sprite.x - 800 +  Math.floor(Math.random() * 1600/100)*100;			yer = this.sprite.y - 600 +  Math.floor(Math.random() * 1200/100)*100;		}		newPoint = xer + "," + yer;		if ((doubleCheck.indexOf(newPoint) == -1) && ( (Math.abs(xer - this.sprite.x) > 50) && (Math.abs(yer - this.sprite.y) > 50) )) {			this.asteroids(xer, yer);			doubleCheck.push(newPoint);		}	}	console.log("actually placed:" + doubleCheck.length);	//asteroidGroup.forEachAlive(constrainVelocity, this, 25);},toRadians:function (angle) {  return angle / (180 / Math.PI);},explode: function() {		var explosion = game.add.emitter(this.sprite.x, this.sprite.y);		explosion.makeParticles('square');		explosion.setAlpha(1, 0, 5000, Phaser.Easing.Quintic.None);		explosion.setScale(.2, 1, .2, 1, 0);		explosion.setRotation(500,1000);		explosionarea = new Phaser.Rectangle(-1, -1, 2, 2);		explosion.gravity = 0;		explosion.area = explosionarea;		explosion.start(true, 5000, null, 20);		explosion.update();},fuelGet: function() {	console.log("fuelPodSprite.x: " + fuelPodSprite.x);	this.storyTeller();	if (this.instruct) this.instruct.text = "";	score++;	var distance = this.scoreCalc();	var moveX = Math.random() * distance;	var moveY = Math.sqrt(Math.pow(distance, 2) - Math.pow(moveX, 2));	if (Math.floor(Math.random() * 2) == 0) moveY = 0 - moveY;	returnToNormal = false;	fuelPodSprite.body.x = fuelPodSprite.body.x + moveX;	fuelPodSprite.body.y = fuelPodSprite.body.y + moveY;	fuelPodSprite.body.velocity.x = 0;	fuelPodSprite.body.velocity.y = 0;	fuelPodSprite.angle = 0;    displayGroup.bringToTop(fuelBar);	game.add.tween(fuelRatio).to({"value":1}, 500, Phaser.Easing.Linear.None, true);	var rotateTween = game.add.tween(dial).to({"rotation":Math.atan2(fuelLoc.y - this.sprite.y, fuelLoc.x - this.sprite.x)}, 250, Phaser.Easing.Linear.None, true);		rotateTween.onComplete.add(this.dialMan);	refilling = true;	fuelLoc.x = fuelPodSprite.body.x;	fuelLoc.y = fuelPodSprite.body.y;	console.log("fuelPodSprite.x: " + fuelPodSprite.x);	},dialMan: function() {	returnToNormal = true;},scoreCalc: function() {	if (score == 1) return 250;	else if (score == 2) return 500;	else if (score == 3) return 750;	else if (score == 4) return 1000;	else if (score == 5) return 1250;	else if (score == 6) return 1500;	else if (score == 7) return 1750;	else if (score == 8) return 2000;	else if (score == 9) return 2250;	else return 2500;},boardInit: function() {	this.storyStyle =  { font: "25px Courier", fill: "#FFFFFF" };		storyBoard = game.add.text(45, 35, "", this.storyStyle);	storyBoard.setShadow(3, 3, '#000000', 5);	storyBoard.lineheight = 40;	storyBoard.fixedToCamera = true;},storyInit: function() {	storyArray = [	"They left you behind",	"So did he",	"Now you’re lost",	"In the space beyond",	"Your memories and your dreams",	"In your dreams, he still smiles,",	"But your future is your past",	"Lost forever",	"Even if you made it home,",	"It’s just another empty rock",	"In an empty space.",	"Some dreams fade in the light.",	"But if you can make it one more night,",	"You’ll make it one more night.",	];},storyTeller: function() {		if (storyArray.length > 0) {		var newText = storyArray.splice(0,1);		console.log(newText[0]);		this.newTrans(newText[0]);	} else this.newTrans("You'll make it one more night.");	},thrustExhaust: function() {		var exhaust = game.add.emitter(this.sprite.x, this.sprite.y);		exhaust.makeParticles('square');		exhaust.setScale(.2, .3, .2, .3, 0);		exhaust.setRotation(500,1000);		//console.log("rotations in pi: " + sprite.rotation/3.141592);		var theangle = this.toRadians(this.sprite.angle);		var xVec = 0 - Math.sin(theangle);		var yVec = Math.cos(theangle);		exhaust.setAlpha(1, 0, 500, Phaser.Easing.Quadratic.None);		exhaust.x = this.sprite.x+xVec*20;		exhaust.y = this.sprite.y + yVec*20;		exhaustport = new Phaser.Rectangle(-5, -5, 10, 10);		exhaust.area = exhaustport;		//console.log("Thrust X, Y: " + Math.cos(sprite.rotation) * 300 + "," + Math.sin(sprite.rotation) * 300);		var yThrust = yVec * 300 + (this.sprite.body.velocity.y);		var xThrust = xVec * 300 + (this.sprite.body.velocity.x);		exhaust.setYSpeed(yThrust - 100, yThrust + 100);		exhaust.setXSpeed(xThrust - 100, xThrust + 100);		exhaust.gravity = 0;		exhaust.start(true, 500, null, 25);		exhaust.update();},constantGen: function() {	//console.log("constantGen");	if ((threshold.x + 400) < this.sprite.x) {this.asteroidGen("right"); threshold.x = this.sprite.x; this.killAsteroids();}	if ((threshold.x - 400) > this.sprite.x) {this.asteroidGen("left"); threshold.x = this.sprite.x; this.killAsteroids();}	if ((threshold.y + 300) < this.sprite.y) {this.asteroidGen("down"); threshold.y = this.sprite.y; this.killAsteroids();}	if ((threshold.y - 300) > this.sprite.y) {this.asteroidGen("up"); threshold.y = this.sprite.y; this.killAsteroids();}},killAsteroids: function() {	console.log("asteroid count: " + asteroidGroup.countLiving());	asteroidGroup.forEachAlive(this.destruction, this);},destructionForSure: function(rock) {	rock.kill();},destruction: function(rock) {	if ((rock.x < (threshold.x - 1000)) || (rock.x > (threshold.x + 1000)) || (rock.y > (threshold.y + 1000)) || (rock.y < (threshold.y - 1000))) {		rock.kill();	}	else {		//this.constrainVelocity(rock, 10);	}},constrainVelocity: function(sprite, maxVelocity) {	console.log("starting constraint " + maxVelocity);  var body = sprite.body  var angle, currVelocitySqr, vx, vy;  vx = body.data.velocity[0];  vy = body.data.velocity[1];  currVelocitySqr = vx * vx + vy * vy;  if (currVelocitySqr > maxVelocity * maxVelocity) {    angle = Math.atan2(vy, vx);    vx = Math.cos(angle) * maxVelocity;    vy = Math.sin(angle) * maxVelocity;    body.data.velocity[0] = vx;    body.data.velocity[1] = vy;    console.log('limited speed to: '+maxVelocity);  }},controls: function() {    if (cursors.up.isDown)    {        if (fuelRatio.value > 0) {        	fuelRatio.value -= .004;       		this.sprite.body.thrust(200);        	this.thrustExhaust();	        }        this.fuelRemaining();    }    else    {    }    if (cursors.left.isDown)    {        this.sprite.body.angularVelocity = -4;    }    else if (cursors.right.isDown)    {        this.sprite.body.angularVelocity = 4;    }    else    {    }},update: function() {	if (loss == false) this.controls();	    this.constantGen();    	if (spacebar.isDown) {		if ((this.lockSpace != true) && (loss == true)) this.reStart();		else if (fuelRatio.value < 0) {				this.explode();				this.fail();				this.lockSpace = true;				console.log("explode");		}	} else if (this.lockSpace == true) {this.lockSpace = false; console.log("unlocking");}	if (returnToNormal == true) this.fuelArrow();		if (refilling == true) this.fuelRemaining();},fuelArrowSpawn: function() {	dial = game.add.sprite(650, 50, 'dial'); // 750    dial.anchor.set(0.5);    dial.angle = -90;    dial.scale.x = .5;    dial.scale.y = .5;    dial.fixedToCamera = true;},fuelArrow: function() {	//console.log(Math.atan(fuelLoc.y - sprite.y, fuelLoc.x - sprite.x));	fuelLoc.x = fuelPodSprite.x;	fuelLoc.y = fuelPodSprite.y;	dial.rotation = Math.atan2(fuelLoc.y - this.sprite.y, fuelLoc.x - this.sprite.x);	 },fuelPod: function(x, y) {	fuelPodCircle = game.add.graphics(0, 0);	fuelLoc.x = x;	fuelLoc.y = y;	fuelPodCircle.beginFill('0xCC9999');	fuelPodCircle.lineStyle(1, 0xCC9999);	fuelPodCircle.drawRoundedRect(0, 0, 10, 15, 2);		fuelPodSprite = game.add.sprite(x, y);	game.physics.p2.enable(fuelPodSprite, false);	fuelPodSprite.addChild(fuelPodCircle);		fuelPodSprite.body.clearShapes();	fuelPodSprite.anchor.x = .5;	fuelPodSprite.anchor.y = .5;	fuelPodSprite.body.addCapsule(4, 5, 5, 8, 3.14/2);		//fuelPodSprite.body.debug = true;		fuelPodSprite.name = 'fuelPodSprite';	fuelPodSprite.body.collides([asteroidCollisionGroup, shipCollisionGroup]);	fuelPodSprite.body.setCollisionGroup(fuelPodCollisionGroup);		displayGroup.add(fuelPodSprite);	},circleCreate: function() {	var circleSize = Math.random() * 50 + 60;		asteroidCircle = game.add.bitmapData(100, 100, "asteroidBit", true);	asteroidCircle.circle(50, 50, 50, "#FFFFFF")		asteroidGroup = game.add.group();	asteroidGroup.createMultiple(100, asteroidCircle);	},asteroids: function(x, y) {	//console.log("asteroid at " + x + "," + y);		var circleSize = Math.random() * .5 + .5;		//asteroidGroup.forEachAlive(checkOverlap, this, x, y);	asteroid = asteroidGroup.getFirstDead();	asteroid.scale.x = circleSize;	asteroid.scale.y = circleSize;	asteroid.reset(x,y);	asteroid.anchor.x = .5;	asteroid.anchor.y = .5;		console.log("?" + asteroid.body);		if (asteroid.body == null) {			game.physics.p2.enable(asteroid, false);	   		asteroid.body.clearShapes();		asteroid.body.addCircle(circleSize * 50);		asteroid.body.setCollisionGroup(asteroidCollisionGroup);		asteroid.body.collides([shipCollisionGroup, asteroidCollisionGroup, fuelPodCollisionGroup]);		//asteroid.body.debug = true;		asteroid.body.damping = .1;		}		asteroid.body.velocity.x = Math.random() * 30 - 15;	asteroid.body.velocity.y = Math.random() * 30 - 15;			//this.spacerocks.add(asteroid);	},gotHit: function(body) {	if (body.sprite.name != "fuelPodSprite"){ 		this.explode();		this.fail();	} else this.fuelGet();	},newTrans: function(newWords) {	var fadeOut = game.add.tween(storyBoard).to({alpha:0}, 200, Phaser.Easing.Linear.None, true);	fadeOut.onComplete.add(this.newBoard);	newMessage = newWords;},newBoard: function() {	storyBoard.text = newMessage;	game.add.tween(storyBoard).to({alpha:1}, 600, Phaser.Easing.Linear.None, true, 500);},fail: function() {	reset.x = this.sprite.x;	reset.y = this.sprite.y;	this.sprite.kill();	var message = this.finalMessage();	this.newTrans(message);	this.instruct = game.add.text(45, 65, "Press spacebar to try again.", this.storyStyle);	this.instruct.alpha = 0;	this.instruct.setShadow(3, 3, '#000000', 5);	this.instruct.lineheight = 40;	this.instruct.fixedToCamera = true;	game.add.tween(this.instruct).to({"alpha": 1}, 500, Phaser.Easing.Linear.None, true, 2000);	loss = true;	},finalMessage: function() {	var closingThoughts = ["Death is the final dream.",							"You die with his face on your mind.",							"The air explodes from your lungs.",							"You are forgotten.",							"The inevitable happens."];								if (this.instruct) this.instruct.text = "";								return closingThoughts[Math.floor(Math.random() * closingThoughts.length)];},reStart: function() {	this.fade = game.add.tween(this.game.world).to({"alpha": 0}, 1000, Phaser.Easing.Linear.None, true, 100);	this.fade.onComplete.add(this.reStart2, this);},reStart2: function() {	asteroidGroup.forEachAlive(this.destructionForSure, this);	loss = false;	score = 0;	fuelRatio.value = 1;	//this.sprite.reset(reset.x, reset.y);	this.storyInit();	storyBoard.text = " ";	this.fuelRemaining();	game.state.start('main'); },fuelRemaining: function() {	if (fuelRatio.value <= 0) {		fuelBar.clear();		if (this.justonce != 1) {			this.newTrans("You run out of fuel.");			this.instruct = game.add.text(45, 65, "Press spacebar to self-destruct.", this.storyStyle);			this.instruct.alpha = 0;			this.instruct.setShadow(3, 3, '#000000', 5);			this.instruct.lineheight = 40;			this.instruct.fixedToCamera = true;			game.add.tween(this.instruct).to({"alpha": 1}, 500, Phaser.Easing.Linear.None, true, 2000);			this.justonce = 1;			this.lockSpace = true;		}	} else {		//console.log(fuelRatio);		fuelBar.clear();    	fuelBar.beginFill(0xFFFFFF);    	fuelBar.lineStyle(10, 0xFFFFFF, 1);    	fuelBar.drawRect(100, 450, 500*fuelRatio.value, 10); // 100, 550, 600    	if (fuelRatio.value == 1) refilling = false;    }},render: function() {}}game_state.end = function() { };game_state.end.prototype = {	preload: function() {			},		create: function() {		},		update: function() {		}}game.state.add('preload', game_state.preload);game.state.add('load', game_state.load); game.state.add('menu', game_state.menu); game.state.add('main', game_state.main);game.state.add('end', game_state.end); game.state.start('preload'); 

Thank you very much for any assistance that anyone is able to offer.

 

I also hope you enjoy the game if you play it!

Share this post


Link to post
Share on other sites

I love this game! The music and the sparse graphics mesh nicely. I thought the fuel meter was ground for like a second. The typography of the story has a nice shadow on it, good legibility while avoiding asteroids.

 

Don't make an emitter every time the ship thrusts, in thrustExhaust. Make one and turn it on and off by setting its "on" property to true and false. Each one of those emitters is making a bunch of sprites, which you then don't re-use because you made another one. That's probably your biggest performance issue.

 

There are a bunch of places where you create new tweens instead of re-using them. You might be able to re-use them. Every call to "game.add.tween" is a potential place for re-use.

 

Same with the text, in some places. Again, you could probably make all the text objects in create for you game state and then re-use them.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.