JavaScript Space Shooter Tutorial

This is a step-by-step guide to creating a simple top-down shooter game with WADE. You can download the full source code here.


Did you know that there is now a video tutorial to make this type of game more quickly through WADE's visual editor?

1. Getting ready

Let's start by creating a new folder somewhere, and copying the WADE files into it. At the time of writing, the latest version of WADE is 1.0, so that's the one we're going to use. So download the zip file from clockwork chilli's website, and extract wade_1.0.js (or a newer version), index.html and style.css into your game folder. I am also going to create an empty images sub-folder in here. Next, we are going to create a new, empty file in this folder, called shooter.js, which is going to contain our game. Now let's edit index.html and change the line that says:

    // wade.init('test.js');

To:

    wade.init('shooter.js');

And in shooter.js we are going to create our WADE App. So:

    App = function()
    {
    };

Inside the App function, we are going to create load and init functions, like this:

    App = function()
    {
        this.load = function()
        {

        };

        this.init = function()
        {

        };
    };

2. The player ship

Let's start by setting a fixed resolution for our project (708x398) in the init function, to keep things simple:

    this.init  = function()
    {
        wade.setMinScreenSize(708, 398);
        wade.setMaxScreenSize(708, 398);
    };

The first thing we want to do, is create an images sub-folder in our game's folder. Then we load a picture for the player spaceship that we're going to use. I've got one from opeclipart.org and I've saved it as ship.png in the images folder. So in the load function:

    wade.loadImage('images/ship.png');

Next we create an object that uses this picture. I am going to define a ship variable at the very top of our App function. By defining it here, I am making it accessible to any child functions of the App function too:

    var ship;

Now let's create it in the init function like this:

    var sprite = new Sprite('images/ship.png');
    ship = new SceneObject(sprite, 0, 0, 0);
    wade.addSceneObject(ship);

If you now open our index.html in a browser, you should see this (if you have any problems with this step, have a quick look at this setup guide to make sure the browser you are using is able to run local scripts):

It isn't much, but it's a start! Now let's make it move, by creating an onMouseMove functions inside our App function:

    this.onMouseMove = function(eventData)
    {
        ship.setPosition(eventData.screenPosition.x, eventData.screenPosition.y);
    };

This will move the ship by setting its position to the current mouse position (you can try it by refreshing index.html in your browser). However we should probably set an initial position too, at the initial mouse position. So let's change the content of our init function to this:

    var sprite = new Sprite('images/ship.png');
    var mousePosition = wade.getMousePosition();
    ship = new SceneObject(sprite, 0, mousePosition.x, mousePosition.y);
    wade.addSceneObject(ship);

Note that we are using a "mouse" here, by responding to an onMouseMove event, and asking WADE for the mouse position. However WADE will automatically translate all this to make it work on touch-screen devices too, where a mouse is not present and players will be using their fingers instead.

3. Fire

Now let's fire some bullets. We want to do this when the mouse button is pressed (or when a finger is on the screen), and to keep things simple we are going to implement an auto-fire system, so we keep firing bullets as long as the mouse is pressed or our player's finger is on the screen. We need some sort of main loop, or a function that is executed at each step (i.e. several times per second) by WADE. We can do this by adding this to the init function for example:

    wade.setMainLoopCallback(function()
    {
        // code to execute several times per second
    }, 'fire');

As you can see we are creating a function (that we are going to fill up with some code now) and we are telling WADE that this is supposed to be executed for each main loop (and we are calling this particular main loop "fire", more on this later). Now let's add some code to this function:

    if (wade.isMouseDown())
    {
        var shipPosition = ship.getPosition();
        var shipSize = ship.getSprite().getSize();
        var sprite = new Sprite('images/bullet.png');
        var bullet = new SceneObject(sprite, 0, shipPosition.x, shipPosition.y - shipSize.y / 2);
        wade.addSceneObject(bullet);
        bullet.moveTo(shipPosition.x, -500, 600);
    }

As you can see, when the mouse is down I'm creating a new bullet sprite at the ship position (at the top of the ship sprite to be precise). Then I'm moving it upwards with a speed of 600 units per second.
If you try this, you'll see that the ship is firing a lot of bullets when the mouse is pressed and, importantly, these bullets will live forever, because we are never deleting them. So let's add a couple more lines of code after telling each bullet to move:

    bullet.onMoveComplete = function()
    {
        wade.removeSceneObject(this);
    };

This will ensure that bullets get deleted when they reach their destination (which we've set, arbitrarily, at -500 units along the y axis, somewhere above the top of the screen). But we want to fire fewer bullets: we need to decide a fire rate (how many bullets per second), and we need to keep track of when was the last time we fired a bullet. So at the top of the App function:

    var lastFireTime = 0;
    var fireRate = 5;

And now at the beginning of our fire main loop:

    var nextFireTime = lastFireTime + 1 / fireRate;
    var time = wade.getAppTime();
    if (wade.isMouseDown() && time >= nextFireTime)
    {
        lastFireTime = time;
        // create bullet...
    }

And now our ship is firing 5 bullets per second, which is what we wanted.

4. Enemy ships

It's time to add some enemies. Again, I've grabbed a picture from openclipart.org, and I've saved it into our images folder, calling it enemy.png. Then I'm going to load it in the load function:

    wade.loadImage('images/enemy.png');

Now let's create a new function inside our App function, calling it spawnEnemy. In this function, we create an enemy ship somewhere off screen (we calculate a position that's right above the top edge of the screen). Then we move it downwards, by calculating a position that's right below the bottom edge of the screen. For both the start and end positions, we pick a random X coordinate:

    this.spawnEnemy = function()
    {
        // create a sprite
        var sprite = new Sprite('images/enemy.png');

        // calculate start and end coordinates
        var startX = (Math.random() - 0.5) * wade.getScreenWidth();
        var endX = (Math.random() - 0.5) * wade.getScreenWidth();
        var startY = -wade.getScreenHeight() / 2 - sprite.getSize().y / 2;
        var endY = -startY;

        // add the object to the scene and and make it move
        var enemy = new SceneObject(sprite, 0, startX, startY);
        wade.addSceneObject(enemy);
        enemy.moveTo(endX, endY, 200);

        // when the enemy is finished moving, delete it
        enemy.onMoveComplete = function()
        {
            wade.removeSceneObject(this);
        };
    };

And now at the bottom of the init function, we can call this function we've just created. But rather than spawning an enemy directly, let's give the player a couple of seconds to get ready. To do this, I'm going to create another two variables at the top of the App function:

    var nextEnemy;
    var enemyDelay;

And at the bottom of the init function:

    enemyDelay = 2000;
    nextEnemy = setTimeout(wade.app.spawnEnemy, enemyDelay);

As you can see I'm introducing a 2000ms delay before the first enemy is spawned. You can try the game at this point and, while it is quite cool, we want more than one single enemy. In fact, at the bottom of our spawnEnemy function, we are going to call the same spawnEnemy function again, to spawn another enemy, after some time.

    nextEnemy = setTimeout(wade.app.spawnEnemy, enemyDelay);

And to make the whole thing more interesting, I'm also going to lower the enemyDelay every time we spawn a new enemy – but let's make it so the minimum delay is 200ms (we never want to spawn more than 5 enemies per second, or the game will just be too hard).

5. Destroying enemy ships

The game is starting to look promising, but now we want our bullets to actually destroy these enemies. To do this, we should create an array to store all the bullets that are currently active. We declare the array variable, as usual, at the top of the App function:

    var activeBullets = [];

Every time we create a bullet we add it to this array:

    activeBullets.push(bullet);

And every time we delete a bullet, we remove it from the activeBullets array:

    wade.removeObjectFromArray(this, activeBullets);

Now in our main loop, for each bullet that's currently active, we want to see if it's colliding with an enemy. And if it is, we delete the enemy:

    for (var i=0; i < activeBullets.length; i++)
    {
        var colliders = activeBullets[i].getOverlappingObjects();
        for (var j=0; j < colliders.length; j++)
        {
            if (colliders[j].isEnemy)
            {
                wade.removeSceneObject(colliders[j]);
                break;
            }
        }
    }

Of course, for this to work, we need to set the isEnemy flag for each enemy, when we create it. So in spawnEnemy:

    enemy.isEnemy = true;

But we also want to delete any bullet that's hitting an enemy. Which is fine, but we should also update the activeBullets array, and since we are iterating over it, that would be a bad idea.... Unless we iterate over the activeBullets array backwards, in which case deleting elements while we are iterating is perfectly fine. So we can rewrite our for loop like this:

    for (var i=activeBullets.length-1; i>=0; i--)
    {
        var colliders = activeBullets[i].getOverlappingObjects();
        for (var j=0; j < colliders.length; j++)
        {
            if (colliders[j].isEnemy)
            {
                wade.removeSceneObject(colliders[j]);
                wade.removeSceneObject(activeBullets[i]);
                wade.removeObjectFromArrayByIndex(i, activeBullets);
                break;
            }
        }
    }

Explosions are cool, and this is the perfect place to add an explosion, so just before deleting these objects, let's do this:

    var position = colliders[j].getPosition();
    wade.app.explosion(position);

And we create an explosion function for our app:

    this.explosion = function(position)
    {
        var animation = new Animation('images/boom.png', 6, 4, 30);
        var explosionSprite = new Sprite();
        explosionSprite.setSize(100, 100);
        explosionSprite.addAnimation('boom', animation);
        var explosion = new SceneObject(explosionSprite, 0, position.x, position.y);
        wade.addSceneObject(explosion);
        explosion.playAnimation('boom');
        explosion.onAnimationEnd = function()
        {
            wade.removeSceneObject(this);
        };
    };

I've just added an animated object, which is then removed from the scene as soon as the animation is finished, in its onAnimationEnd function. Of course we have to load the image that we're using here (a 6x4 spritesheet that I've called boom.png and placed in the images folder). So in our load function:

    wade.loadImage('images/boom.png');

And now we have exploding enemies!

6. Enemy fire

But it would be nice if these enemies turned to face us, and shot bullets in our direction. Let's sort out the orientation first. We are going to update the orientation of each enemy at every simulation step, so we could just add some code at the end of our main loop function. Or, even better, we could create a new main loop function, calling it ‘rotateEnemies' for example, and have it run together with our ‘fire' main loop. But we've done that already and I want to show you a different way of doing it. At each simulation step, wade calls the step function of each SceneObject. So we can do this after creating each enemy:

    enemy.originalStep = enemy.step;
    enemy.step = function()
    {
        this.originalStep();
        // handle rotation here
    };

So we override the step function of our enemy and WADE will then call our step function. This, in turn, calls the original step function, and then goes on to rotate the object. This approach turns out to be easier, because (unlike what we did for our ‘fire' main loop) here we don't have to maintain a list of active enemy ships, and don't have to worry about updating things when the ship dies. Now let's actually do the rotation bit. We need to calculate the angle between our enemy and the player ship, then we rotate the enemy ship accordingly.

    var enemyPosition = this.getPosition();
    var playerPosition = ship.getPosition();
    var angle = Math.atan2(playerPosition.y - enemyPosition.y, playerPosition.x - enemyPosition.x) - 3.141 / 2;
    this.setRotation(angle);

Now let's get these enemy ships to fire. I am adding a new image (enemyBullet.png) to our images folder, and I load it as usual:

    wade.loadImage('images/enemyBullet.png');

Now in the spawnEnemy function we can create a new fire function for the enemy, where we calculate the direction of our bullet (the vector from the enemy to the player, normalized), then we create a bullet and tell it to start moving. We don't want to call this function straight away though. We want to schedule its execution (let's say 1 second after the enemy ship is created), and then reschedule it again every 0.5 seconds, until the enemy ship dies. WADE makes this very easy by providing us with a very convenient schedule function:

    enemy.fire = function()
    {
        var enemySize = this.getSprite().getSize();
        var enemyPosition = this.getPosition();
        var playerPosition = ship.getPosition();

        // calculate direction
        var dx = playerPosition.x - enemyPosition.x;
        var dy = playerPosition.y - enemyPosition.y;
        var length = Math.sqrt(dx * dx + dy * dy);
        dx /= length;
        dy /= length;

        // calculate initial and final position for the bullet
        var startX = enemyPosition.x + dx * enemySize.x / 2;
        var startY = enemyPosition.y + dy * enemySize.y / 2;
        var endX = startX + dx * 3000;
        var endY = startY + dy * 3000;

        // create bullet
        var sprite = new Sprite('images/enemyBullet.png');
        var bullet = new SceneObject(sprite, 0, startX, startY);
        wade.addSceneObject(bullet);
        bullet.moveTo(endX, endY, 200);

        // delete bullet when it's finished moving
        bullet.onMoveComplete = function()
        {
            wade.removeSceneObject(this);
        };

        // schedule next bullet
        this.schedule(1000, 'fire');
    };
    enemy.schedule(500, 'fire');

Note that when the enemy dies (i.e. when it's removed from the scene) any scheduled functions that it had will be cancelled automatically.

7. Adding a dynamic background

Now the game is starting to look interesting, but a good background would make it certainly look nicer. Space games usually have a black, starry sky in the back. We could use a static sprite for this, but I think it would look nicer if the start moved downwards (to give the impression that everything else is moving forward). So let's use a static black picture for the back, and some moving sprites for the stars. I don't really want to open an external program to create a solid black picture to put in the background though – there's an easier way of doing it: we can create a blank sprite (with no images), which I'm putting on layer 10 to make sure it appears behind anything else, and make it use a custom draw function:

    var backSprite = new Sprite(0, 10);
    backSprite.setSize(wade.getScreenWidth(), wade.getScreenHeight());
    backSprite.setDrawFunction(wade.drawFunctions.solidFill_('black'));
    var backObject = new SceneObject(backSprite);
    wade.addSceneObject(backObject);

Now let's add some stars on top of this black background. I'm going to use a singly tiny (16x16) sprite that I'm placing in the usual location calling it star.png. Then I load it as usual:

    wade.loadImage('images/star.png');

And in the init function I'm going to create a few of these, in random positions, with random orientations and random sizes. I am putting them on the same layer as the background (number 10) for the time being, but we'll get back to this later on:

    // create stars
    for (var i=0; i<15; i++)
    {
        var size = Math.random() * 8 + 8;
        var rotation = Math.random() * 6.28;
        var posX = (Math.random() - 0.5) * wade.getScreenWidth();
        var posY = (Math.random() - 0.5) * wade.getScreenHeight();
        var starSprite = new Sprite('images/star.png', 10);
        starSprite.setSize(size, size);
        var star = new SceneObject(starSprite, 0, posX, posY);
        star.setRotation(rotation);
        wade.addSceneObject(star);
    }

So it's looking a bit nicer.

Now let's get these stars to move towards the bottom edge of the screen, and when they get there we pick a new random position along the top edge of the screen, and make them move downwards again. So at the bottom of this for loop, after adding each start to the scene, we do this:

    star.moveTo(posX, wade.getScreenHeight() / 2 + size / 2, 20);
    star.onMoveComplete = function()
    {
        var size = this.getSprite().getSize().y;
        var posX = (Math.random() - 0.5) * wade.getScreenWidth();
        this.setPosition(posX, -wade.getScreenHeight() / 2 - size / 2);
        this.moveTo(posX, wade.getScreenHeight() / 2 + size / 2, 20);
    };

This gives the impression that we're constantly moving forward, which is a nice touch.

8. Score

Now let's add a score counter too. We're going to keep it easy, so it will just be a simple text sprite in the upper-right corner of the screen. I'm going to define a couple of variables at the top of the App function:

    var scoreCounter;
    var score;

Then in our init function we create this object. I'm going to set the text alignment to right, and I'm placing the object in the upper-right corner of the screen:

    score = 0;
    var scoreSprite = new TextSprite(score.toString(), '32px Verdana', '#f88', 'right');
    scoreCounter = new SceneObject(scoreSprite, 0, wade.getScreenWidth() / 2 - 10, -wade.getScreenHeight() / 2 + 30);
    wade.addSceneObject(scoreCounter);

Now let's get some points, when we destroy an enemy ship in our fire main loop:

    score += 10;
    scoreCounter.getSprite().setText(score);

9. Dying and going back to the main menu

The game is almost fully functional, but there's one vital bit missing: we should die when we're hit by an enemy ship or by an enemy bullet. To do this, first of all we need to know which objects are enemy ships and which objects are enemy bullets. Enemy ships have an isEnemy flag that we can check, so we're going to add a similar flag (isEnemyBullet) for enemy bullets when they are created, in the enemyShip.fire function:

    bullet.isEnemyBullet = true;

Now we are going to create a new main loop (which I'm calling die). A good place to do this is our init function. In this loop, we see which objects are overlapping our ship, and if any of them have either the isEnemy or the isEnemyBullet flag set, we create an explosion and remove the ship from the scene.

    wade.setMainLoopCallback(function()
    {
        var overlapping = ship.getOverlappingObjects();
        for (var i=0; i < overlapping.length; i++)
        {
            if (overlapping[i].isEnemy || overlapping[i].isEnemyBullet)
            {
                wade.app.explosion(ship.getPosition());
                wade.removeSceneObject(ship);
            }
        }
    }, 'die');

If you try the game now though, you'll see that this is not enough: even when the ship is removed from the scene, we are still able to fire bullets, and we can still die and generate more explosions. This is because our fire and die main loops are still active, and we should really remove them when we die, like this:

    wade.setMainLoopCallback(null, 'fire');
    wade.setMainLoopCallback(null, 'die');

Of course it's a bit annoying that when we do die, we have to refresh the page to play again. To address this problem, we'll have to move some code around. The init function, as it is now, does many things: sets up resolution parameters, creates and initializes objects, and start our main loops. Let's move some of this code into a new function, called startGame, like this:

    this.startGame = function()
    {
        var sprite = new Sprite('images/ship.png');
        var mousePosition = wade.getMousePosition();
        ship = new SceneObject(sprite, 0, mousePosition.x, mousePosition.y);
        wade.addSceneObject(ship);

        wade.setMainLoopCallback(function()
        {
            var nextFireTime = lastFireTime + 1 / fireRate;
            var time = wade.getAppTime();
            if (wade.isMouseDown() && time >= nextFireTime)
            {
                lastFireTime = time;
                var shipPosition = ship.getPosition();
                var shipSize = ship.getSprite().getSize();
                var sprite = new Sprite('images/bullet.png');
                var bullet = new SceneObject(sprite, 0, shipPosition.x, shipPosition.y - shipSize.y / 2);
                wade.addSceneObject(bullet);
                activeBullets.push(bullet);
                bullet.moveTo(shipPosition.x, -500, 600);
                bullet.onMoveComplete = function()
                {
                    wade.removeSceneObject(this);
                    wade.removeObjectFromArray(this, activeBullets);
                };
            }

            // check for collisions
            for (var i=activeBullets.length-1; i>=0; i--)
            {
                var colliders = activeBullets[i].getOverlappingObjects();
                for (var j=0; j < colliders.length; j++)
                {
                    if (colliders[j].isEnemy)
                    {
                        // create explosion
                        var position = colliders[j].getPosition();
                        wade.app.explosion(position);

                        // add points
                        score += 10;
                        scoreCounter.getSprite().setText(score);

                        // delete objects
                        wade.removeSceneObject(colliders[j]);
                        wade.removeSceneObject(activeBullets[i]);
                        wade.removeObjectFromArrayByIndex(i, activeBullets);
                        break;
                    }
                }
            }
        }, 'fire');

        wade.setMainLoopCallback(function()
        {
            var overlapping = ship.getOverlappingObjects();
            for (var i=0; i < overlapping.length; i++)
            {
                if (overlapping[i].isEnemy || overlapping[i].isEnemyBullet)
                {
                    wade.app.explosion(ship.getPosition());
                    wade.removeSceneObject(ship);
                    wade.setMainLoopCallback(null, 'fire');
                    wade.setMainLoopCallback(null, 'die');
                }
            }
        }, 'die');

        // add a score counter
        score = 0;
        var scoreSprite = new TextSprite(score.toString(), '32px Verdana', '#f88', 'right');
        scoreCounter = new SceneObject(scoreSprite, 0, wade.getScreenWidth() / 2 - 10, -wade.getScreenHeight() / 2 + 30);
        wade.addSceneObject(scoreCounter);

        // spawn enemies
        enemyDelay = 2000;
        nextEnemy = setTimeout(wade.app.spawnEnemy, enemyDelay);
    };

So in the init function we only set the resolution limit and create the background with the stars, and all the rest is done in startGame. I'm doing this because I would like to add a menu screen before the game start. This menu screen is going to have our starry sky background, with a simple text sprite asking the user to click to start. When the user does click, we start the game. So now we can add this to the bottom of our init function:

    // main menu text
    var clickText = new TextSprite('Click or tap to start', '32px Verdana, 'white', 'center');
    var clickToStart = new SceneObject(clickText);
    wade.addSceneObject(clickToStart);
    wade.app.onMouseDown = function()
    {
        wade.removeSceneObject(clickToStart);
        wade.app.startGame();
        wade.app.onMouseDown = 0;
    };

As you can see, I'm adding this text object and I'm setting an onMouseDown function for the whole app, so that it's executed when the user clicks anywhere (not on a particular object). In this onMouseDown function I'm actually starting the game, removing the menu text, and also removing the onMouseDown event handler for the app, by setting it to a falsy value. The text isn't looking particularly good, so let's make it blink, which would give it a nice retro feeling:

    clickText.setDrawFunction(wade.drawFunctions.blink_(0.5, 0.5, clickText.draw));

Now when the game ends (i.e. when the player dies) we want to go back to the main menu screen, and we can do so by clearing the scene, and calling the init function again. So just after deleting our ship in our die main loop, we can do this:

    wade.clearScene();
    wade.app.init();
    clearTimeout(nextEnemy);

Note that I'm also cancelling the spawning of the next enemy with a clearTimeout function. But while this works, it isn't very nice to be kicked out of the game as soon as you die... we may want to wait a couple of seconds, so let's move this code inside a setTimeout:

    setTimeout(function()
    {
        wade.clearScene();
        wade.app.init();
        clearTimeout(nextEnemy);
    }, 2000);

Now it's much nicer, and I think the game is nearly complete.

10. Quick optimization

We should probably spend a couple of minutes optimizing things, because if you try it as it is on a very low-end mobile device, it may be a bit too slow. The main reason for slowness on mobiles is, generally speaking, the poor virtual fill rate. This means that the more pixels you draw onto your canvas, the slower your game runs. For this reason WADE tries to minimize the amount of pixels it draws, so if, for example, an area of the screen hasn't changed, it doesn't redraw it.
However consider what happens in our background layer: the stars are moving, and they need to be redrawn every frame. But since the stars are semi-transparent objects, any objects behind them will have to be redrawn every frame, and in this case it's the big black background rectangle (which is made of a lot of pixels). If we moved it onto its own layer though, we wouldn't need to draw it every frame, because each layer is a separate canvas object. So the background black canvas would never change, and we could draw it only once. So let's move our background sprite to layer 11:

    var backSprite = new Sprite(0, 11);

11. Persistent score

Having done this, there's one last thing I would like to add to this example game: a "persistent" high score. I say "persistent", but I'm not going to upload the score to a server; instead, I'm going to save it locally, but in a way that it will still be there the next time the user navigates to the game's page with the same browser. Of course this isn't really a "persistent" way of storing the score, because our users could always decide to delete the browser cached and locally saved files (sometimes they do that), and their score will be lost when that happens. But it's still a very useful technique to use when you want to store non-vital information without having to resort to an external server to upload data to. First of all, in our init function, we are going to attempt to retrieve a locally stored object called shooterData. This object will not exist the first time we run the game, but then the game will save it into the local storage. So we see if this object exist, and set a highscore variable accordingly.

        var shooterData = wade.retrieveLocalObject('shooterData');
        var highScore = (shooterData && shooterData.highScore) || 0;

Now we want to display this high score, possibly just below the "click to start" text:

    clickToStart.addSprite(new TextSprite('Your best score is ' + highScore, '18px Verdana', 'yellow', 'center'), {y: 30});

And finally, when we die we check this variable again and, if we got a higher score, we save the new score into the shooterData object:

    var shooterData = wade.retrieveLocalObject('shooterData');
    var highScore = (shooterData && shooterData.highScore) || 0;
    if (score > highScore)
    {
        shooterData = {highScore: score};
        wade.storeLocalObject('shooterData', shooterData);
    }

Now if you play the game, then refresh the page, you'll see that the high score is being saved across different sessions, which is what we wanted.

12. Bug fixing

If you keep your browser's console open while testing your game (which is very good practice), you have probably noticed that the game comes up with an error: Uncaught TypeError: Cannot call method 'setPosition' of undefined. This happens because when we move the mouse, the onMouseMove function of our App is executed, and in that function we try to set the position of the ship object. However, if the game hasn't started yet, the ship object hasn't been defined yet – hence the error. To fix it, we can change the contents of our onMouseMove function to this:

        ship && ship.setPosition(eventData.screenPosition.x, eventData.screenPosition.y);

So it won't attempt to set the position of the ship if ship is not defined.

This concludes this top-down shooter sample, I hope you found it useful as we covered quite a few different topics here. If you want to download the full source code, get it here.