newguy
newguy

Reputation: 5986

Drawing static background on HTML 5 Canvas

Just edited my question to make it clearer and more obvious.

I am building a background for my game and I want my background to stay at the same relative position of the objects I draw on top the background except the player object, like a FPS game except that my game is 2D. I am pretty sure this has been done a thousand times but I don't know where to find the solution.

My game is designed to always focus my character at the center of the screen, and draw other objects on the other parts of the background.

Suppose my background is as big as the size of 9 frames as described below, one frame is the exact size of my browser screen.

enter image description here

Now if my character goes from frame 1 to frame 9, my character will always stay at the center, which is at about (frame.width / 2, frame.height / 2) because my character will have some kind of shape. So the screen will display frame '9' instead of frame '1' now. During the moving process, I will be able to see other objects 'moving away' from my character.

I use a Map class to draw my background, its render() method is like this:

var drawBackground = function (callback) {      
  callback(this);
}

image.onload = drawBackground(() => {
  var pattern = this.ctx.createPattern(image, 'repeat');
  this.ctx.fillStyle = pattern;
  this.ctx.fillRect(0, 0, this.paintWidth, this.paintHeight);
  // this.ctx.drawImage(image, 0, 0, 10000, 10000);

});
image.src = require('../media/bg.jpg');

Now there is a problem in my code. Because my background(map) drawing always redraw the screen around my character, the effect I am seeing is like the background moving together with my character, whereas everything else stay at the same location. This doesn't look natural. I want the background and all the other objects stay at the same location. They don't move with my character. How can I do this?

I use an animate() function to show the animation.

function animate() {

  map.clear(); // clearRect

  ... Some code to track my character...

  map.render();

  character.render();

  requestAnimationFrame(animate);
}

animiate();

EDIT:

some people suggest I use two canvases. I am actually using two canvases.

this is my setup in index.html file:

<canvas id="background" style="z-index: 1;">You are viewing the Game Background Canvas -- Update your browser!</canvas>
<canvas id="canvas" style="z-index: 2;">...</canvas>

they have css styles as follow:

#canvas, #background {position: absolute;left: 0px; top: 0px;}

And my background uses 'background' canvas, my character uses 'canvas', but my other objects also use 'background' canvas and they didn't move with my character, only the background moves with my character, which is strange.

Upvotes: 0

Views: 1760

Answers (2)

Oliver
Oliver

Reputation: 1644

To draw the background how you want, you simply need to position the background to be drawn at -character.x, -character.y, assuming that that is where the character positions are stored. To do this with a canvas pattern, simply translate the canvas to that position and then draw the pattern at the origin point + the character's position.

The reason so much fiddling around is needed is because of the way canvas patterns work. They are like canvas gradients; it doesn't matter where you fill, it's almost like you're using magic textas that reveal hidden colour underneath. It doesn't matter if you fill at position 100, 100 or 200, 200; the pattern always starts at 0, 0. So we change 0, 0 to be the negative player's position and draw at the positive player's position.

image.onload = drawBackground(() => {
  var pattern = this.ctx.createPattern(image, 'repeat');
  this.ctx.fillStyle = pattern;
  this.ctx.save();
  this.ctx.translate(-character.x, -character.y);
  this.ctx.fillRect(character.x, character.y, this.paintWidth, this.paintHeight);
  this.ctx.restore();
});

As requested by the OP, I will try and explain this 'magic' in more detail.

Canvas patterns are a kind of fill style, like plain colors or canvas gradients. Think of methods like fillRect as being a window wiper, wiping a dirty window to reveal the other side. When using a canvas pattern, that other side is a repeatedly tiled pattern. Now, imagine that pattern made up the wallpaper of the wall visible through the window. (To make it simpler imagine that the wall is close to touching the window, so you can only see the shape of any wiped parts of the window projected against the wall.) Say you were to wipe a random box shape of the window clean to reveal a section of the pattern behind. At the top left corner, you wouldn't necessarily see the top left corner of one of the pattern's tiles. You might see the middle. Or any random bit. Now, say we wanted the top left corner of a tile to be at the top left corner of the box shape wiped from the window. We would have to change where the wallpaper starts being tiled. On a canvas, we simply use translate. We translate the origin point of the canvas (x = 0, y = 0) to be whatever we need it to be (which is irrelevant for now) and then we execute fillRect. However, then fillRect will be executed at that new origin point. We don't want that. So, we set the x and y to start filling to be the opposite of the amount we translated the canvas's origin point. Then, we can get to see whatever part of the pattern we want by just changing the amount we translate the canvas origin point by.

In our case, we translate it by the negative version of the character's x and y position. (Negative so that the background moves the other way the character does.) Then, we draw the pattern starting at the character's position, because -character.x + character.x = 0, and we want to draw starting at the top left corner of the screen. And there you have it! Lastly we just need to change the canvas's origin point back to the top left corner of the screen, using context.save and context.restore, to get the final result!

Upvotes: 2

Jeetendra Chauhan
Jeetendra Chauhan

Reputation: 1977

use two canvas one for character and other for moving background, you can place them on same position using absolute positioning.

<div style="position: relative;">
  <canvas id="layer1" width="100" height="100" 
    style="position: absolute; left: 0; top: 0; z-index: 0;"></canvas>
  <canvas id="layer2" width="100" height="100" 
    style="position: absolute; left: 0; top: 0; z-index: 1;"></canvas>
</div>

Upvotes: 0

Related Questions