# My game works, but I want it to work faster

## Recommended Posts

I'm working on a game called "The Space Between" for a gamejam, and I have a problem with memory efficiency. I'm drawing asteroids as circles and destroying them when I don't need them, but it still starts to lag a bit as the game goes on.

How can I make it faster and smoother?

Here is a link to the game, and copy-pasted below is the part that deals with asteroid creation and, below that, the entire source code (all 382 lines of it).

I don't really know anything about memory allocation, so I'm sure I'm making some pretty elementary errors. (Also, there's no loading screen yet, so give it a minute to pop up!)

Asteroid Creation/Destruction Code:

`// This function is called in update and constantly checks to see if certain thresholds are passed.// If they are, it generates a rectangular area of asteroids and calls a function to destroy others.function constantGen() {	if ((threshold.x + 400) < sprite.x) {asteroidGen("right"); threshold.x = sprite.x; killAsteroids();}	if ((threshold.x - 400) > sprite.x) {asteroidGen("left"); threshold.x = sprite.x; killAsteroids();}	if ((threshold.y + 300) < sprite.y) {asteroidGen("down"); threshold.y = sprite.y; killAsteroids();}	if ((threshold.y - 300) > sprite.y) {asteroidGen("up"); threshold.y = sprite.y; killAsteroids();}}// This is the function that generates asteroids when the ship gets to the edge of an areafunction asteroidGen(dir) {	console.log("dir: " + dir);	var xer = 0;	var yer = 0;		for (var b = 0; b < 10; b++) {		if (dir == "right") {			xer = sprite.x + 400 + Math.random() * 800;			yer = sprite.y - 300 + Math.random() * 1200;		}		else if (dir == "down") {			xer = sprite.x - 1200 + Math.random() * 1600;			yer = sprite.y + 300 + Math.random() * 600;		}		else if (dir == "left") {			xer = sprite.x - 400 - Math.random() * 800;			yer = sprite.y - 900 + Math.random() * 1200;		}		else if (dir == "up") {			xer = sprite.x - 400 + Math.random() * 1600;			yer = sprite.y - 300 - Math.random() * 600;		}		else {			xer = sprite.x - 800 + Math.random() * 1600;			yer = sprite.y - 600 + Math.random() * 1200;			asteroids(xer, yer);			xer = sprite.x - 800 + Math.random() * 1600;			yer = sprite.y - 600 + Math.random() * 1200;		}		asteroids(xer, yer);	}	}// This is the function that sends all of the asteroid sprites to a destruction functionfunction killAsteroids() {	console.log("spacerock count: " + spacerocks.length);	spacerocks.forEachAlive(destruction, this);	console.log("spacerock count after destruction: " + spacerocks.length);}// This function checks to see if asteroids are too far away and destroys themfunction destruction(rock) {	//console.log("check for destruction");	if ((rock.x < (threshold.x - 1000)) || (rock.x > (threshold.x + 1000)) || (rock.y > (threshold.y + 1000)) || (rock.y < (threshold.y - 1000))) {		rock.body.destroy();		rock.destroy();		//console.log("destroyed?");	};}// This is the function that creates an asteroidfunction asteroids(x, y) {	//console.log("asteroid at " + x + "," + y);		var circleSize = Math.random() * 50 + 60;		var circles = game.add.graphics(0, 0);	circles.beginFill('0xffffff');	circles.lineStyle(1, 0xffffff);	circles.drawCircle(0, 0, circleSize);	circles.endFill();		asteroid = game.add.sprite(x, y);	game.physics.p2.enable(asteroid, false);		asteroid.addChild(circles);		asteroid.body.addCircle(circleSize/3);	asteroid.body.setCollisionGroup(asteroidCollisionGroup);	asteroid.body.collides([shipCollisionGroup, asteroidCollisionGroup]);		asteroid.body.velocity.x = Math.random() * 50 - 25;	asteroid.body.velocity.y = Math.random() * 50 - 25;		this.spacerocks.add(asteroid);	}`

Full Source:

`var game = new Phaser.Game(800, 600, Phaser.CANVAS, 'game_div', { preload: preload, create: create, update: update, render: render });function preload() {    game.load.image('ship', 'ship.png');    game.load.audio('music', 'music.wav');    game.load.image('square', 'square.png');    game.load.image('dial', 'dial.png');	game.world.setBounds(0, 0, 800*100, 600*100);}var sprite;var shapeSprite;var cursors;var fuelRatio = {"value":1};var graphics;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;function create() {	    music = game.add.audio('music');    music.play();	game.stage.backgroundColor = "#000000";    game.physics.startSystem(Phaser.Physics.P2JS);        spacerocks = game.add.group();    game.physics.p2.setImpactEvents(true);    game.physics.p2.restitution = 0.8;    	shipCollisionGroup = game.physics.p2.createCollisionGroup();    asteroidCollisionGroup = 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;    //  We need arcade physics    	graphics = game.add.graphics(0, 0);    graphics.fixedToCamera = true;            fuelPod(400*100, 300*100);    //  Our player ship    sprite = game.add.sprite(400*100, 300*100, 'ship');    sprite.anchor.set(0.5);    sprite.angle = 90;    sprite.scale.x = .5;    sprite.scale.y = .5;        //  and its physics settings    game.physics.enable(sprite, Phaser.Physics.P2JS);        sprite.body.addCircle(5);        sprite.body.setCollisionGroup(shipCollisionGroup);    sprite.body.damping = .1;    sprite.body.angularDamping = .99;    sprite.anchor.setTo(0.5,0.5);        sprite.name = 'sprite';        sprite.body.collides(asteroidCollisionGroup, gotHit, this);    threshold.x = sprite.x;    threshold.y = sprite.y;    cursors = game.input.keyboard.createCursorKeys();	spacebar = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);        game.camera.follow(sprite);        fuelArrowSpawn();    fuelRemaining();    asteroidGen();}function asteroidGen(dir) {	console.log("dir: " + dir);	var xer = 0;	var yer = 0;		for (var b = 0; b < 10; b++) {		if (dir == "right") {			xer = sprite.x + 400 + Math.random() * 800;			yer = sprite.y - 300 + Math.random() * 1200;		}		else if (dir == "down") {			xer = sprite.x - 1200 + Math.random() * 1600;			yer = sprite.y + 300 + Math.random() * 600;		}		else if (dir == "left") {			xer = sprite.x - 400 - Math.random() * 800;			yer = sprite.y - 900 + Math.random() * 1200;		}		else if (dir == "up") {			xer = sprite.x - 400 + Math.random() * 1600;			yer = sprite.y - 300 - Math.random() * 600;		}		else {			xer = sprite.x - 800 + Math.random() * 1600;			yer = sprite.y - 600 + Math.random() * 1200;			asteroids(xer, yer);			xer = sprite.x - 800 + Math.random() * 1600;			yer = sprite.y - 600 + Math.random() * 1200;		}		asteroids(xer, yer);	}	}function toRadians (angle) {  return angle / (180 / Math.PI);}function explode() {		var explosion = game.add.emitter(sprite.x, sprite.y);		explosion.makeParticles('square');		explosion.setScale(.2, 1, .2, 1, 0);		explosion.setRotation(500,1000);		explosionarea = new Phaser.Rectangle(-1, -1, 2, 2);		explosion.gravity = 0;		explosion.area = this.explosionarea;		explosion.start(true, 10000, null, 20);		explosion.setAlpha(0, 1, 10000, Phaser.Easing.Linear.None );		explosion.update();}function fuelCheck() {	if (Math.sqrt(Math.pow((sprite.x - fuelPodSprite.x), 2) + Math.pow((sprite.y - fuelPodSprite.y), 2)) < 15) fuelGet();}function fuelGet() {	//console.log("fuelPodSprite.x: " + fuelPodSprite.x);	var moveX = Math.random() * 2000 - 1000;	var moveY = Math.random() * 2000 - 1000;	fuelPodSprite.body.x = fuelPodSprite.body.x + moveX;	fuelPodSprite.body.y = fuelPodSprite.body.y + moveY;	game.add.tween(fuelRatio).to({"value":1}, 500, Phaser.Easing.Linear.None, true);	refilling = true;	fuelLoc.x = fuelPodSprite.body.x;	fuelLoc.y = fuelPodSprite.body.y;	console.log("fuelPodSprite.x: " + fuelPodSprite.x);	}function thrustExhaust() {		var exhaust = game.add.emitter(sprite.x, 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 = toRadians(sprite.angle);		var xVec = 0 - Math.sin(theangle);		var yVec = Math.cos(theangle);		exhaust.setAlpha(1, 0, 500, Phaser.Easing.Quadratic.None );		exhaust.x = sprite.x+xVec*20;		exhaust.y = sprite.y + yVec*20;		exhaustport = new Phaser.Rectangle(-5, -5, 10, 10);		exhaust.area = this.exhaustport;		//console.log("Thrust X, Y: " + Math.cos(sprite.rotation) * 300 + "," + Math.sin(sprite.rotation) * 300);		var yThrust = yVec * 300 + (sprite.body.velocity.y);		var xThrust = xVec * 300 + (sprite.body.velocity.x);		exhaust.setYSpeed(yThrust - 100, yThrust + 100);		exhaust.setXSpeed(xThrust - 100, xThrust + 100);		exhaust.gravity = 0;		exhaust.start(true, 5000, null, 25);		exhaust.update();}function constantGen() {	//console.log("constantGen");	if ((threshold.x + 400) < sprite.x) {asteroidGen("right"); threshold.x = sprite.x; killAsteroids();}	if ((threshold.x - 400) > sprite.x) {asteroidGen("left"); threshold.x = sprite.x; killAsteroids();}	if ((threshold.y + 300) < sprite.y) {asteroidGen("down"); threshold.y = sprite.y; killAsteroids();}	if ((threshold.y - 300) > sprite.y) {asteroidGen("up"); threshold.y = sprite.y; killAsteroids();}}function killAsteroids() {	console.log("spacerock count: " + spacerocks.length);	spacerocks.forEachAlive(destruction, this);	console.log("spacerock count after destruction: " + spacerocks.length);}function destructionForSure(rock) {	rock.body.destroy();	rock.kill();}function destruction(rock) {	//console.log("check for destruction");	if ((rock.x < (threshold.x - 1000)) || (rock.x > (threshold.x + 1000)) || (rock.y > (threshold.y + 1000)) || (rock.y < (threshold.y - 1000))) {		rock.body.destroy();		rock.destroy();		//console.log("destroyed?");	};}function constrainVelocity(sprite, 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);  }};function update() {    if (cursors.up.isDown)    {       	sprite.body.thrust(200);       	thrustExhaust();        if (fuelRatio.value > 0) fuelRatio.value -= .002;        fuelRemaining();    }    else    {    }    if (cursors.left.isDown)    {        sprite.body.angularVelocity = -5;    }    else if (cursors.right.isDown)    {        sprite.body.angularVelocity = 5;    }    else    {    }    constrainVelocity(sprite, 200);    //game.world.wrap(sprite.body, 10, false, true, true);    //game.world.wrap(asteroid.body, 10, false, true, true);    constantGen();    	if ((spacebar.isDown) && (loss == true)) reStart();	fuelArrow();	fuelCheck();		if (refilling == true) fuelRemaining();}function fuelArrowSpawn() {	dial = game.add.sprite(750, 50, 'dial');    dial.anchor.set(0.5);    dial.angle = -90;    dial.scale.x = .5;    dial.scale.y = .5;    dial.fixedToCamera = true;}function fuelArrow() {	//console.log(Math.atan(fuelLoc.y - sprite.y, fuelLoc.x - sprite.x));	dial.rotation = Math.atan2(fuelLoc.y - sprite.y, fuelLoc.x - sprite.x); }function fuelPod(x, y) {	var fuelPodCircle = game.add.graphics(0, 0);	fuelLoc.x = x;	fuelLoc.y = y;	fuelPodCircle.beginFill('0xFF0000');	fuelPodCircle.lineStyle(1, 0xFF0000);	fuelPodCircle.drawRoundedRect(0, 0, 10, 15, 2);		fuelPodSprite = game.add.sprite(x, y);	game.physics.p2.enable(fuelPodSprite, false);	fuelPodSprite.addChild(fuelPodCircle);		fuelPodSprite.name = 'fuelPodSprite';		fuelPodSprite.body.collides([asteroidCollisionGroup]);	fuelPodSprite.body.setCollisionGroup(asteroidCollisionGroup);	}function asteroids(x, y) {	//console.log("asteroid at " + x + "," + y);		var circleSize = Math.random() * 50 + 60;		var circles = game.add.graphics(0, 0);	circles.beginFill('0xffffff');	circles.lineStyle(1, 0xffffff);	circles.drawCircle(0, 0, circleSize);	circles.endFill();		asteroid = game.add.sprite(x, y);	game.physics.p2.enable(asteroid, false);		asteroid.addChild(circles);		asteroid.body.addCircle(circleSize/3);	asteroid.body.setCollisionGroup(asteroidCollisionGroup);	asteroid.body.collides([shipCollisionGroup, asteroidCollisionGroup]);		asteroid.body.velocity.x = Math.random() * 50 - 25;	asteroid.body.velocity.y = Math.random() * 50 - 25;		this.spacerocks.add(asteroid);	}function gotHit() {	explode();	fail();}function fail() {	reset.x = sprite.x;	reset.y = sprite.y;	sprite.kill();	var textstyle = { font: "53px Helvetica", fill: "#FFFFFF" };	textdisplay = game.add.text(50, 50, "press spacebar to restart.", textstyle);	textdisplay.fixedToCamera = true;	loss = true;}function reStart() {	fuelRatio.value = 1;	sprite.reset(reset.x, reset.y);	textdisplay.destroy();	textdisplay.text = "";	loss = false;	fuelRemaining();}function fuelRemaining() {	graphics.clear();    graphics.beginFill(0xFFFFFF);    graphics.lineStyle(10, 0xFFFFFF, 1);    graphics.drawRect(100, 550, 600*fuelRatio.value, 10);    if (fuelRatio.value == 1) refilling = false;}function render() {}`

##### Share on other sites

You should create a Pool of asteroids at start (multiple invisible asteroids) and instead of deleting/creating new ones, just select one free from the Pool, mark it as used. Mark it as free instead of destroying it.

##### Share on other sites

I'm pretty sure I know how to do that for sprites with images:

`asteroidGroup = game.add.group();asteroidGroup.createMultiple(200, "asteroid");`

But how do I do it with graphics? Do I need to use Bitmap Data? I haven't used that at all, but I'm studying up on it now. If anybody knows how to do this, it would be an enormous help for me.

Thank you so much!

##### Share on other sites

Maybe it would be easier if I just created a circle graphic rather than drawing them with Graphics. I assumed that drawing them would be faster than displaying a .png, but maybe that's wrong.

##### Share on other sites

And will I need to apply the P2 bodies each time I create one? I'm sorry to keep posting questions.

##### Share on other sites

I think I implemented your suggestion! It still could run a little faster, though...

`function circleCreate() {	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);	}function asteroids(x, y) {	//console.log("asteroid at " + x + "," + y);		var circleSize = Math.random() * .5 + .5;	asteroid = asteroidGroup.getFirstDead();	asteroid.reset(x,y);	asteroid.anchor.x = .5;	asteroid.anchor.y = .5;	asteroid.scale.x = circleSize;	asteroid.scale.y = circleSize;		game.physics.p2.enable(asteroid, false);	    asteroid.body.clearShapes();	asteroid.body.addCircle(circleSize * 50);	asteroid.body.setCollisionGroup(asteroidCollisionGroup);	asteroid.body.collides([shipCollisionGroup, asteroidCollisionGroup]);	asteroid.body.debug = true;	asteroid.body.velocity.x = Math.random() * 50 - 25;	asteroid.body.velocity.y = Math.random() * 50 - 25;			this.spacerocks.add(asteroid);	}`