Creating an HTML 5 Game – Part 3–Simple Parallax Stars

 

Part 1 – The beginning
Part 2 – Moving stuff around
Part 3- Simple Parallax Stars – You Are Here!

With our last tutorial we showed you how you could move an object on the screen with key events. Today we are going to start building the main structure of our game, and get a star field up and running and have our “ship” (which is actually just a plain borg cube moving around the screen). So now lets start adding some more detail to our game.

“Parallax is the difference in the apparent position of an object viewed along two different lines of sight.” – Wikipedia

Ok so parallax gives an illusion of depth and movement, its used quite a bit in 2D side scrolling games, and I am going to show you how to create a neat simple example of this just using Javascript. To make life easier we are going to spend a little time refactoring the code. Step 1 game screen usable. We are going to centre the screen and change its size. To do this we add the following styles into the body. You could create a separate style sheet if you like.

<style>
html, body
{
width: 100%;
height: 100%;
margin: 0px;
}

.bodyClass
{
background-color: Black;
text-align: center;
}

.canvasClass
{
position: absolute;
left: 50%;
top: 50%;
width: 1280px;
height: 720px;
margin-left: -640px; /* half of width */
margin-top: -360px; /* half of height */
background-color: Black;
}
</style>

Also replace the body of the HTML document with the following:

<div class="canvasClass">
<canvas id="myCanvas" width="1280" height="720">
This game was written for an HTML 5 compliant browser.
</canvas>
</div>

What this does is set the background to black, and increase the size. It should now look something like this:

New Screen Size

Ok now a few more tweaks to the Javascript side of things. We are going to auto detect the canvas size. to do this we create two variables at the top of the script and add code to the init function that fetches the canvas size. Like so:

// With the other global var declarations
var width = 0;
var height = 0;

// Inside the init function.
width = parseInt(document.getElementById(canvasName).attributes["width"].value, 0);
height = parseInt(document.getElementById(canvasName).attributes["height"].value, 0);

Now we also wan to keep track of the amount of time spent between frames, we do this by simply taking the 1000/FPS and storing it in a variable deltaTime. So in the init function we have:

deltaTime = 1000/FPS;
setInterval(update, deltaTime);   

This really so we can reuse it through out our game. Ok so now on to making some stars. first we need to initialise the star field. To do this we need an array to store the stars. But our stars are going to have 2 properties Depth (Distance from the screen between 0.1 and 1.0) and the position. We are going create the array and a variable so we can easily adjust the number of stars.

// Global variables for the star field

var starArray = []
var starCount = 300;

Now we are going to create random stars. We do this by simply looping through and creating a random x and y position and a random depth. I made the minimum value 0.20 and the max value 0.99 for the depth because we use the depth to calculate how fast the stars move.

// Init stars function
function InitStars(){
for (var i = 0; i < starCount; i++) {
starArray[i] = new function () {
this.Position = new Vector2D(Math.random() * width,Math.random() * height),
this.Depth = ((Math.random() * 79) + 20) / 100.0
}
}
}

Now all we need to do is call InitStars() in the init function. Once the stars are initialised we want to draw them on the screen. To do this we create a DrawStars function which take the current 2D context. Like so:

function drawStars(ctx) {
for (var i = 0; i < starArray.length; i++) {
ctx.fillStyle = '#fff'; // Black
ctx.fillRect(starArray[i].Position.X, starArray[i].Position.Y, 1, 1);
}
}

We are just drawing single pixel dots for each star, we could do a bunch of stuff like make them different colours and different sizes based on depth or just randomly but for our needs we can get away with simple white pixels. Now we add the following line into the update function inside the context check. 

if(context){
drawStars(context);           

context.fillStyle = '#fff'; // Black
context.fillRect(x - 25,y -25,50,50);
}        

Note the draw order is important, if you draw the stars last you will draw them on top of everything drawn before. So now we should have this when you run the page in the browser (Zoom to see the effect single pixel dots disappear when scaling down Smile ):

New Screen Size Stars

Ok now we have stars the next step for us is to start making the stars move. For this tutorial we are only going to make the scroll from right to left. We also then just wrap those stars to the other side of the screen. To do this we create an updateStars function like so:

function updateStars(){
var warp = 0.25;
for (var i = 0; i < starArray.length; i++) {
var dp = deltaTime * starArray[i].Depth * warp;
starArray[i].Position.X -= dp;
if (starArray[i].Position.X < 0) starArray[i].Position.X = width;
}

}

So what are we doing here, we are taking the amount of time passed, multiplying it by the speed (Warp) and then by the depth. If the position is off the screen move it to the opposite side of the screen and done. This could be a bit more complex but possibly also cooler if you used a vector for direction and speed. But i leave that to you to play with. Last step is to add the updateStars function to the update function before you do all your drawing and after the key handling.

// Draw

updateStars();
context.fillStyle = '#000'; // Black

Now if you run it you should have a parallax star field. Performance will vary dependent on star size, number of stars and the machine that you are using. I hope this was useful and till the next tutorial

Dave out-

Code:

<!DOCTYPE html>
<html>
<head>
<title>The Swarm</title>
<style>
html, body
{
width: 100%;
height: 100%;
margin: 0px;
}

.bodyClass
{
background-color: Black;
text-align: center;
}

.canvasClass
{
position: absolute;
left: 50%;
top: 50%;
width: 1280px;
height: 720px;
margin-left: -640px; /* half of width */
margin-top: -360px; /* half of height */
background-color: Black;
}
</style>
<script>

    var x = 0;
var y = 0;
var FPS = 30;
var canvasName = "myCanvas";
var speed = 10;
var context;
var downPress = false;
var upPress = false;
var leftPress = false;
var rightPress = false;
var deltaTime = 0;
var width = 0;
var height = 0;
window.onload = init;

    function init(){
var elem = document.getElementById(canvasName);
if (elem && elem.getContext) {
context = elem.getContext("2d");
}

        document.onkeydown = keyDownEvent;
document.onkeyup = keyUpEvent;

        width = parseInt(document.getElementById(canvasName).attributes["width"].value, 0);
height = parseInt(document.getElementById(canvasName).attributes["height"].value, 0);
   

        InitStars();

        deltaTime = 1000/FPS;
setInterval(update, deltaTime);
}   

    function update(){
// Handle input
               

        if(downPress) y += speed;
if(upPress) y-= speed;
if(leftPress) x-=speed;
if(rightPress) x+=speed;

        if(x<0) x = 0;
if(x>width) x = width;

        if(y<0) y = 0;
if(y>height) y = height;
// Update AI
// Handle Physics
// Update Objects
// Draw

        updateStars();
context.fillStyle = '#000'; // Black
context.clearRect(0,0,width,height);
if(context){
drawStars(context);           

                context.fillStyle = '#fff'; // Black
context.fillRect(x - 25,y -25,50,50);
}

}   

    function keyDownEvent(evt) {
var key = evt.keyCode != 0 ? evt.keyCode : evt.charCode;

switch (key) {
case 40:
downPress = true;
break;
case 38:
upPress = true;
break;
case 37:
leftPress = true;
break;
case 39:
rightPress = true;
break;
}
}

    function keyUpEvent(evt) {
var key = evt.keyCode != 0 ? evt.keyCode : evt.charCode;

switch (key) {
case 40:
downPress = false;
break;
case 38:
upPress = false;
break;
case 37:
leftPress = false;
break;
case 39:
rightPress = false;
break;
}
}

    function Vector2D(x,y){
this.X = x,
this.Y = y
}

    function Star(position, depth){
this.Position,
this.Depth
}

    var starArray = []
var starCount = 300;

    function InitStars(){
for (var i = 0; i < starCount; i++) {
starArray[i] = new function () {
this.Position = new Vector2D(Math.random() * width,Math.random() * height),
this.Depth = ((Math.random() * 79) + 20) / 100.0
}
}
}

    function drawStars(ctx) {
for (var i = 0; i < starArray.length; i++) {
ctx.fillStyle = '#fff'; // Black
ctx.fillRect(starArray[i].Position.X, starArray[i].Position.Y, 1, 1);
}
}

    function updateStars(){
var warp = 0.25;
for (var i = 0; i < starArray.length; i++) {
var dp = deltaTime * -starArray[i].Depth * warp;
starArray[i].Position.X += dp;
if (starArray[i].Position.X < 0) starArray[i].Position.X = width;
}

}

    </script>
</head>
<body>
<div class="canvasClass">
<canvas id="myCanvas" width="1280" height="720">
This game was written for HTML 5 Compliant browsers
</canvas>
</div>
</body>
</html>