Tutorial: How to make a tile-based 2D scrolling game world in JavaScript

Lesson 3) Creating a camera

Let's be candid

The camera will tell us which part of our map is currently on the player's screen. Like everything else, the camera will be an object:
var camera = {
x: 0,
y: 0,
w: 800,
h: 600
};
For this tutorial we'll make it cover the whole canvas area, therefore we need a size of 800 x 600 pixels, and it will start life in the top-left corner of the game area (the start point is really irrelevant since we'll be centering it on the player anyway).

The camera doesn't have to cover the whole canvas, it could be a smaller portion if you wanted. Some games, like the first Doom games, left some space for a dashboard displaying the player's stats:

An image of Doom gameplay would normally appear here

I prefer to use the whole canvas and have that info floating on top. But whatever floats your boat. In any case, we need to limit what's displayed in it to only those tiles around the player, so the first thing we need to do is make sure that the camera is centered on the player.

Remember the camera's x and y coordinates are it's upper right corner, just like drawing a rectangle. So to center it on the player we need to deduct half of it's width and height from the player's x and y coordinates respectively:

function updateCamera() {
camera.x = Player1.x - (camera.w / 2);
camera.y = Player1.y - (camera.h / 2);
}
Now we need to call this function from mainDraw, but also please add in the three extra lines below it which draw a border around the edge of the camera - you'll see why in a minute:
updateCamera();
c.beginPath();
c.rect(camera.x, camera.y, camera.w, camera.h);
c.stroke();
Before we conduct our obligatory check of our handiwork, I'd like to draw your attention to a couple of updates I've made to the Player.

Firstly, I've changed the player's starting coordinates to 400x300, not 395x295 as they were before.

Secondly, I've pulled the code that draws the player out of mainDraw and put it into it's own function, playerDraw:
function playerDraw() {
c.fillStyle="red";
c.strokeStyle="blue";
c.beginPath();
c.rect(Player1.x - (Player1.w / 2), Player1.y - (Player1.h / 2), Player1.w, Player1.h);
c.lineWidth=1;
c.stroke();
c.fill();
}
Thirdly, as you may have noticed, I've changed the c.rect in this function. Instead of starting at Player1.x, we're starting at Player1.x minus half his width. The starting y coordinate of the rect is also now Player1.y minus half his height. This is to make sure the Player is fully centered in the screen - this didn't matter a great deal before, but it does now that the Player is going to be in the middle of the screen all the time.

Finally, I added a speed property to the Player...
// construct a player
function Player () {
this.x = 400;
this.y = 300;
this.w = 10;
this.h = 10;
this.speed = 5;
}
...and in playerMove I update his position by that value, rather than the static "1" that was there before. I also made him a bit faster. Heroes should be fast:
function playerMove(e){
if (keys[87]) {
Player1.y -= Player1.speed;
}
if (keys[83] ) {
Player1.y += Player1.speed;
}
if (keys[65] ) {
Player1.x -= Player1.speed;
}
if (keys[68] ) {
Player1.x += Player1.speed;
}
return false;
}
Right, let's see what we've done. Remember the extra rect I asked you to add in? The purpose of that was to check we'd done this correctly! We should see an 800x600 border around the player:


This seems to be working! We've created a camera that centers on the player. But as you can see, the player is not yet centered on the screen. A sharp and intelligent reader like yourself will also have noticed that we haven't actually drawn the map yet!

Don't worry, we'll do all this in the next lesson! Are you as excited as I am?

Go to Lesson 4 - Drawing the map