SpaceInvaders

HomePage | RecentChanges | Preferences

Space Invaders

I saw a mention of Mary Rose Cook live-coding a Space Invaders game in JavaScript at Hacker School, and then another reference popped up with a video: https://vimeo.com/105955605

I watched it once through and thought wow, maybe it's NOT too hard! After a Very Unpleasant Experience with Swing a long time ago, I've avoided pretty much everything graphical. This is not quite that, but still.

So I started over, pressed pause a lot, and typed in all the code. It works! (Except it's really sticky about playing the sound and moving at the same time. I probably need a smaller sound file.)

But it raised Questions:

I started with the last item. Adding a console.log statement to print the number of bodies before looping through them to update the screen confirmed that the number just grows and grows.

So I added another filter to get rid of anything that moves off the screen:

	  // stillVisibleOnScreen returns true if passed body
	  // is within the 300x300 canvas
	  var stillVisibleOnScreen = function(b1) {
		return b1.center.x > 0 && b1.center.y > 0 && b1.center.x < 300 && b1.center.y < 300;
	  };

	  // Throw away bodies that have moved beyone the edge of the screen.
	  this.bodies = this.bodies.filter(stillVisibleOnScreen);

(Yes, I know I shouldn't hard-code the screen size. We'll get there in a minute.)

This worked reasonably well, except that you can move the player off the edge of the screen... at which point it disappears! So, let's prevent that:

in Player.prototype, update:

      // If left cursor key is down and we're not too close to the edge...
      if (this.keyboarder.isDown(this.keyboarder.KEYS.LEFT) &&
	      this.center.x - this.size.x/2 > 0 ) {
          // ... move left.
          this.center.x -= 2;
      } else if (this.keyboarder.isDown(this.keyboarder.KEYS.RIGHT) &&
               this.center.x + this.size.x/2 < 300) {
          this.center.x += 2;
      }

This also works, except that it allows the player to move a little too far to the right, so it looks squashed. It's probably off by one pixel on that side. Still, it doesn't allow the *center* of the player to go too far, so it solves the problem of it getting filtered out.

And this is what we have so far: https://github.com/wsmoak/annotated-code/blob/97d9a2b3068098099ea974b6b95bce70cbaa85b0/space-invaders/space-invaders.js

(Forked from https://github.com/maryrosecook/annotated-code/blob/master/space-invaders/space-invaders.js which is pretty close to the code in the video)

Now back to those 'magic numbers'. Instead of hard-coding 300 for the width or height of the canvas, it would be better to use the values in the gameSize var.

I would rather write this...

	  // stillVisibleOnScreen returns true if passed body
	  // is within the canvas
	  var stillVisibleOnScreen = function(b1) {
		return b1.center.x > 0 && b1.center.y > 0 && b1.center.x < gameSize.x && b1.center.y < gameSize.y;
	  };
... but it's an error:
[Error] TypeError: undefined is not an object (evaluating 'gameSize.x')
	stillVisibleOnScreen (space-invaders.js, line 72)
	filter ([native code], line 0)
	update (space-invaders.js, line 76)
	tick (space-invaders.js, line 39)
	Game (space-invaders.js, line 50)
	(anonymous function) (space-invaders.js, line 320)

Apparently gameSize isn't visible there. See above about scope questions. 'bodies' is visible, but it was defined as this.bodies = []; not with var as gameSize was.

This explains things a bit: http://stackoverflow.com/questions/4354418/var-vs-this-vs-constructor-parameter-variables

So, we'll use the draw function as an example, and pass gameSize into update.

  var Game = ...
      // Update game state.
      self.update(gameSize);

Game.prototype = ...
    update: function(gameSize) {
...
	  var stillVisibleOnScreen = function(b1) {
		return b1.center.x > 0 && b1.center.y > 0 && b1.center.x < gameSize.x && b1.center.y < gameSize.y;
	  };

The other place where I used the magic number 300 was in the Player's update function. Changing *that* update function to accept gameSize would mean passing it into *all* of the bodies' update functions, and no one else needs it.

But Player already receives gameSize in its constructor var Player = function(game, gameSize) { so I can just grab that and make it an instance variable.

   var Player = function(game, gameSize) { ...
       this.gameSize = gameSize;
...

Player.prototype {
...
       } else if (this.keyboarder.isDown(this.keyboarder.KEYS.RIGHT) &&
                 this.center.x + this.size.x/2 < this.gameSize.x) {
                          this.center.x += 2;
       }

There! No more magic numbers, the player stays on the screen, and the bullets are removed from the bodies array when they move beyond the edge of the canvas.

Final version is at: https://github.com/wsmoak/annotated-code/blob/master/space-invaders/space-invaders.js

Further improvements:

See MoreSpaceInvaders


HomePage | RecentChanges | Preferences
This page is read-only | View other revisions
Last edited November 3, 2014 2:24 pm by WendySmoak (diff)
Search: