Jump to content

  • Log In with Google      Sign In   
  • Create Account

Like
9Likes
Dislike

Orbital Debris – Making an HTML5 Game With Phaser

By Wolfgang Graebner | Published May 16 2014 07:43 PM in Game Programming
Peer Reviewed by (jbadams, Orymus3, Servant of the Lord)

html5 javascript chrome mobile 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.

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 with this 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.


Attached Image: gravity_film_still_a_l1-450x253.jpg
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.


Attached Image: stationPosition-e1394677091284.jpg


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 are 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 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.


Attached Image: phaser-450x229.jpg


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.


Attached Image: javascriptProfiler.jpg
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 did 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!

This article was orginally posted to Wolf's blog AllWorkAllPlay - head on over there for some more great content



About the Author(s)


I like making games. Usually for mobile devices and web browsers using Flash, HTML5 / JavaScript & C#

License


GDOL (Gamedev.net Open License)




Comments

I've never made or tried to make a web based game, but when I open up the index.html (all decompressed) there's no textures and just green squares.

@Wolfgang

 

Awesome idea and I like the article too! Did you make all the assets yourself?

 

 

@Joshhua5

 

Phaser stuff needs a back-end to run. If you open the html file statically, the assets won't get served up correctly.

 

It's easy though: If you have python installed, open a command line, navigate to the game folder and type 

 

  python -m SimpleHTTPServer

 

then open a browser and go to 127.0.0.1:8000

 

Or if you prefer node, get node-static:

 

npm install node-static -g

static -p 8000

 

any way to serve up a page will do

 

 

 

[edit]

 

Sorry, that should be 

 

npm install node-static -g

 

of course


Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS