In this post we will look at a second way to move our graphics across the canvas. Click this link here to see the page we will be coding. I had to make some changes to the project we have been working on because this version moves so much faster than the first. We will talk about why later.
I also changed the size of the canvas to make the game board much wider. This gives us a chance to actually see which dogs are pulling ahead and which ones are falling behind.
We will let every dog finish the race rather than stopping when the first one crosses the line. And we will print the names of the first, second and third place winners.
On the left side of the web page you will see images of the six dogs. I put them there while I was writing the code to see exactly what the image was that I was capturing at the beginning of the game. You will recall from the earlier posts that we have made our canvas wider than the dark area that you see on the screen. The actual game board is smaller and is shifted to the right from the edge of the window. We use the variable canvasLeft to determine this gap. This makes the page look more visually pleasing but it also gives us a little bit of works space to test things on. It is important that it is a different background color so that we can easily see where the edges are on the images that we manipulate in the game. I have left them here so that you can see how I use this space during game development. This is even more clear if you step through the script using Firebug.
In the previous post our puppies appear to move because we a capturing a strip of the image with the dog somewhere in the middle and then placing it back on the screen in a new location shifted only two pixels right of the original. We do not know where the dog is in the final image, so we have to stop and check for a color other than the background in a key location at the finish line. We repeatedly recapture and reposition images as the dogs race across the screen.
In this version we must capture six images, one for each dog, and then keep track of where we are putting them on the screen. The images are smaller and once they are captured they do not change so we only need to do that once, when we are first setting up the game. We also need to keep track of the location of each dog. We will know where each trotter is, so we don't have to test the screen for the winner. Instead we can check a value.
To do these two things we will declare two arrays:
var hisSpot = new Array();Remember that an array is a list of values. So hisSpot[] will be a list of six numbers representing the X coordinate of the dog's image. dogImage[] will be a set of six images stored in computer memory. These values need to be global so that they can be used and manipulated by several functions in our script. You simply place the declarations anywhere in your script as long as it is not inside of one of your functions. Sometimes you might want to put all your declarations in the same place at the beginning of the script. As your code gets longer, it is sometimes more convenient to locate them near the functions that will use them so that debugging and editing is easier.
var dogImage = new Array();
You will find this code in the first function that gets called when the page is loaded. We only need to do this once.
// set start point for each dog and image array
for (var x = 0; x < 6; x++){
hisSpot[x] = canvasLeft;
dogImage[x] = 0;
}
dogCatcher();
We use a simple for loop to load hisSpot[] for each dog and give or dogImage[] an initial value. After these are initialized we call the function dogCatcher() to capture an image of each of the six puppies. Here it is.
function dogCatcher(){
for(var dog = 0; dog < 6; dog++){
dogImage[dog]=ctx.getImageData(hisSpot[dog],dog*60+15,70 ,55);
ctx.putImageData(dogImage[dog],0,dog*60);
}
}
We have to be sure that two things are done first. One: the dogs have to be already drawn on the canvas. Two: our array, hisSpot[], has to be filled with their locations. You will see that we are still multiplying our loop value, dog, times 60. That is the vertical spacing between the dogs. The +15 how far down the first dog is from the top of the canvas. The 70 is the width of the image that we need to capture, and the 55 is the height. The final line: ctx.putImageData(dogImage[dog],0,dog * 60); is for debugging. It puts a copy of the image that we just captured in the scratch space on the left side of the screen. If you experiment by changing the last two values in the line above it, you will see how handy this is to get just the image you need.
It is important that the dog itself is offset in the image so that there is more space behind his tail than there is infront of his nose. We need this background space to erase the tail on the canvas as the dog moves forward. That is why we set the original value at simply canvasLeft. You can experiment by adding and subtracting numbers in the getImageData line. For example:
hisSpot[dog] + 15Play around with this a little and it wll be very clear how that trailing background color makes the annimation work.
hisSpot[dog] - 15
In the next few posts we will look at other parts of this program and see how they each work. If you need it, here is the full script that you can copy and paste into your own editor.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="generator" content="CoffeeCup HTML Editor (www.coffeecup.com)">
<meta name="description" content="">
<meta name="keywords" content="">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width; initial-scale=1.0; user-scalable=1;">
<title>Dog Race</title>
<style>body{color:#FFFFFF}</style>
<script type="text/javascript">
/* Demo Script for 'Writing Games with Canvas in HTML5'
© Copyright 2013, Budd Churchward
*/
var canvasLeft = 120;
var myRed = "#FE0000"; //red - first value is 254
var myBlue = "#0200DD"; //blue - first value is 2
var myYellow = "#FCFF00"; //yellow - first value is 252
var myMagenta = "#660066"; //magenta - first value is 253
var myOrange = "#FF6600"; //orange is - first value is 255
var myGreen = "#06FF00"; //green is - first value is 6
var myBrown = "#993300"; //brown is - first value is 153
var myBackground = "#007777"; //background -- first value is 0
function canvasDogRaces(){
// Get the canvas element.
canvas = document.getElementById("myCanvas");
// Make sure you got it.
if (canvas.getContext){
ctx = canvas.getContext("2d");
clearCanvas(myBackground);
drawDog(myGreen, canvasLeft + 20, 10);
drawDog(myYellow, canvasLeft + 20, 70);
drawDog(myBrown, canvasLeft + 20, 130);
drawDog(myRed, canvasLeft + 20, 190);
drawDog(myBlue, canvasLeft + 20, 250);
drawDog(myMagenta, canvasLeft + 20, 310);
//draw the finish line
ctx.strokeStyle = 'black'
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo(canvasLeft + 1180-canvasLeft,15);
ctx.lineTo(canvasLeft + 1180-canvasLeft,380);
ctx.closePath();
ctx.stroke();
// set start point for each dog and image array
for (var x = 0; x < 6; x++){
hisSpot[x] = canvasLeft;
dogImage[x] = 0;
}
dogCatcher();
delayLoop=self.setInterval(function(){scooter(15)},20);
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
var delayLoop = 0;
var runningDogX = canvasLeft;
var testSpot = 0;
var hisSpot = new Array();
var dogImage = new Array();
var thisDog = new Array();
thisDog[0] = "Arlo";
thisDog[1] = "O'Yeller";
thisDog[2] = "Brownie";
thisDog[3] = "Red Rover";
thisDog[4] = "Bo";
thisDog[5] = "Max";
function showWinner(dog){
ctx.fillStyle='White';
ctx.font="24px Arial";
ctx.textAlign="left";
ctx.fillText(placeText[place] + thisDog[dog],canvasLeft + 20,40*place+30);
}
function goAgain(){
location.reload();
}
function dogCatcher(){
for(var dog = 0; dog < 6; dog++){
dogImage[dog] = ctx.getImageData(hisSpot[dog], dog * 60 + 15,70 ,55);
ctx.putImageData(dogImage[dog],0,dog * 60);
}
}
var leaderNose = 1230-canvasLeft;
var place = 0;
var placeText = new Array();
placeText[0] = "1st Place: ";
placeText[1] = "2nd Place: ";
placeText[2] = "3rd Place: ";
function scooter(){
var step = 1;
var keepGoing = 0;
for(var dog = 0; dog < 6; dog++){
if(hisSpot[dog]>0){
step = Math.floor(Math.random() * 8)+1;
hisSpot[dog] = hisSpot[dog] + step;
ctx.putImageData(dogImage[dog],hisSpot[dog],dog * 60 + 15);
keepGoing++;
if(hisSpot[dog]>leaderNose){
//clearInterval(delayLoop);
hisSpot[dog]=0;
//leaderNose = leaderNose - 5;
if(place<3){showWinner(dog); }
place++;
ctx.fillText(place,1180,dog*60 + 60);
}
}
}
if(keepGoing==0){clearInterval(delayLoop); }
}
function myDogSpot(x,y){
ctx.putImageData(dogImage,x, y);
}
function drawDog(thisColor,dogX,dogY){
// draw tail
ctx.beginPath();
ctx.lineWidth = 3;
ctx.strokeStyle = 'black';
ctx.moveTo(dogX + 5,dogY+32);
ctx.lineTo(dogX-10,dogY + 25);
ctx.closePath();
ctx.stroke();
// draw body
ctx.fillStyle = thisColor;
ctx.beginPath();
ctx.rect(dogX+8,dogY + 30,24,16);
ctx.rect (dogX,dogY + 35, 10, 25);
ctx.rect (dogX + 30,dogY + 35, 10, 25);
ctx.fill();
ctx.arc(dogX + 8, dogY + 38, 8, 0, 2*Math.PI, true);
ctx.arc(dogX + 32, dogY + 38, 8, 0, 2*Math.PI, true);
ctx.closePath();
ctx.fill();
// draw head
ctx.beginPath();
ctx.arc(dogX + 36, dogY + 22, 8, 0, 2*Math.PI, true);
ctx.closePath();
ctx.fill();
ctx.beginPath();
ctx.arc(dogX + 40, dogY + 22, 8, 0, Math.PI, false);
ctx.closePath();
ctx.fill();
// draw nose
ctx.fillStyle = 'black';
ctx.beginPath();
ctx.rect(dogX +46,dogY +22,3,3);
ctx.closePath();
ctx.fill();
// draw eye
ctx.beginPath();
ctx.rect(dogX +40,dogY +20,3,3);
ctx.closePath();
ctx.fill();
// draw ear
ctx.beginPath();
ctx.arc(dogX + 30, dogY + 18, 4, Math.PI, 0.2 *Math.PI, false);
ctx.closePath();
ctx.fill();
}
function clearCanvas(thisColor){
ctx.fillStyle = thisColor;
ctx.rect(canvasLeft,0,1100,400);
ctx.fill();
}
</script>
</head>
<body onload="canvasDogRaces()" bgcolor="#339999">
<h3>
<div id="rowOne_text">Welcome to the Races</div>
<div id="rowTwo_text">Pick Your Dog</div>
</h3>
<canvas id="myCanvas" width="1200" height="400" ;">
</canvas>
</body>
</html>
No comments:
Post a Comment