Orbital Debris – Making an HTML5 Game With Phaser

This is Orbital Debris, a small game I made in HTML5 with Phaser for a game jam organized by FGL. It won 3rd place! :) Considering I only had 48 hours and was working with technology I’d never used before, I’m really proud of the result.

Orbital Debris Click To Play

Making Of

I assume you’ve already gone through the Phaser starting tutorials and know how to create a basic game. If this is not the case, please read this and this first. I’m only going to cover the things unique to Orbital Debris.

A link to the souce files (code + art) is included at the end of the article. But be warned, it is game jam code and my first time working with Phaser so it’s far from perfect code.

The Concept

The theme set by FGL was “silent, but deadly”. Which made me think of “in space nobody can hear you scream”. Which made me think back to Gravity, my favorite movie of 2013. And just like that I had my idea within 15 minutes of the jam starting: you play as a space station orbiting the earth and have to dodge space junk released by satellites crashing into each other.

Gravity, The Main Inspiration for Orbital Debris

Gravity, The Main Inspiration for Orbital Debris

Basic Game Logic

Orbiting A Planet

The first thing I did was get some objects orbiting around the earth. Every orbiter is just a Phaser sprite with some special properties that gets added to a group that contains all orbiters.

function spawnNewOrbiter(graphic) {
	var orbiter = game.add.sprite(0, 0, graphic);
	orbiter.anchor.setTo(0.5, 0.5);
	orbiter.moveData = {};
	orbiter.moveData.altitude = 0;
	orbiter.moveData.altitudeTarget = 0;
	orbiter.moveData.altitudeChangeRate = 0;
	orbiter.moveData.altitudeMin = 0;
	orbiter.moveData.altitudeMax = 0;
	orbiter.moveData.orbit = 0;
	orbiter.moveData.orbitRate = 0;
	orbiterGroup.add(orbiter);
	return orbiter;
}

Orbiter.moveData.altitude and orbit describe the orbiter’s current position relative to the planet. The altitude is the distance from the center, and the orbit is how far along its orbit it is. So making the orbiters move is a simple matter of using the values to update them in Phaser’s built-in state update function.

Station Position

So I loop through the group:

function updateOrbiterMovement() {
	orbiterGroup.forEach(function(orbiter) {
		if (orbiter.alive) {
			updateOrbiterAltitude(orbiter);
			updateOrbiterOrbit(orbiter);
		}	
	});
}

And position the orbiters accordingly.

function updateOrbiterOrbit(orbiter) {

	if (orbiter.moveData.orbitRate != 0) {
		orbiter.moveData.orbit += orbiter.moveData.orbitRate;
		if (orbiter.moveData.orbit >= 360) {
			orbiter.moveData.orbit -= 360;
		}
	}

	var oRad = Phaser.Math.degToRad(orbiter.moveData.orbit);
	orbiter.x = game.world.width/2 + orbiter.moveData.altitude * Math.cos(oRad);
	orbiter.y = game.world.height/2 + orbiter.moveData.altitude * Math.sin(oRad);
}

I also set the orbiter’s sprite angle to its orbit so it appears aligned with the planet – except for pieces of space junk that rotate according to a tumble rate out of sync with their orbits.

if (!orbiter.isJunk) {
	orbiter.angle = orbiter.moveData.orbit - 90;
} else {
	orbiter.angle += orbiter.tumbleRate;
}

Player Input

I wanted to force players to keep re-adjusting their altitude to stop them from idling at a fixed altitude, and I also wanted movement to feel quite flow-y. So I came up with a simple acceleration-style system. First I check the keyboard and adjust the altitudeChangeRate accordingly. You can think of altitudeChangeRate as velocity towards / away from the earth.

function processInput() {
	if (game.input.keyboard.isDown(Phaser.Keyboard.UP)) {
		station.moveData.altitudeChangeRate += ALTITUDE_CHANGE_RATE;
	}
	if (game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) {
		station.moveData.altitudeChangeRate -= ALTITUDE_CHANGE_RATE;
	}
	station.moveData.altitudeChangeRate = Phaser.Math.clamp(
		station.moveData.altitudeChangeRate,
		-ALTITUDE_CHANGE_RATE_MAX,
		ALTITUDE_CHANGE_RATE_MAX
	);
}

And then I apply it to orbiters like so:

function updateOrbiterAltitude(orbiter) {
	if (orbiter.moveData.altitudeChangeRate != 0) {
		orbiter.moveData.altitude = Phaser.Math.clamp(
			orbiter.moveData.altitude + orbiter.moveData.altitudeChangeRate,
			orbiter.moveData.altitudeMin,
			orbiter.moveData.altitudeMax
		);
	}
}

Collision

Since all orbiters are added to the same group, a single call to Phaser’s built-in overlap method checks for all collisions:

game.physics.overlap(orbiterGroup, orbiterGroup, onOrbiterOverlap);

All collisions are then checked. Orbiters that space junk are not processed further, because I only want satellites and stations to spawn junk:

function onOrbiterOverlap(orbiterA, orbiterB) {
	if (!orbiterA.isJunk) {
		orbiterWasHit(orbiterA);
	}
	if (!orbiterB.isJunk){
		orbiterWasHit(orbiterB);
	}
}

When a satellite or station is hit, I spawn new pieces of space junk. They are just orbiters (just like the space station or satellites). Their altitude, and orbit direction are based on the satellite or station that was destroyed. When the space station is hit, it’s a bit of a special case: I spawn a lot more junk than usual to make it feel more dramatic and end the game.

function orbiterWasHit(orbiter) {
	if (orbiter.alive) {

		var junkQuantity;
		if (orbiter.isStation) {
			junkQuantity = 40;
		} else {
			junkQuantity = game.rnd.integerInRange(2, 4);
		}

		for (var i = 0; i < junkQuantity; i++) {
			var junk = spawnNewOrbiter(IMAGES_JUNK[game.rnd.integerInRange(0,IMAGES_JUNK.length)]);
			junk.moveData.altitude = orbiter.moveData.altitude;
			junk.moveData.altitudeMin = 60;
			junk.moveData.altitudeMax = 700;
			junk.moveData.altitudeChangeRate = game.rnd.realInRange(-1.0, 1.0);
			junk.moveData.orbit = orbiter.moveData.orbit;
			junk.moveData.orbitRate = game.rnd.realInRange(0.4, 1.2);
			if (orbiter.moveData.orbitRate < 0) {
				junk.moveData.orbitRate *= -1;
			}
			junk.tumbleRate = game.rnd.realInRange(-10, 10);
			junk.isJunk = true;
		}

		if (orbiter.isStation) {
			playerDied();
		}

		orbiter.kill();
	}
}

Remaining Bits & Pieces

That’s pretty much all there is to it! To complete the experience I added some powerups, scaled the difficulty up over time, and added a bunch of hud stuff and music. And then I was out of time and had to submit the game for the competition. If you have any questions about how anything was done that I didn’t explain here, please leave a comment and I’ll get back to you.

Thoughts on HTML5 / JavaScript / Phaser

This was my first time working with the Phaser HTML5 game engine. Usually I avoid unfamiliar technologies during game jams… every hour counts and I don’t have time to start learning new things. It was a risky, but I decided to try it anyways. I liked that 1) it can run in mobile phone web browsers which makes it easy to share 2) it’s similar to flixel which I know inside-out and 3) I already knew some Javascript / HTML5.

Overall I really like the engine, it has a lot of great features and is easy to work with. Phaser gets updated often, which is great because it’s constantly improving. But it also means that a lot has changed from previous versions. This was sometimes frustrating when I would read about how to do a certain thing in an older version of Phaser, which no longer works the same in the latest version. I plan to use it again soon, but for a hobby project without such a stressful deadline so I can spend more time getting to know it. I could see it one day becoming one of my go-to development tools.

Phaser Logo

Performance

I couldn’t get the game running smoothly in my phone’s browser in time for the deadline, so I had to cancel the mobile version. It’s a shame because being able to play the game in mobile phone browsers was the #1 reason I wanted to use Phaser in the first place :( It appears it’s something you really have to keep a close eye on.

I used the Chrome JavaScript profiler to take a peek at what’s taking up most of my processing time. From what I can tell the biggest performance drain is the collision system. Especially when the space station crashes and 30 new pieces of junk are spawned, my iPhone 4S performance slows to a crawl.

JavaScript Profile

Using the Chrome JavaScript Profiler to Check for Performance Issues

Since I was unfamiliar with the engine, and had to no time to clean up my code or learn how to do things properly I know I do a lot of things badly. I’m careless with creating objects and freeing up memory. I could pre-compute some stuff. I could simplify other things. But I didn’t have time to do it. Next time, I’ll do better and keep testing on my phone along the way! :D

Source Files

This is not optimal code. It was my first time working with Phaser. I had a 48 hour deadline. There are lots of things that could should be improved upon. I am including the source files as a download anyways. They are not intended as some sort of model perfect Phaser project. Download at your own risk!

Click To Download Orbital Debris Source Files (32MB ZIP)

Any Questions, Comments or Feedback?

Leave a comment! :)

Leave a Reply

comment-avatar