Let's look at how we can move one of our dogs closer to the finish line. Our example project is relatively simple because the objects in our game never collide with each other. They move along in an orderly manner on their own 'track' until they reach a finish line. We will simply capture a strip of the canvas that contains one of the dogs. Then we will put it back but shifted over 2 pixels, so that the dog will appear to be in a new location.
The image you see above is the actual image that we are capturing. Many captures earlier, the dog was on the far left. Now it is nearly to the finish line. You do not see the finish line in our image, because we don't want it to appear to be moving. We capture this section just a few pixels short of the finish line so when it is put back on the screen, the line is not disturbed.
We should be clear about this. We are not taking anything off of the canvas, we are simply copying a section of it and storing it in memory. Then we put it into the canvas image in a new location. That covers up the original puppy so you don't see any trailing bits of dog dribbled across the screen.
You might think of this as a Copy and Paste function.
Almost any game you create will require an element of unpredictability. We need to chose a dog at random, move him a bit, then chose another and do the same. We repeat this over and over until one of the dog reaches the finish line. We do this with a random number.
Ah! But the image is always the same size and we always put it back in the same place, not far from where we got it. So how do we know when one of the dog reaches the end of the race? This is where our coding gets clever. We are going to capture another image, a small one. It will come from just in front of the finish line. We will store it in a variable testSpot and then check the very first byte in the data to see if it is not our background. REMEMBER the first byte of our background is Hex 00. So if the first byte of this test image is not zero, it has to be the color of one of our dogs. GAME OVER!
Let's start with the random numbers. We have six dogs. Like all things computer, we number them 0 through 5. Look at this code:
var dog = Math.floor(Math.random() * 6);
We establish a variable called dog and then give it a random value from 0 to 5. This variable will then be used to mathematically determine the Y axis for all the image manipulation. In effect, it identifies which of the six dogs were are going to move at this moment. The X axis of the image location is the same for every dog, so that never changes. The line: dogCatcher(dog); calls the following function which will capture our image:
function dogCatcher(dog){
dogImage = ctx.getImageData(canvasLeft, dog * 60 + 15, 577, 55);
}
We have our usual players in here. The ever present canvasLeft references the edge of our game board. dog is the random number we sent to this function. First we multiply it by 60, which is the distance between two dogs: tail to tail, nose to nose. It represents the Y axis of our graphic. We add 15 to the value because our first dog is dropped a little more than 15 pixels down from the top of the canvas. The 577 is the width of the image we want to capture and the 55 is the height.
Notice that for the first dog, we multiply 0 x 60 = 0 and then 0 + 15 = 15. The second dog will be 1 x 60 = 60 and 60 + 15 = 75. This technique allows us to capture a strip of the canvas that is 557 pixels wide and 55 pixels high that will contain one of our dogs somewhere in the middle. Just like the one you see above.
We are grabbing an image every 60 pixels up and down the screen that is only 55 pixels high. That leaves a 5 pixel gap between them that is undisturbed. We could use that to draw in lines, if we wanted, separating the tracks the dogs run in.
Once we have stored the image we can write it back to the canvas with this line:
myDogSpot(canvasLeft + 2, dog * 60 + 15);
Of course this line doesn't put the image on the screen itself, it calls a function that we have used before that does that for us. Notice that the math here is nearly the same. dog * 60 + 15 is the Y axis and it is identical to what we captured. But this time we have increased the X axis a very small amount: canvasLeft + 2. As you experiment with these numbers, play around with the + 2 and you can see that it makes a big difference on how fast your puppies trot. But it is really the distance that you are changing.
Now let's talk about the finish line.
We can use the same random value, dog, to pick a spot of the screen to test. In our simple project we don't need to worry about any of the other dogs only the one we just moved. So we will get a bit of the image again:
testSpot = ctx.getImageData(canvasLeft+575,dog * 60 + 31,10,10);
testSpot is a variable that we declared earlier. It is a place to park the data so we can peek into it and check for the winning pouch. You will see canvasLeft in there again. This time we adding 575 to it because the finish line is at the other edge of the canvas. We still multiply dog * 60 for the same reason we did above. This time we are adding 31 to that value. I figured that out by trial and error. It should pickup the color of the dog's snout right behind it's black nose. The size here, 10 by 10, could be anything, 4 by 4 or 100 by 100. It doesn't matter because we only care about the very first byte, which is the 'red' value of the pixel in the upper left corner of the rectangle. It will be: testSpot.data[0]. Here is how we use it:
if (testSpot.data[0] > 0){
clearInterval(delayLoop);
}
We just keep checking that first byte and the first time it isn't zero, GAME OVER, we have a winner. So we clear the interval and everything stops.
Find the line that reads:
ctx.putImageData(testSpot,20,dog * 60 + 30);
I put that in there just for debugging purposes. Notice that there is no canvasLeft in it. No, this time our Y axis is simply 20. This allows us to park a tiny image on the scratch space to the left of the game and actually see for our self if part of the dog is in there or not. I used it for my trial and error to find the right place on the board to catch the first dog as her nose reaches the finish line. We will want to get rid of this line when the project is completed.
You will be able to copy the complete code below. As you experiment with it on your own computer, add a break point in Firebug so you can watch the test image yourself. Also, you might find it interesting to actually select testSpot.data[0]and watch it's value. Firebug will let you see all the bytes in the image data which should help you learn even more about how a pixel image is stored in the computer.
CU Next Time ...
<!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">
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);
ctx.strokeStyle = 'black'
ctx.beginPath();
ctx.lineWidth = 1;
ctx.moveTo(canvasLeft + 580,15);
ctx.lineTo(canvasLeft + 580,380);
ctx.closePath();
ctx.stroke();
delayLoop=self.setInterval(function(){scooter(15)},2);
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
var dogImage = 0;
var delayLoop = 0;
var runningDogX = canvasLeft;
var testSpot = 0;
function scooter(){
var dog = Math.floor(Math.random() * 6);
dogCatcher(dog);
myDogSpot(canvasLeft + 2, dog * 60 + 15);
testSpot = ctx.getImageData(canvasLeft+575,dog* 60 + 31,10,10);
ctx.putImageData(testSpot,20,dog * 60 + 30);
if (testSpot.data[0] > 0){
clearInterval(delayLoop);
}
}
function goAgain(){
location.reload();
}
function dogCatcher(dog){
dogImage = ctx.getImageData(canvasLeft, dog * 60 + 15, 577, 55);
}
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,600,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="840" height="400" ;">
</canvas>
</body>
</html>
Sunday, December 16, 2012
Wednesday, December 12, 2012
On your mark, get set, GO!
OK, if you have been following this blog, I hope you have copied and pasted the code from the earlier post and seen the dog's all stacked up and ready to race across the screen. Let's talk now about how we are going to move them.
You might think that we could somehow erase the image that is there and then draw it again in a new spot. We could do that, but it wouldn't be very efficient and might look pretty jerky on some screens.
The method that I prefer is to actually capture a small rectangle of the canvas and then put it back in a new location. To do that we will use getImageData() and putImageData().
We will need to declare a variable that will hold the image data. And we need to tell Javascript the location and the size of the image we want to capture. It goes like this:
var dogImage = ctx.getImageData(x, y, w, h);
x and y are the coordinates of the top left corner of the image we want to capture. w and h are the width and height.
To put the image back on the canvas we do this:
ctx.putImageData(dogImage, newX, newY);
So the image on the left shows you how we can use this work space that we set up next to our game background. The code you find at the bottom captured a section of the canvas using the following function. You can play with the numbers and make it capture only one whole dog.
function dogCatcher(){
dogImage = ctx.getImageData(canvasLeft, 15, 80, 80);
}
And the following function is used to put a copy of it in our scratch space. We are using different background colors so it is easy to see how much of the image we are grabbing.
function myDogSpot(){
ctx.putImageData(dogImage,0, 0);
}
These two lines are added to the first function at the beginning of the script. Here is the whole thing. Copy and paste it into your editor and give it a try. I will let you do some trial and error to get the image you want.
Try for something like the one you see here. There is a reason I have more background on the left of the dog than on the right. We need that so we can overlap the previous image and erase the tail end of the dog as we move it forward across the screen. We will do that in the next post. I have highlighted the new lines of code below, so you can simply add them to the code you copied last time if you prefer.
<!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">
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);
dogCatcher();
myDogSpot();
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
var dogImage = 0;
function dogCatcher(){
dogImage = ctx.getImageData(canvasLeft, 15, 80, 80);
}
function myDogSpot(){
ctx.putImageData(dogImage,0, 0);
}
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,600,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="840" height="400" ;">
</canvas>
</body>
</html>
You might think that we could somehow erase the image that is there and then draw it again in a new spot. We could do that, but it wouldn't be very efficient and might look pretty jerky on some screens.
The method that I prefer is to actually capture a small rectangle of the canvas and then put it back in a new location. To do that we will use getImageData() and putImageData().
We will need to declare a variable that will hold the image data. And we need to tell Javascript the location and the size of the image we want to capture. It goes like this:
var dogImage = ctx.getImageData(x, y, w, h);
x and y are the coordinates of the top left corner of the image we want to capture. w and h are the width and height.
To put the image back on the canvas we do this:
ctx.putImageData(dogImage, newX, newY);
So the image on the left shows you how we can use this work space that we set up next to our game background. The code you find at the bottom captured a section of the canvas using the following function. You can play with the numbers and make it capture only one whole dog.
function dogCatcher(){
dogImage = ctx.getImageData(canvasLeft, 15, 80, 80);
}
And the following function is used to put a copy of it in our scratch space. We are using different background colors so it is easy to see how much of the image we are grabbing.
function myDogSpot(){
ctx.putImageData(dogImage,0, 0);
}
These two lines are added to the first function at the beginning of the script. Here is the whole thing. Copy and paste it into your editor and give it a try. I will let you do some trial and error to get the image you want.
Try for something like the one you see here. There is a reason I have more background on the left of the dog than on the right. We need that so we can overlap the previous image and erase the tail end of the dog as we move it forward across the screen. We will do that in the next post. I have highlighted the new lines of code below, so you can simply add them to the code you copied last time if you prefer.
<!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">
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);
dogCatcher();
myDogSpot();
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
var dogImage = 0;
function dogCatcher(){
dogImage = ctx.getImageData(canvasLeft, 15, 80, 80);
}
function myDogSpot(){
ctx.putImageData(dogImage,0, 0);
}
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,600,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="840" height="400" ;">
</canvas>
</body>
</html>
Problems with Motion in Canvas and HTML5
If you have kept up with the previous posts and tried the code out for yourself you are ready to see things move on our canvas. Up to now, HTML5 has served us well and we are finding all the right tools to do the jobs we need to do. Unfortunately, that isn't the case when you want to create a simple animation and move objects across the screen.
Let's start with what might look like the obvious way to do this task and see just why it doesn't work.
Add the highlighted line below after the two that come before it.
dogCatcher();
myDogSpot(0,0);
scooter(15);
We will be adding a function that will move our dogs across the screen. We will call it scooter and pass one variable to it which will be the Y axis of our graphics. It determines how far down the canvas our moving image will be placed.
Let's set up a simple FOR LOOP in the new function and increment the graphic across the screen:
function scooter(dogY){
for (var i = 0; i < 500; i++){
myDogSpot(canvasLeft + i, dogY)
}
}
If you try this out you should see that the page loads and the first dog suddenly appears at the other side of the game board. There is no illusion of motion here. Things happen so quickly that the image seems to just jump from one side of the screen to the other. What we would like here is a simple delay that we could call that would slow the execution of the code down long enough to give our eyes a chance to see the image moving across the screen.
Unfortunately, there is nothing in JavaScript that does that for us. There are, however, some functions that come close.
We will use the first of the two. We will turn our interval timer on by changing:
scooter(15);
to:
delayLoop=self.setInterval(function(){scooter(15)},20);
This calls the same routine, which we will rewrite, once every 20 milliseconds. We will get rid of the FOR LOOP because this new line above will do the job of calling the routine for us over and over.
We will need to declare a variable to hand the X coordinate used for placing the image on the screen. Here is the new function:
var delayLoop = 0;
var runningDogX = canvasLeft;
function scooter(y){
runningDogX = runningDogX + 2;
myDogSpot(runningDogX,y);
if (runningDogX > 640){
clearInterval(delayLoop);
}
}
The new function is called repeatedly until runningDogX is greater than 640. When that happens, the interval is shut down.
You will find the full code of this example at the bottom of this post. You can copy it and paste it into your own editor. Experiment with it. Change the values and check out the result.
This routine is very similar to many examples that you will find on the web. Like those examples, this one works fine because there are no more instructions after we turn on the interval timer. As the code for our project expands, it will soon be clear that this creates many problems. JavaScript doesn't stop executing instructions while it waits for the interval timer to clock itself out. It just keeps right on with whatever you have next. When the interval times out, it stops what it's doing and executes the function we have called.
To make practical use of this tool, we have to be sure that we write our code so that JavaScript can't find anything it needs to do until after function scooter() has finished moving our image. This task is not as simple as you might think and may result in surprises on you screen that are difficult to debug. For now, give it a try.
<!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">
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);
dogCatcher();
myDogSpot(0,0);
delayLoop=self.setInterval(function(){scooter(15)},20);
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
var dogImage = 0;
var delayLoop = 0;
var runningDogX = canvasLeft;
function scooter(y){
runningDogX = runningDogX + 2;
myDogSpot(runningDogX,y);
if (runningDogX > 640){
clearInterval(delayLoop);
}
}
function dogCatcher(){
dogImage = ctx.getImageData(canvasLeft, 15, 70, 400);
}
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,600,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="840" height="400" ;">
</canvas>
</body>
</html>
Let's start with what might look like the obvious way to do this task and see just why it doesn't work.
Add the highlighted line below after the two that come before it.
dogCatcher();
myDogSpot(0,0);
scooter(15);
We will be adding a function that will move our dogs across the screen. We will call it scooter and pass one variable to it which will be the Y axis of our graphics. It determines how far down the canvas our moving image will be placed.
Let's set up a simple FOR LOOP in the new function and increment the graphic across the screen:
function scooter(dogY){
for (var i = 0; i < 500; i++){
myDogSpot(canvasLeft + i, dogY)
}
}
If you try this out you should see that the page loads and the first dog suddenly appears at the other side of the game board. There is no illusion of motion here. Things happen so quickly that the image seems to just jump from one side of the screen to the other. What we would like here is a simple delay that we could call that would slow the execution of the code down long enough to give our eyes a chance to see the image moving across the screen.
Unfortunately, there is nothing in JavaScript that does that for us. There are, however, some functions that come close.
- setInterval() - executes a function, over and over again, at specified time intervals
- setTimeout() - executes a function, once, after waiting a specified number of milliseconds
We will use the first of the two. We will turn our interval timer on by changing:
scooter(15);
to:
delayLoop=self.setInterval(function(){scooter(15)},20);
This calls the same routine, which we will rewrite, once every 20 milliseconds. We will get rid of the FOR LOOP because this new line above will do the job of calling the routine for us over and over.
We will need to declare a variable to hand the X coordinate used for placing the image on the screen. Here is the new function:
var delayLoop = 0;
var runningDogX = canvasLeft;
function scooter(y){
runningDogX = runningDogX + 2;
myDogSpot(runningDogX,y);
if (runningDogX > 640){
clearInterval(delayLoop);
}
}
The new function is called repeatedly until runningDogX is greater than 640. When that happens, the interval is shut down.
You will find the full code of this example at the bottom of this post. You can copy it and paste it into your own editor. Experiment with it. Change the values and check out the result.
This routine is very similar to many examples that you will find on the web. Like those examples, this one works fine because there are no more instructions after we turn on the interval timer. As the code for our project expands, it will soon be clear that this creates many problems. JavaScript doesn't stop executing instructions while it waits for the interval timer to clock itself out. It just keeps right on with whatever you have next. When the interval times out, it stops what it's doing and executes the function we have called.
To make practical use of this tool, we have to be sure that we write our code so that JavaScript can't find anything it needs to do until after function scooter() has finished moving our image. This task is not as simple as you might think and may result in surprises on you screen that are difficult to debug. For now, give it a try.
<!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">
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);
dogCatcher();
myDogSpot(0,0);
delayLoop=self.setInterval(function(){scooter(15)},20);
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
var dogImage = 0;
var delayLoop = 0;
var runningDogX = canvasLeft;
function scooter(y){
runningDogX = runningDogX + 2;
myDogSpot(runningDogX,y);
if (runningDogX > 640){
clearInterval(delayLoop);
}
}
function dogCatcher(){
dogImage = ctx.getImageData(canvasLeft, 15, 70, 400);
}
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,600,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="840" height="400" ;">
</canvas>
</body>
</html>
Monday, December 10, 2012
Canvas Graphics - How'd He Do That?
So wait a minute. How did I know where to put those body parts? How did I get the tail and the eye and the ear all in the right place?
The truth of the matter is, it is all done with trial and error. Oh sure, I can crunch numbers and figure out where things are in relation to my reference point. But it is much easier to just make an educated guess and then load the page and see where it lands.
To do the ear, for example, I messed around with drawing part of a circle to get the shape I wanted. I had it no where near the dog's head while I was doing that. Once I had a shape I liked, I started changing the X coordinate to slide it above the dog's head. Then I brought it down by increasing the Y coordinate.
I make a small change, save the code, and reload the page. Doing this I can nudge the thing into a place that I like.
I pretty much did that with all the parts. There is nothing wrong with trial and error.
The truth of the matter is, it is all done with trial and error. Oh sure, I can crunch numbers and figure out where things are in relation to my reference point. But it is much easier to just make an educated guess and then load the page and see where it lands.
To do the ear, for example, I messed around with drawing part of a circle to get the shape I wanted. I had it no where near the dog's head while I was doing that. Once I had a shape I liked, I started changing the X coordinate to slide it above the dog's head. Then I brought it down by increasing the Y coordinate.
I make a small change, save the code, and reload the page. Doing this I can nudge the thing into a place that I like.
I pretty much did that with all the parts. There is nothing wrong with trial and error.
Sunday, December 9, 2012
Drawing a Game Graphic on Canvas
Let's take a look at how we can draw graphics on our Canvas Element.
On the right you see the finished dog. To create this we write a function that will draw it in parts. We may want to draw more dogs that are each a different color, and we may want to put them in different places on the canvas. To do that, we will set up some parameters that will allow us to pass three variables to the function.
To begin, we will set up our colors. As you will recall in an earlier post, these colors are all set up so that the first byte of each pixel is a unique value. Also none of them have a zero in the first byte. This will be important later when we start racing our dogs and we want to know which one is the winner.
We make sure that these variables are defined outside of all functions. That will make them global variables, which means every function can use them.
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
Now we are ready to create the function. Here is how we are going to call it:
drawDog(myGreen, canvasLeft + 20, 10);
We choose anyone of our colors. Next we tell the function how far to the right we want to draw. This is our X reference and it will always be linked to the variable canvasLeft. This means that we can change one value and shift all of our graphics left or right on the screen. The Y value is always linked to 0, the top of our canvas. These two coordinates form the X,Y location for the graphic. All the pieces will be relative to them when we start drawing.
Here is how we would define the function:
function drawDog(thisColor, dogX, dogY){ ....}
You can put the following sections into your own script and test them out, or you might simply copy the whole thing that is listed at the bottom and then use Firebug to step through the script and see how each part comes together.
Let's start with the dog's tail. The graphic is created by starting and ending a path. We draw a simple line. We set the width of the line as 3 pixels. The tail of the dog will be black, no matter what color the body is. We just a moveTo and a lineTo to create the graphic. The first determines where we start and draws no line. The second draws the line to the end point. Notice that as you step through the code, the line does not appear until the final line ctx.stroke(); is executed.
// 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();
To create the body, we will draw three rectangles. Now we will switch to the color that was passed into the function. We do that with ctx.fillStyle = thisColor;
Each of the three rectangles are referenced to the dogX and dogY coordinates that we passed to the function along with the color. The values used in the rectangle refer to these four parts: (location X, location Y, width, height). So the first two values are the location of the the upper left corner of the rectangle. The last two values define it's size.
As you step through the code, you will see, again, that nothing appears until the final line: ctx.fill();
// 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();
Now we will round out the two ends of the dog. The reason we put the tail in first, is so that we can cover the doggy end with this semi-circle and make it look like it was shaped exactly to match the curve.
We will use ctx.arc() to draw two semicircles. They look like northern hemispheres cut off below the equator. We lay them onto of the body so it only looks like a quarter circle, but it was easier to use the same code twice. The only thing that changes is the X coordinate.
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();
That arc stuff can look confusing. The first two values are the X, Y location of the center of a circle. You have to watch out for that when putting them together with rectangles. Rectangles are referenced from their corner, circles from the middle!
The third value is the radius of the circle: 8 pixels.
The next two determine beginning and ending points of the arc. Here's an image from w3schools.com that explains it all.
The last value, true, simply determines whether we are drawing clockwise or counter-clockwise.
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();
To do the dog's head, we will add another full circle a smaller half circle. Then we dress it up with a black nose, eye and ear.
// 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();
You can copy and paste the whole code below into your editor and experiment the script. You will see that I have moved the code from earlier that cleared our canvas into it's own function and set it up so we can pass a color to it as well. This will allow you to try different background colors and do some creative messing around. If you play with the background, remember that the first byte will need to be 00 for our animation needs later.
<!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">
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);
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
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,600,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="840" height="400" ;">
</canvas>
</body>
</html>
On the right you see the finished dog. To create this we write a function that will draw it in parts. We may want to draw more dogs that are each a different color, and we may want to put them in different places on the canvas. To do that, we will set up some parameters that will allow us to pass three variables to the function.
To begin, we will set up our colors. As you will recall in an earlier post, these colors are all set up so that the first byte of each pixel is a unique value. Also none of them have a zero in the first byte. This will be important later when we start racing our dogs and we want to know which one is the winner.
We make sure that these variables are defined outside of all functions. That will make them global variables, which means every function can use them.
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
Now we are ready to create the function. Here is how we are going to call it:
drawDog(myGreen, canvasLeft + 20, 10);
We choose anyone of our colors. Next we tell the function how far to the right we want to draw. This is our X reference and it will always be linked to the variable canvasLeft. This means that we can change one value and shift all of our graphics left or right on the screen. The Y value is always linked to 0, the top of our canvas. These two coordinates form the X,Y location for the graphic. All the pieces will be relative to them when we start drawing.
Here is how we would define the function:
function drawDog(thisColor, dogX, dogY){ ....}
You can put the following sections into your own script and test them out, or you might simply copy the whole thing that is listed at the bottom and then use Firebug to step through the script and see how each part comes together.
Let's start with the dog's tail. The graphic is created by starting and ending a path. We draw a simple line. We set the width of the line as 3 pixels. The tail of the dog will be black, no matter what color the body is. We just a moveTo and a lineTo to create the graphic. The first determines where we start and draws no line. The second draws the line to the end point. Notice that as you step through the code, the line does not appear until the final line ctx.stroke(); is executed.
// 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();
To create the body, we will draw three rectangles. Now we will switch to the color that was passed into the function. We do that with ctx.fillStyle = thisColor;
Each of the three rectangles are referenced to the dogX and dogY coordinates that we passed to the function along with the color. The values used in the rectangle refer to these four parts: (location X, location Y, width, height). So the first two values are the location of the the upper left corner of the rectangle. The last two values define it's size.
As you step through the code, you will see, again, that nothing appears until the final line: ctx.fill();
// 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();
Now we will round out the two ends of the dog. The reason we put the tail in first, is so that we can cover the doggy end with this semi-circle and make it look like it was shaped exactly to match the curve.
We will use ctx.arc() to draw two semicircles. They look like northern hemispheres cut off below the equator. We lay them onto of the body so it only looks like a quarter circle, but it was easier to use the same code twice. The only thing that changes is the X coordinate.
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();
That arc stuff can look confusing. The first two values are the X, Y location of the center of a circle. You have to watch out for that when putting them together with rectangles. Rectangles are referenced from their corner, circles from the middle!
The third value is the radius of the circle: 8 pixels.
The next two determine beginning and ending points of the arc. Here's an image from w3schools.com that explains it all.
The last value, true, simply determines whether we are drawing clockwise or counter-clockwise.
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();
To do the dog's head, we will add another full circle a smaller half circle. Then we dress it up with a black nose, eye and ear.
// 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();
You can copy and paste the whole code below into your editor and experiment the script. You will see that I have moved the code from earlier that cleared our canvas into it's own function and set it up so we can pass a color to it as well. This will allow you to try different background colors and do some creative messing around. If you play with the background, remember that the first byte will need to be 00 for our animation needs later.
<!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">
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);
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
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,600,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="840" height="400" ;">
</canvas>
</body>
</html>
CYA While Programming for Canvas
Just a quick note, that I think is very important.
When you are working on a project you will probably write it in stages and sections. You can spend a lot of time working the bugs out of a special function and it is great to see it working fine. However, watch out, because as you move on to the next task, you might find yourself tied into such a knot that you have to start over. You do not want to have to start again and the beginning and you do not want to lose the work that you already have running fine.
To solve this problem I religiously rename my file as I progress from step to step. On a really big project, like the 'Drop Your Anchor' game, I name my files after the day of the week and also add a number that I can increment through the day. For example the current version that I am working on might be named:
Sunday.1.html
After a bunch of work, when I am happy with what I have just accomplished, I save it again as:
Sunday.2.html
At this point, the two files are identical. Now I can make changes to the second one and always be able to come back to the first if I really screw things up. Hey, it happens. Trust me.
To keep things manageable, I change the day of the week each day. So I will actually have multiple back ups. I don't start writing over older versions until they are a full week old.
And here is another simple tip that will make your tasks easier. I like to open a window in 'Note Pad' which is a real simple text editor. I keep it handy and use it as a place to paste little snippets of code and URLs that I know I might need again later. It makes it real easy to come back and copy them again even days after they were first put there. I store this as a file called scratchPad that I keep in the same folder as the rest of my project.
When you are working on a project you will probably write it in stages and sections. You can spend a lot of time working the bugs out of a special function and it is great to see it working fine. However, watch out, because as you move on to the next task, you might find yourself tied into such a knot that you have to start over. You do not want to have to start again and the beginning and you do not want to lose the work that you already have running fine.
To solve this problem I religiously rename my file as I progress from step to step. On a really big project, like the 'Drop Your Anchor' game, I name my files after the day of the week and also add a number that I can increment through the day. For example the current version that I am working on might be named:
Sunday.1.html
After a bunch of work, when I am happy with what I have just accomplished, I save it again as:
Sunday.2.html
At this point, the two files are identical. Now I can make changes to the second one and always be able to come back to the first if I really screw things up. Hey, it happens. Trust me.
To keep things manageable, I change the day of the week each day. So I will actually have multiple back ups. I don't start writing over older versions until they are a full week old.
And here is another simple tip that will make your tasks easier. I like to open a window in 'Note Pad' which is a real simple text editor. I keep it handy and use it as a place to paste little snippets of code and URLs that I know I might need again later. It makes it real easy to come back and copy them again even days after they were first put there. I store this as a file called scratchPad that I keep in the same folder as the rest of my project.
Friday, December 7, 2012
Getting Started With Canvas
I won't go into a lot of detail here about starting your first project. You will be able to find lots of examples on the web and on YouTube, as well. Many of the best features available to us as programmers are fairly new. You need to be sure that your own web browser is as up to date as possible. Otherwise, some of the things you try to do won't work.
<h3>
<div id="rowOne_text">Welcome to the Races</div>
<div id="rowTwo_text">Pick Your Dog</div>
</h3>
<canvas id="myCanvas" width="840" height="400" ;">
</canvas>
</body>
We have a couple of things going on here. In the <body> tag, we are telling the computer to wait until the whole page is loaded and then go find a function in the script called: canvasDogRaces() and execute it. That will start the whole game.
In the <canvas> element, we give it an id, "myCanvas" and set the width and height.
Now let's look at the script:
var canvasLeft = 120;
function canvasDogRaces(){
// Get the canvas element.
canvas = document.getElementById("myCanvas");
// Make sure you got it.
if (canvas.getContext){
ctx = canvas.getContext("2d");
ctx.fillStyle = "#007777";
ctx.rect(canvasLeft,0,600,400);
ctx.fill();
}else{
alert("Your browser does not/n support 'Canvas'");
}
}
We start by setting up a global variable: canvasLeft that we will use for referencing our images on the canvas.
The first thing the function does is attempts to find the HTML element <canvas> that we have ID'd as myCanvas. A cool thing about HTML is that usually it ignores things that it doesn't understand. So if the browser doesn't know what the heck a canvas is, the line canvas.getContext will come up false and the flow will jump to the else part of the structure. If the browser is compatible we're ready to go. We can write some code.
Now that we know it works, we assign the context to a variable that we will use throughout the projected: ctx = canvas.getContext("2d");
ctx becomes our canvas and we can play all we want with it.
This simple code simply lays out a background that we can work on. You can copy the full code below, paste it into your editor, and then load the page in your browser. You will see that we have drawn a rectangle that is offset from the edge of the window. This leaves a bit of buffer space that will come in real handy when you are trying to move images around on the background. You can park images there where the background color is different. This will help you debug your code and get stuff the way you want it. We can talk about getting rid of the work space buffer later. Leave it there for now. You will soon see why it is so helpful.
<!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">
var canvasLeft = 120;
function canvasDogRaces(){
// Get the canvas element.
canvas = document.getElementById("myCanvas");
// Make sure you got it.
if (canvas.getContext){
ctx = canvas.getContext("2d");
ctx.fillStyle = "#007777";
ctx.rect(canvasLeft,0,600,400);
ctx.fill();
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
</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="840" height="400" ;">
</canvas>
</body>
</html>
We will start by looking at some code in the <body> section of your page:
<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="840" height="400" ;">
</canvas>
</body>
We have a couple of things going on here. In the <body> tag, we are telling the computer to wait until the whole page is loaded and then go find a function in the script called: canvasDogRaces() and execute it. That will start the whole game.
In the <canvas> element, we give it an id, "myCanvas" and set the width and height.
Now let's look at the script:
var canvasLeft = 120;
function canvasDogRaces(){
// Get the canvas element.
canvas = document.getElementById("myCanvas");
// Make sure you got it.
if (canvas.getContext){
ctx = canvas.getContext("2d");
ctx.fillStyle = "#007777";
ctx.rect(canvasLeft,0,600,400);
ctx.fill();
}else{
alert("Your browser does not/n support 'Canvas'");
}
}
Make sure everyone is playing by the same rules:
It is probably best that you build into your code a test to make sure that the browser your user is running will support canvas. I found this example online and it works well for me.We start by setting up a global variable: canvasLeft that we will use for referencing our images on the canvas.
The first thing the function does is attempts to find the HTML element <canvas> that we have ID'd as myCanvas. A cool thing about HTML is that usually it ignores things that it doesn't understand. So if the browser doesn't know what the heck a canvas is, the line canvas.getContext will come up false and the flow will jump to the else part of the structure. If the browser is compatible we're ready to go. We can write some code.
Now that we know it works, we assign the context to a variable that we will use throughout the projected: ctx = canvas.getContext("2d");
ctx becomes our canvas and we can play all we want with it.
This simple code simply lays out a background that we can work on. You can copy the full code below, paste it into your editor, and then load the page in your browser. You will see that we have drawn a rectangle that is offset from the edge of the window. This leaves a bit of buffer space that will come in real handy when you are trying to move images around on the background. You can park images there where the background color is different. This will help you debug your code and get stuff the way you want it. We can talk about getting rid of the work space buffer later. Leave it there for now. You will soon see why it is so helpful.
Here is the full code:
<!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">
var canvasLeft = 120;
function canvasDogRaces(){
// Get the canvas element.
canvas = document.getElementById("myCanvas");
// Make sure you got it.
if (canvas.getContext){
ctx = canvas.getContext("2d");
ctx.fillStyle = "#007777";
ctx.rect(canvasLeft,0,600,400);
ctx.fill();
}else{
alert("Your browser does not\n support 'Canvas'");
}
}
</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="840" height="400" ;">
</canvas>
</body>
</html>
Thursday, December 6, 2012
Firebug and other Programming Tools
Let's talk about the tools I use when writing script for Canvas. My favorite is Firebug. What a great program this is! It works well with Firefox and I see where they have a version for Chrome as well.
Programming without Firebug would be a nightmare. It allows me to step through my code and watch the values of variables change as I move from line to line. I can set breakpoints that let me jump quickly to the functions that I am working on.
There is a DOM tab that let's me see all the blood and guts. I can view all the locally stored variables, even the individual bytes that make up an image. These are a great help.
Firebug is free, but there is a 'Donate' button. I think after you have worked with Firebug for a short time you will agree that a donation is in order. I did.
For some time now I have been using Coffee Cup as my HTML Editor. It has served me well. As you can see by the image at the right my version may be a little old.
Coffee Cup has a preview tab that has worked well for general HTML pages, but it doesn't work with Canvas. This is no big deal, though. It forces me to save my work before I can test it in Firefox. That means when something crashes I've never lost my work. [Yes, you can actually crash your web browser, especially if you screw up a loop and your script runs away from you. I've even had to reboot my computer!] I must say, that ctrl-S has become such a habit that I find my self using it in other programs where it is NOT what I want to do.
While studying programming for Linux I saw a video on YouTube where the guy was using an editor that allowed him to collapse functions. Oh, I wish I could do that. Once your code gets lengthy it would be great to hide the stuff that is running right and only see what you need to work on. If any of you know of an editor that would do that, please let me know.
And, as I'm sure you have gathered, I also use Firefox as my main browser. I have IE on my computer but that just makes me want to cry. I know a lot of people use it, because it was on the computer when they bought it.
Any serious programmer can't simply ignore IE. My canvas web app is just now in beta mode, so I will ignore IE for a little longer and then spend some time with it. I have a hunch that it won't take too much effort to wrestle into working with the project.
If you want to write games for the web, animated web apps, take a look at my project and watch this blog for more information. Remember Canvas Games work on PCs, Macintosh, and Linux. That is a lot of platforms that you can program for all at once.
C U Again Soon
Programming without Firebug would be a nightmare. It allows me to step through my code and watch the values of variables change as I move from line to line. I can set breakpoints that let me jump quickly to the functions that I am working on.
There is a DOM tab that let's me see all the blood and guts. I can view all the locally stored variables, even the individual bytes that make up an image. These are a great help.
Firebug is free, but there is a 'Donate' button. I think after you have worked with Firebug for a short time you will agree that a donation is in order. I did.
For some time now I have been using Coffee Cup as my HTML Editor. It has served me well. As you can see by the image at the right my version may be a little old.
Coffee Cup has a preview tab that has worked well for general HTML pages, but it doesn't work with Canvas. This is no big deal, though. It forces me to save my work before I can test it in Firefox. That means when something crashes I've never lost my work. [Yes, you can actually crash your web browser, especially if you screw up a loop and your script runs away from you. I've even had to reboot my computer!] I must say, that ctrl-S has become such a habit that I find my self using it in other programs where it is NOT what I want to do.
While studying programming for Linux I saw a video on YouTube where the guy was using an editor that allowed him to collapse functions. Oh, I wish I could do that. Once your code gets lengthy it would be great to hide the stuff that is running right and only see what you need to work on. If any of you know of an editor that would do that, please let me know.
And, as I'm sure you have gathered, I also use Firefox as my main browser. I have IE on my computer but that just makes me want to cry. I know a lot of people use it, because it was on the computer when they bought it.
Any serious programmer can't simply ignore IE. My canvas web app is just now in beta mode, so I will ignore IE for a little longer and then spend some time with it. I have a hunch that it won't take too much effort to wrestle into working with the project.
If you want to write games for the web, animated web apps, take a look at my project and watch this blog for more information. Remember Canvas Games work on PCs, Macintosh, and Linux. That is a lot of platforms that you can program for all at once.
C U Again Soon
Wednesday, December 5, 2012
HTML5 and Games on Canvas
HTML5 and its 'canvas' element offer some new and exciting ways for programers to write web apps that include animation.
Until a few weeks ago, this was new to me. So I decided to give it a try. I took an old game that I had written long ago and decided to rewrite it in JavaScript. This is my first attempt using that language and I have learned a lot.
Canvas is basically an image on a web page. It is an image that you can manipulate from within your script.
You can find the beta version of my game and a Beta Testers' Blog at:
http://bit.ly/QI8t8t
http://ggbbetatesters.blogspot.com/
One of the neatest things I discovered very early on in the project is that there is plenty of room to hide data right inside the image. I have objects that move around on the game board. I need to know who they are and what attributes they have. I hide that info in the image itself. So it doesn't matter where an object ends up, I can read the data I need when I need it, and ignore the data I don't need even though it's location changes.
How do I do that? It's cool. You see each pixel in an image contains four bytes of data. The first three are the RGB color values. These are numbers between 0 and 255. The fourth byte is a transparency level. I've played around with it a bit which I will discuss later.
This game is very simple, so there isn't a lot of information that I need to hide. I found it convenient to simply do everything with the first byte of each pixel. This is where the Red value is stored. The human eye (and the computer monitor for that matter) doesn't distinguish between very small changes in value. That is why most of the color pickers that you use when creating web pages trim the number of choices way down. We can't really tell the difference between values like 255, 254, 253, 252. They all look the same to us. But from a programming point of view, they are each very much different, so they can tell us different things.
If you search the web for canvas demos you will see examples of space ships trying to avoid asteroids as you maneuver them over a black background. Black makes a great background color because it is 0 0 0 in RGB or #000000 in hex. Zero is a great value to test for in your code because IF statements treat any non-zero value as true and zero itself as false. So you can write something like this:
if (!myColor){do something here;}
[you need to know that ! means NOT ... so 'do something here' will only happen when myColor is zero.]
I soon realized that my game did not have to have a black background. I am only going to test the first byte of a pixel, so any color that has it's R value as zero will work just the same as black. So a blue slate background might look much better. Instead of #000000 I chose #007777.
In fact, there were other things that I wanted to put on the game board that I render neutral to the moving players. Like the text for the high wire that defines the end of the game. A value of #00EEEE works well for that job. Notice again, that the first byte is zero.
The gumdrop game is about colors and I need lots of them. When one of my players hits a target, I need to know what color I just hit. To make that job easy I have made sure that the first byte of each color is unique. No two are the same. Here is how they are defined:
var myRed = "#FE0000"; //red - first byte is 254
var myBlue = "#0200DD"; //blue - first byte is 2
var myYellow = "#FCFF00"; //yellow - first byte is 252
var myMagenta = "#660066"; //magenta - first byte is 253
var myOrange = "#FF6600"; //orange is - first byte is 255
var myGreen = "#06FF00"; //green is - first byte is 6
var myBrown = "#993300"; //brown is - first byte is 153
To actually test for a color you need to have the X Y coordinates of the spot you want to test.
Grab a small rectangle of the image and store it:
var testSpot = ctx.getImageData(X, Y, 2, 2);
testSpot will be an array of 16 bytes. You can pick any size, we are only going to look at one byte anyway. For debugging purposes it is nice to be able to park a test image on your canvas so you can visually see exactly what you're testing. Then you REM out the line that does that when you don't need it anymore. So we have a group of 4 pixels in a square that is 2x2. Each pixel is 4 bytes which gives us that array of 16.
Now test the value of the first byte:
hitColor = testSpot[0];
if (hitColor > 0){we just hit something!;}
[Stuff you need to know: The first item in an array is numbered: 0. The text I have in {}'s won't do anything ... you would probably call a function there.]
All of our colors have their own unique value. We can look it up and see who it is.
Just think of all the things you might want to hide in the image. You can keep track of hit points that a player might have or even the health of one of your dragons! What if you played around with the transparency byte? As your hero does battle with the evil zombie, the zombie becomes more and more transparent with each blow. Finally he's gone! You can have a cool visual effect on an object that can wander around your screen at random and you don't have to have fancy complicated code to keep track of where he is and what his current health may be. Wow. Canvas and HTML5 really open the door to some new ways to have fun on the web.
Please, please, please help me beta test the game. Try it out, and be sure to subscribe to the Beta Tester's Blog as well. We'll keep that blog simple and put the techie stuff over here.
C U next time.
Until a few weeks ago, this was new to me. So I decided to give it a try. I took an old game that I had written long ago and decided to rewrite it in JavaScript. This is my first attempt using that language and I have learned a lot.
Canvas is basically an image on a web page. It is an image that you can manipulate from within your script.
You can find the beta version of my game and a Beta Testers' Blog at:
http://bit.ly/QI8t8t
http://ggbbetatesters.blogspot.com/
One of the neatest things I discovered very early on in the project is that there is plenty of room to hide data right inside the image. I have objects that move around on the game board. I need to know who they are and what attributes they have. I hide that info in the image itself. So it doesn't matter where an object ends up, I can read the data I need when I need it, and ignore the data I don't need even though it's location changes.
How do I do that? It's cool. You see each pixel in an image contains four bytes of data. The first three are the RGB color values. These are numbers between 0 and 255. The fourth byte is a transparency level. I've played around with it a bit which I will discuss later.
This game is very simple, so there isn't a lot of information that I need to hide. I found it convenient to simply do everything with the first byte of each pixel. This is where the Red value is stored. The human eye (and the computer monitor for that matter) doesn't distinguish between very small changes in value. That is why most of the color pickers that you use when creating web pages trim the number of choices way down. We can't really tell the difference between values like 255, 254, 253, 252. They all look the same to us. But from a programming point of view, they are each very much different, so they can tell us different things.
If you search the web for canvas demos you will see examples of space ships trying to avoid asteroids as you maneuver them over a black background. Black makes a great background color because it is 0 0 0 in RGB or #000000 in hex. Zero is a great value to test for in your code because IF statements treat any non-zero value as true and zero itself as false. So you can write something like this:
if (!myColor){do something here;}
[you need to know that ! means NOT ... so 'do something here' will only happen when myColor is zero.]
I soon realized that my game did not have to have a black background. I am only going to test the first byte of a pixel, so any color that has it's R value as zero will work just the same as black. So a blue slate background might look much better. Instead of #000000 I chose #007777.
In fact, there were other things that I wanted to put on the game board that I render neutral to the moving players. Like the text for the high wire that defines the end of the game. A value of #00EEEE works well for that job. Notice again, that the first byte is zero.
The gumdrop game is about colors and I need lots of them. When one of my players hits a target, I need to know what color I just hit. To make that job easy I have made sure that the first byte of each color is unique. No two are the same. Here is how they are defined:
var myRed = "#FE0000"; //red - first byte is 254
var myBlue = "#0200DD"; //blue - first byte is 2
var myYellow = "#FCFF00"; //yellow - first byte is 252
var myMagenta = "#660066"; //magenta - first byte is 253
var myOrange = "#FF6600"; //orange is - first byte is 255
var myGreen = "#06FF00"; //green is - first byte is 6
var myBrown = "#993300"; //brown is - first byte is 153
To actually test for a color you need to have the X Y coordinates of the spot you want to test.
Grab a small rectangle of the image and store it:
var testSpot = ctx.getImageData(X, Y, 2, 2);
testSpot will be an array of 16 bytes. You can pick any size, we are only going to look at one byte anyway. For debugging purposes it is nice to be able to park a test image on your canvas so you can visually see exactly what you're testing. Then you REM out the line that does that when you don't need it anymore. So we have a group of 4 pixels in a square that is 2x2. Each pixel is 4 bytes which gives us that array of 16.
Now test the value of the first byte:
hitColor = testSpot[0];
if (hitColor > 0){we just hit something!;}
[Stuff you need to know: The first item in an array is numbered: 0. The text I have in {}'s won't do anything ... you would probably call a function there.]
All of our colors have their own unique value. We can look it up and see who it is.
Just think of all the things you might want to hide in the image. You can keep track of hit points that a player might have or even the health of one of your dragons! What if you played around with the transparency byte? As your hero does battle with the evil zombie, the zombie becomes more and more transparent with each blow. Finally he's gone! You can have a cool visual effect on an object that can wander around your screen at random and you don't have to have fancy complicated code to keep track of where he is and what his current health may be. Wow. Canvas and HTML5 really open the door to some new ways to have fun on the web.
Please, please, please help me beta test the game. Try it out, and be sure to subscribe to the Beta Tester's Blog as well. We'll keep that blog simple and put the techie stuff over here.
C U next time.
Subscribe to:
Posts (Atom)