adding a margin to a fillRect with JavaScript canvas - javascript

I am making a snake game for fun, to practice JavaScript/canvas, and what I have so far is a while loop that says while i is < 100, then add a square and the square is added by using fillRect(10, 10, xCoord, yCoord) and in the while loop I have xCoord = xCoord + 11; so it moves the square to the right with a space in between... here is a link:
http://brycemckenney.com/snake_canvas
And the relevant code:
while(i < 100) {
// fillRect(x, y, width, height)... x is horizontal and y is vertical...
context.fillRect(xCoord, yCoord, sW, sH);
// We want each squere to have one pixel in between them, so we increment by 11, not 10.
xCoord = xCoord + 11;
// When i (0) gets to numOfSquares (3), then make the rest white so that you can't see them...
if (i >= numOfSquares) {
context.fillStyle = "#FFFFFF";
}
//Increment by 1 every loop, so that the squares keep going
i++;
}
I am trying to get the snake to animate, and I have tried many different options. Now I am trying to just add a margin-right the the 4 squares, so it looks like it's moving... is it possible to add margin to those? Here is a snapshot of what I have:

Well, since you're making the game of snake, we know what you want to do and I think it would be more useful if we tried to get you on the right track instead of modifying your existing code.
Lets suppose our logical game grid is 60x60.
So snake-pieces can be anywhere in this grid, having X and Y values between 0 and 59. This means:
A piece of snake in the top-left corner is at [0, 0]
A piece of snake in the top-right corner is at [59, 0]
Let us further suppose that a snake is made up of a number of segments. How about 4 segments to start. This means that we need to keep an array of the 4 positions:
[position1, position2, position3, position4]
We have to pick a front, so lets say that the end of the array is the "front" of the snake. If we pick the top left 4 positions the snake would be:
var mySnake = [[0, 0], [1,0], [2,0], [3,0]]
Which on the board looks like this:
++++OOOOOOOO
OOOOOOOOOOOO
OOOOOOOOOOOO
That means its 4 snake-pieces going from left to right, just like you have so far. The thing is, by saving these locations we've gone and added some persistent state to our program.
Now snake is a funny game, because "moving" the snake really means two things:
Taking the last (tail) piece away
Adding a new piece to the snake
So we should make a function for moving the snake that does both of these. We'll make one based on direction:
// modifies a snake's array
function moveSnake(snake, direction) {
// First we must remove a piece of the snake's tail, which is at the start of the array
// There's a built in command in JavaScript for this called shift()
snake.shift();
// Now we must add the new piece!
// To tell where we need to go we must look at the last location:
var lastLoc = snake[snake.length - 1];
// Just to be a little more clear:
var lastX = lastLoc[0];
var lastY = lastLoc[1];
switch (direction) {
case 'up':
snake.push([lastX, lastY-1]);
break;
case 'down':
snake.push([lastX, lastY+1]);
break;
case 'left':
snake.push([lastX-1, lastY]);
break;
case 'right':
snake.push([lastX+1, lastY]);
break;
}
// redraw after every move!
drawSnake(ctx, mySnake);
}
With this method we could do:
var mySnake = [[0, 0], [1,0], [2,0], [3,0]];
moveSnake(mySnake, 'down');
// mySnake is now [[1,0], [2,0], [3,0], [3,1]];
The snake now looks like this:
O+++OOOOOOOO
OOO+OOOOOOOO
OOOOOOOOOOOO
Now this method is pretty dumb, and in a real game of snake we'd need to add some conditions. Typically:
If the new piece of snake is on top of a piece of food, the tail piece is NOT removed, instead the snake is +1 block longer
If the new piece is outside of out 60x60 grid, the user loses the game
If the new piece is at a location that any of the other snake pieces already exist, the user loses the game
Alas those are not done here
We still need to draw it all, but since we are keeping track of the snake's location that's easy peasy. We can just draw one square for each piece of the snake:
function drawSnake(context, snake) {
// Remember to clear the board!
ctx.clearRect(0, 0, 600, 600);
var length = snake.length;
for (var i = 0; i < length; i++) {
context.fillStyle = 'teal';
// position is its own array and looks like: [x, y]
var position = snake[i];
// our logical snake board is 60x60, but the real canvas is 600x600,
// so we multiply x and y by 10.
// We also use "9" for the width and height so theres a tiny gap
context.fillRect(position[0]*10, position[1]*10, 9, 9);
}
}
See the completed demo here.
http://jsfiddle.net/FsqXE/
Note that it allows arrow keys to control the snake to show off the moveSnake method, but the regular snake game has the movement controlled by a timer, and the user can usually only change the next-possible-direction.

The canvas element is a 2D drawing surface, so it doesn't support (higher level) things like CSS margins.
To do animation, you'll need to continually clear the canvas and redraw the scene, each time slightly adjusting the position of moving objects (so that they appear to move). In your case, this means redrawing the squares, each time with a starting position 4 pixels away from the last time you redrew the frame.
You may want to have a look at the Basic animations page at MDN (part of their canvas tutorial).

Related

Handle movement after collision is detected

I'm making a small platformer in js and I'm having trouble with the collisions. Unfortunately it seems that 90% of the info online is detecting the collisions, and not what comes after. I can easily detect collisions as everything in my game is an axis aligned 2d rectangle, but handlining those collisions is the hard part.
I've tried moving the player up to the nearest floor when a collision is detected, but it also happens when you collide with a wall. So I tried calculating the closest face and snapping the player there, but it leads to all kinds of weirdness. Here is the code I have so far (the current code is just for floor collisions now, but the same principal can be applied to the rest of the directions)
if (collided) {
let ld = {'a': 'l', 'b': Math.abs(player.left.x - col.left.x)}
let rd = {'a': 'r', 'b': Math.abs(player.right.x - col.right.x)}
let td = {'a': 't', 'b': Math.abs(player.top.y - col.top.y)}
let bd = {'a': 'b', 'b': Math.abs(player.bottom.y - col.bottom.y)}
let dirs = [ld, rd, td, bd]
let nearestFace = dirs.hasMin('b').a
if (nearestFace == 'b') {
player.grounded = true
player.yvel = 0
player.pos.y = col.top.y + player.size.y/2
} else {
player.grounded = false
}
}
Your code seems to not check where the nearest face collides just that it is the nearest face after collision. Which could cause this kind of problem:
If your game is in 2d, everything is a rectangle, and nothing has an angle, then you have a collision problem known as AABB (Axis-Aligned Bounding Boxes) collision, it's a great keyword to search for in your case and an amazing starting point.
First, I advise you to predict collisions instead of dealing with them after they happen because of potential problems like this:
Moreover, if you wish an advanced tutorial on this, the above image comes from this tutorial: Swept AABB Collision Detection and Response. It probably has everything you need.
In short, the input for your collision handling logic should be your objects old position, current size, its velocity, and all other collidable objects (their size and positions). The response should be the expected behavior of your object (i.e. the new velocity, corrected for 'avoiding' collision). That way you can easily test scenarios and your implementation with ease, consider this:
const player = {x: 0, y: 0, width: 10, height: 10, xvel: 12, yvel: 0};
// player.right = {x: player.x + player.width, y: player.y} // Your engine does this, right?
const collidableList = [{x: 15, y: 0, width: 5, height: 10}];
const newVelocity = handleCollision(player, collidableList);
I imagine that the player position is aligned at top left, so in this case you can predict that your new velocity ought to be xvel = 5, yvel = 0. Which means you just have to create a handleCollision that works for this test case, and then you could run multiple tests to make sure that the collision is behaving nicely on edge cases such as when nothing collides and when two things collides and one is closer than the other.
The main idea behind this collision is to find the velocity that is closest to zero as necessary to AVOID a collision in the next frame.
For example, imagine a scenario where the player is moving to the right. Let's disregard the Y axis because it should only be used to detect if the objects will collide in the future and will not influence the calculations of the horizontal velocity itself. If the player is moving to the right, it must check the distance between the left side of every object to the right side of the player, if this space is smaller than the velocity, it will obviously cause a collision, like this:
let targetVelocityX = player.xvel;
for (const collidable of ...) {
// ...
if (targetVelocityX > 0) {
// We are moving to the right so:
// Figure out how much space do we have between the objects
const leftOverSpace = collidable.left.x - player.right.x;
// Is our velocity larger than the space we have available?
if (targetVelocityX > leftOverSpace) {
// We must restrict the velocity because the player will collide otherwise
// We can only move as much as we have space available
targetVelocityX = leftOverSpace;
}
} else if (targetVelocityX < 0) {
// Moving to the left...
}
Using the test case I made before, we should get leftOverSpace = 5, which is smaller than our targetVelocityX = 12, so the new velocity will be 5, which will make it touch the collidable object, and in the next frame our player position will be x = 5 and xvel = 5, if we run it again the collision logic will tell us that the left over space is zero so the horizontal velocity will be set to zero which means we cannot move to the right anymore because we are touching the object.
I should remind you that this is different than the tutorial I linked above, the tutorial tries to find the time where the collision happened as a floating point number which is useful if you want to conserve the velocity to deflect a pong ball, or slide the object along, which are some of his examples.

Javascript sprites moving between two points

I'm wanting to get some sprites moving between two points in my (very basic) javascript game. Each sprite is randomly placed in the level, so I want them to move back and forth between their base position. I have the code below
function Taniwha(pos) {
this.basePos = this.pos;
this.size = new Vector(0.6, 1);
this.move = basePos + moveDist(5,0));
}
Taniwha.prototype.type = "taniwha"
var moveSpeed = 4;
Taniwha.prototype.act = function(step) {
this.move = ???
and this is where I get stuck. I'm not sure how to tell it to go left, back to base pos, right then back to base pos again (I plan to loop this). Does anyone have any advice? (also using Eloquen Javascript's example game as an outline/guide for this, if how I'm doing things seems odd!)
For horizontal movement, change x coordinate of the position.
var pos = { x: 1, y: 2 };
pos.x++ ; // This will move right
pos.x-- ; // This will move left
Likewise for vertical movement. You also need to update the coordinates after change for the object which you are drawing.
In truth ,there are lots of library to develop a game.
Use those, control a sprite is very easy.
Like:
Pixijs
CreateJS
Both of them are opensource project, you can watch and learn the source.
And have lots of examples and document.

Way to see if whole canvas has been painted in one color. Javascript + processing.js

I'm still a beginner at javascript, and I'm making a game about dying the whole screen white while the paint brush becomes smaller and smaller until in completely disappears.
I wanted to know, is there a simple way to figure out if the whole canvas has been painted, so I can put a winning screen?
I'm using the processing.js library, here is my code, if it's of any use:
background(255,0,0);
var eight = 100;
var draw = function(){
strokeWeight(eight);
point(mouseX,mouseY);
eight -= 0.2;
if(eight<0){
noStroke();
}
Here's a modestly efficient way of determining if the user has whited every pixel
Create an array where each canvas pixel is represented by an array element.
var pixels=new Array(canvas.width*canvas.height);
Initially fill the array with all zeros.
Create a variable that hold the # of unique pixels whited out so far.
var whited=0;
When the user passes over a pixel, see if the pixel has already been whited. If it hasn't been whited, change its array value to 1 and increment the whited variable.
var n = mouseY * canvas.width + mouseX
if(pixels[n]=0){
pixels[n]=1;
whited++;
}
You have a winner if the value of whited equals the number of pixels on the canvas.
if(whited==pixels.length){
alert('You have won!');
}
A thought: Instead of making the user find every (tiny) missed pixel, you might consider making a grid so the user has an easier time finding that 1 (larger) missed grid cell instead of finding one missed pixel in a sea of white.
You can go over all the pixels and check if they are not white
for (var i=0;i<imgData.data.length;i+=4)
{
if(imgData.data[i]==0&&imgData.data[i+1]==0&&imgData.data[i+2]==0&&imgData.data[i]+3==0){alert("white pixel")}
}
http://www.w3schools.com/tags/canvas_getimagedata.asp
Since you're using Processing, just walk over the pixels:
void setup() {
...
}
void draw() {
...
}
void yourCheckFunction() {
loadPixels();
boolean allWhite = true;
for(int c: pixels) {
if(brightness(c) < 255) {
// we found a not-white pixel!
allWhite = false;
break;
}
}
if (allWhite) {
// the paint surface is entirely white.
} else {
// there are non-white patches left
}
}
There are lots of ways to optimize this (like chopping up the surface into distinct areas with their own administrative true/false value so you can first check if they were all-white on a previous run, and if so, you don't need to recheck them) but this covers the basics:
assume the canvas is all white pixels
try to invalidate that assumption by finding a not-white pixel
immediately stop checking if you do
if there are none, your loop will end "naturally"
Alternatively, you can track how many pixels your user's action have painted. Once that number of pixels is equal to width*height, all pixels must necessarily be white (see markE's answer for that)

Snake game algorithm that doesn't use a grid and the segments can move slower than their dimensions

I'm trying to do exactly what this guy is doing but I think he may have less requirements than me because the answers in that post don't seem like they'd work for my game. Let me inline his algorithm so my question is easier to understand:
The easiest solution is to loop from tail to head and set the position of the current to the next segment's position, ie: segment[i].position = segment[i - 1].position
I think the key difference between our requirements is I want to move each piece less than its own width/height on every tick. For example:
Pretend these squares are aligned horizontally (I drew it unaligned because I think it makes it easier to see what's going on). The red H is where the head currently is. The red T is where the tail currently is. The black H' is where the head should be next tick. So the snake is moving right to left. If I use the algorithm described above, won't the segments overlap or start driving apart? Let me play it out step by step:
Create H'.position
T.position = H.position
H.position = H'.position
The result of this would become:
What algorithm can I use to make sure that all the pieces move at the same speed and stay the same distance apart from each other?
I'll give my idea, but I'm skeptical of it because my research doesn't show anyone else using this:
Each segment will store it's coordinates and its direction. EG: [[50, 50, LEFT], [100, 50, LEFT]]. The head is index 0, the tail is index 1. The speed at which the snake moves is 10, even though the segments are 50x50.
Each tick I'll do this:
If the mouse was pressed, overwrite the nextDirection variable with the direction that was pressed. Otherwise, nextDirection is whatever it was last tick. In this example, lets assume someone pressed UP.
Iterate the array and apply the direction to each tick. EG: [[40, 50, LEFT], [90, 50, LEFT]]
Shift the directions from head to tail and set the head's new direction to nextDireciton. EG: [[40, 50, UP], [90, 50, LEFT]]
Repeat every tick.
Does this algorithm seem like it would work? Part of the reason I'm asking is because it's more complicated than the other guy's algorithm so I feel like I'm doing something wrong. And also, the more I think about his algorithm, the less it seems like it can work.
Rewording My Problem
Pretend each segment of the snake is a 20x20 pixel rectangle. Each tick, I want the head to move 5 pixels in some direction. How do I make sure all the segments stay touching each other?
#Rafe's description below in a comment is:
Consider the ordered set of locations occupied by the snake at step t, with the leftmost being the tail and the rightmost being the head: {A, B, C, D, E}. At step t+1, the ordered set of locations occupied by the snake is {B, C, D, E, F} where the tail has moved from A to B and the head has moved from E to F.
And I don't think this algorithm works because:
Yes but if the width of A = B = C = D = E = F = 20px. Shifting {A, B, C, D, E} so that it becomes {B, C, D, E, F} just added 20px to the right side and removed 20px from the left side. Doesn't this mean he moved 20px, not 5px? I want to move 5px, not 20px If you're saying that's accomplished with your suggestion, please explain how. I don't see it.
I know you're ignoring the grid but what about making the distance the snake moves forward some number that factors your big block size evenly?
Let's say that you're moving forward only half of the width/height of your block.
Now you can optimize a bit.
Start a counter, n, that runs throughout the game.
Display the blocks that comprise the snake (n = 0). Let's call this snake_even.
Next move (n = 1), create the blocks that comprise the next snake by moving them all forward 1/2 unit. Let's call this snake_odd.
For all subequent moves, you display either snake-even or snake_odd, but you can create snake_even(n+2) from snake_even(n), or snake_odd(n+2) from snake_odd(n) just by changing the head to the new position and writing over the tail.
Whenever the snake eats something, add the length to whichever snake_xxxx you're on, and then add the length to the other one.
If you want to move forward only 1/5th of the height, you'd do the same thing, but you'd have five arrays to keep track of instead of two.
Additional info based on your added example (20x20 px segments moving 5 px each step):
Take a 4-segment snake moving to the right. Segment sizes are 20x20 px, and they move 5 px per move. I'll define the snakes as a list of coordinates (x, y). I'll mark the head with 'H' and the snake will cycle by moving right in the list, cycling back to the beginning if needed.
// n = 0
snake_0 => H(60, 0), (40, 0), (20, 0), (0, 0)
// Snake is moving to the right.
// n = 1 -- construct snake_1 from snake_0 and display that one (n % 4 = 1)
snake_1 => H(65, 0), (45, 0), (25, 0), (5, 0)
// n = 2 -- construct snake_2 from snake_1 and display that one (n % 4 = 2)
snake_2 => H(70, 0), (50, 0), (30, 0), (10, 0)
// n = 3 -- construct snake_3 from snake_2 and display that one (n % 4 = 3)
snake_3 => H(75, 0), (55, 0), (35, 0), (15, 0)
// n = 4 -- Now just move the head, and re-use all but the tail of snake_0
snake_0 => (60, 0), (40, 0), (20, 0), H(80, 0)
// n = 5
snake_1 => (65, 0), (45, 0), (25, 0), H(85, 0)
// n = 6
snake_2 => (70, 0), (50, 0), (30, 0), H(90, 0)
// etc.
Now one thing I forgot to take into account was the direction each segment needs to move. That could be stored right alongside the coordinate. But I think that part you probably understand already.
Here's how I implemented this algorithm:
Basically, I created a Segment class. Each one has a list of pivots. Whenever the head changes direction, I populate the list of pivots in each of the segments. Whenever a segment collides with a pivot, it changes to the direction of the segment in front of it. I then remove that pivot from it's list.
The one downside is you've got to make it so the snake moves by a factor of its own size, otherwise it won't collide with the pivot.
so, the snake's head moves freeform, and You check for self-collisions?
The easiest way I see is have a list of positions of the snake head in subsequent frames, then draw the rest of segments at every k-th position from previous head positions. Then at the next frame, You append another head position to the list, and again draw the remaining segments at every k-th position.
If the snake speed is variable, You will have to also vary k, to make the snake not stretch (also possibly interpolate between the head positions). This is simple math.
In his situation, each segment just moved to where its predecessor was one tick back.
Think of the segments having a solid bar between their centers. If that isn't correct the snake will stretch and bunch up as it moves.
The difference between you and him is that he had longer solid bars so there wasn't overlap.
But if the bar is the length of a movement, each segment will just go where the previous segment was one tick ago. The head is the exception and will move the direction specified by the last mouse click.
Now ... what if the length of the bar is longer than or shorter than the length of a movement.
I think, in that case, you would have to move the head first and then move each segment so it is still the same distance from the previous segment (the one closer to the head).
The hard part is caused by the movement amount not being equal to the distance. When the head changes direction, the 2nd segment has to change both X and Y or it will not be the same distance from the head as it was.
I suggest you:
Calculate where the H' is. This is the position of the head after the tick.
Calculate the direction from T to H' and
move T along that vector until it is the right distance from H.
Useful part ends here ...
This Part (below here) Isn't Right
I'll leave it here because what is wrong is that it was moving the tail the same distance the head moved. What it should do is move the tail to where it is the same distance from the head as it was before. ('oneTick' isn't relevant for the tail. We should set the distance between segments at the start and use that length.)
Assume 'oneTick' is the length to move per tick. And 'direction' is UP, DOWN, LEFT or RIGHT
if (direction == UP) H'.x = H.x - oneTick;
else if (direction == LEFT) H'.y = H.y - oneTick;
... more elses for the other directions ...
ratio = (H'.x - T.x) / (H'.y - T.y);
dy2 = oneTick * oneTick / (ratio * ratio + 1);
dy = Math.sqrt(dy2);
dx = ratio * dy;
T'.x = T.x + dx; // or minus depending on direction
T'.y = T.y + dy; // or minus ...
The direction has to do with the sign of H'.y - T.y (for y) and similarly for x.
From the specification, if you consider the set of locations occupied by the snake, only the head and tail change at each step. Therefore you could use a circular buffer to represent the snake and update its position in O(1) time rather than O(n).

How can a large canvas have an animated 'viewable area'

The question title may be vague. Basically, imagine a racing game built in canvas. The track takes up 10,000 x 10,000 pixels of screen space. However the browser window is 500 x 500 pixels. The car should stay centered in the browser and the 'viewable' area of the 10,000 x 10,000 canvas will change. Otherwise the car would just drive off the edge at disappear.
Does this technique have a name?
What are the basic principles to make this happen?
If the car should stay at the same position (relative to the canvas' position), then you should not move the car. Instead, move the background picture/track/map to the other side.
Causing your eyes to think the car moves right can be done by either moving the car to the right, or by moving the map to the left. The second option seems to be what you want, since the car won't move whereas the viewable area (i.e. the map) will.
This is a quick demo from scratch: http://jsfiddle.net/vXsqM/.
It comes down to altering the map's position the other way round:
$("body").on("keydown", function(e) {
if(e.which === 37) pos.x += speed; // left key, so move map to the right
if(e.which === 38) pos.y += speed;
if(e.which === 39) pos.x -= speed;
if(e.which === 40) pos.y -= speed;
// make sure you can't move the map too far.
// clamp does: if x < -250 return -250
// if x > 0 return 0
// else it's allowed, so just return x
pos.x = clamp(pos.x, -250, 0);
pos.y = clamp(pos.y, -250, 0);
draw();
});
You can then draw the map with the position saved:
ctx.drawImage(img, pos.x, pos.y);
If you're looking for a way to actually move the car when the map cannot be moved any further (because you're driving the car close to a side of the map), then you'd have to extend the clamping and also keep track of when the car should be moved and how far: http://jsfiddle.net/vXsqM/1/.
// for x coordinate:
function clamp2(x, y, a, b) { // x = car x, y = map x, a = min map x, b = max map x
return y > b ? -y : y < a ? a - y : x;
}
The position clamping then becomes a little more complex:
// calculate how much car should be moved
posCar.x = clamp2(posCar.x, posMap.x, -250, 0);
posCar.y = clamp2(posCar.y, posMap.y, -250, 0);
// also don't allow the car to be moved off the map
posCar.x = clamp(posCar.x, -100, 100);
posCar.y = clamp(posCar.y, -100, 100);
// calculate where the map should be drawn
posMapReal.x = clamp(posMap.x, -250, 0);
posMapReal.y = clamp(posMap.y, -250, 0);
// keep track of where the map virtually is, to calculate car position
posMap.x = clamp(posMap.x, -250 - 100, 0 + 100);
posMap.y = clamp(posMap.y, -250 - 100, 0 + 100);
// the 100 is because the car (circle in demo) has a radius of 25 and can
// be moved max 100 pixels to the left and right (it then hits the side)
Two things:
Canvas transformation methods
First, the canvas transformation methods (along with context.save() and context.restore() are your friends and will greatly simplify the math needed to view a portion of a large 'world`. If you use this approach, you can get the desired behavior just by specifying the portion of the world that is visible and the world-coordinates of everything you want to draw.
This is not the only way of doing things* but the transformation methods are meant for exactly this kind of problem. You can use them or you can reinvent them by manually keeping track of where your background should be drawn, etc., etc.
Here's an example of how to use them, adapted from a project of mine:
function(outer, inner, ctx, drawFunction) {
//Save state so we can return to a clean transform matrix.
ctx.save();
//Clip so that we cannot draw outside of rectangle defined by `outer`
ctx.beginPath();
ctx.moveTo(outer.left, outer.top);
ctx.lineTo(outer.right, outer.top);
ctx.lineTo(outer.right, outer.bottom);
ctx.lineTo(outer.left, outer.bottom);
ctx.closePath();
//draw a border before clipping so we can see our viewport
ctx.stroke();
ctx.clip();
//transform the canvas so that the rectangle defined by `inner` fills the
//rectangle defined by `outer`.
var ratioWidth = (outer.right - outer.left) / (inner.right - inner.left);
var ratioHeight = (outer.bottom - outer.top) / (inner.bottom - inner.top);
ctx.translate(outer.left, outer.top);
ctx.scale(ratioWidth, ratioHeight);
ctx.translate(-inner.left, -inner.top);
//here I assume that your drawing code is a function that takes the context
//and draws your world into it. For performance reasons, you should
//probably pass `inner` as an argument too; if your draw function knows what
//portion of the world it is drawing, it can ignore things outside of that
//region.
drawFunction(ctx);
//go back to the previous canvas state.
ctx.restore();
};
If you are clever, you can use this to create multiple viewports, picture-in-pictures, etc. of different sizes and zoom in and out on stuff.
Performance
Second, as I commented in the code, you should make sure your drawing code knows what portion of your larger 'world' will be visible so that you don't do a lot of work trying to draw things that will not be visible.
The canvas transformation methods are meant for solving exactly this kind of problem. Use 'em!
*You will likely have problems if your world is so large that its coordinates cannot fit in an appropriate integer. You'll hit that problem roughly when your world exceeds billion (10^9) or a long trillion (10^18) pixels in any dimension, depending on whether the integers are 32- or 64-bit. If your world isn't measured in pixels but in 'world units', you'll run into problems when your world's total size and smallest feature scale lead to floating point inaccuracies. In that case, you will need to do extra work to keep track of things... but you'll probably still want to use the canvas transformation methods!
My very first game was a racing game where I moved the background instead of the car and although I want to think now that I had my reasons to make it so... I just didn't know better.
There are a few techniques that you need to know to achieve this well.
Tiled background. You need to make your track out of smaller pieces that tiled. To To draw 10,000 x 10,000 pixels is 100MPix image usually such image will have 32bit depth (4 bytes) this will end up being 400MB in memory. Compressions like PNG, JPEG won't help you since these are made to store and transfer images. They cant be rendered to a canvas without decompressing.
Move the car along your track. There is nothing worst then moving the BG under the car. If you need to add more features to your game like AI cars... now they will have to move along the map and to implement car collisions you need to make some not hard but strange spacial transformations.
Add camera entity. The camera needs to have position and viewport size (this is the size of your canvas). The camera will make or break your game. This is the entity that will give you the sense of speed in the game... You can have a camera shake for collisions, if you have drifts if your game the camera can slide pass the desired position and center back to the car, etc. Of course the most important thing will be tracking the car. Some simple suggestions I can give you are to not put the car in dead center of the camera. put the car a little behind so you can see a bit more what's in front of your. The faster the car moves the more you should offset the camera. Also you can't just compute the position of the camera instead compute desired position and slowly per frame move the current camera position to the desired position.
Now when you have camera and a large tiled map, when you draw the tiles you have to subtrack the camera position. You can also compute which tiles are not visible and skip them. This technique will allow you do extend your game with even larger maps or you can stream your map where you don't have all the tiles loaded and load in advance on background (AJAX) what will be visible soon.

Categories