Many gaming frameworks utitlize a similar game loop and code structure. The format lends itself to animation as well and includes four steps. In order, they are:
Variable declarations
initialze()
update()
drawing()
Each of these areas serve an important purpose and breaking them up into parts will help keep your confusing code organized. Below,
we will discuss each section in detail.
Variable Declarations
In this section, all necessary variables will be declared, and only declared. This will enable these variables to be used in a variety of methods throughout the
program. Remember, a variable declaration is just the naming of a variable. In this section it is rare that a variable will be assigned a value.
The main exception to this rule is the creation of image variables so that the image loads prior to the program running.
It is important that a variable is declared for each part of an image/shape that you may want to alter. For example, if we want to be able to
alter the position of a box throughout the program, we will need to declare variables for both the x and y coordinates. If we want to alter the
size of a box, we would need variables for the width and height. If we want to change the color of the box, we would need variables for the red, blue and green
values.
Additionally, it is important to declare our control variables here as well. Those control variables inlude the canvas, the context and the timer. Declaring
these variables here will allow them to be accessed and used in all of our methods.
//box variables
let boxW;
let boxH;
//control variables
let canvas;
let ctx;
let timer;
//images
let bkgrnd=new Image();
bkgrnd.src="images/mountains.png';
initialize()
This is the function that gets the ball rolling, so to speak. The purpose of the initialize method is to give values to all of the variables.
This method should be called when the page loads . All declared variables should be given a valid starting value in
this method. Also, the last line of the initialize method should call the update method.
function initialize(){
boxW=10;
boxH=10;
canvas=document.getElementById('can');
ctx=canvas.getContext('2d');
timer=0;
update();
}
draw()
The draw() method is responsible for drawing all items to the screen. No variables should be updated in this method, only drawing. Prior to drawing, we need to
clear the screen so that old versions are taken away or we will get trails (previously drawn versions of images).
function draw(){
//clear the canvas
ctx.clearRect(0,0,canvas.width, canvas.height);
//draw the background
ctx.drawImage(bkgrnd, 0, 0, canvas.width, canvas.height);
//draw the box
ctx.beginPath();
ctx.rect(0,0,boxX, boxY);
ctx.fill();
ctx.closePath();
}
update()
The update() method is responsible for altering any variables as needed. This is also the place that we can check timing events to see if an
action should start or stop (in gaming, we would check for user input here). In other words, this is the controller of the script. This method
will be repetively called (approximately 60 times per second) to make our animations appear smooth to the viewer.
We are going to make our box grow for two seconds and then shrink for two seconds. This set of actions will be repeated indefinitely until the
animation is stopped. We only want this code to occur while the box is growing, so we need a new variable to track if the box is growing. At the
top of the program declare a new variable var boxGrowing; and then set its initial value to true in initialize() boxGrowing=true;. We will toggle
the value of this variable between true and false depending on the timer variable.
To keep track of the time in an animation, we can count on the fact that the update method will be called close to 60 times per second.
Using this fact, we can simply increment the timer variable each time update is run. Then, if timer==60, we have reached 1 second; timer=120,
means 2 seconds and so on. Since we want to change the action every 2 seconds, we are checking to see if the timer variable is divisible by 120,
if it is, we will toggle the value of boxGrowing.
update(){
timer++;
if(timer%120==0){
boxGrowing=!boxGrowing; //!boxGrowing makes a value of true into false and vice-versa
}
if(boxGrowing){
boxW++;
boxH++;
}
else{
boxW--;
boxH--;
}
draw();
requestAnimationFrame(update); //this calls update repeatedly
}
The last two lines in the update function are always the same. The draw() command calls the draw method. The requestAnimationFrame(update) function
is what sets the animation loop. This sets the 60 times a second repeat, calling update each time. This puts the program into a loop of update, draw,
update, draw, etc.
Sprite Animation
JavaScript and the HTML Canvas element also provide a way to create sprite animations. A sprite sheet is a set of keyframes that, when shown in order, create
the appearance of movement and animation (similar to flip books from when you were young). In order to create a sprite animation, we need another set of variables
to control the sprite frame, a new drawImage method and a sprite sheet that is correctly formatted.
Sprite Sheets
When creating a sprite animation on a canvas, the sprite sheet needs to be formatted in a certain way. ALL cells of the sprite sheet must be the same width and
height. Many sprite sheets found online are NOT formatted this way. Most likely, if you find a sprite sheet online, you will need to format it using a graphic
editing program (e.g. Photoshop, Photopea, etc).
Sprite Code
Assuming that we have a sprite sheet that is organized horizontally, we will need to cycle through the cells by changing the frame.
In the catSprite.png image, each cell has a width of 43 px and a height of 57 px. There are seven cells (we will count them 0-6).
When coding the sprite movement, we will need to track the image's location on the canvas as well as the frame position within the sprite. Here are the variables that
need to be declared in this situation:
let cat=new Image();
cat.src="catWalk.png";
//the location of the cat on the canvas
let catX, catY;
//the number of frames in the sprite
let catNumFrames;
//the width and height of a frame
let catFrameW, catFrameH;
//the current frame of the cat (values 0-6)
let catCurrentFrame;
In the initialize function, we will set all of the variables. Assume that we want the
cat to start in the center of the screen horizontally and with its feet 20 pixels from the
bottom. The set of cat variables in the initialize function will look as follows:
catX=canvas.width/2;
catY=canvas.height-(cat.height+20);
catNumFrames=7;
catFrameW=43;
catFrameH=57;
catCurrentFrame=0; //start at the beginning
In the update method, we will be changing the x position of the cat to move him to the right.
We will alos need to change the current frame of the cat in each iteration of update, making sure
that it loops around and uses only values of 0-6. Additionally we will only update the frame
20 times a second, or every third time the update function runs as that is the most pleasing to the eye in this case.
//move right
catX++;
//change frame
if(timing%3==0)
catCurrentFrame++;
if(catCurrentFrame==catNumFrame)
catCurrentFrame=0;
The second condition is used to loop the frame variable and keep it in the range of 0-6. Alternatively,
catCurrentFame%=catNumFrame could be used in place of the condition.
In the draw method, we will need to use a new version of the drawImage method. This version will take nine parameters:
image - the image variable name of the sprite sheet
frameX - the left side coordinate of the current frame (currentFrame*frameWidth)
frameY - the top side coordinate of the current frame
frameWidth - the width of a frame
frameHeight - the height of a frame
imgX - the x position of the image on the canvas
imgY - the y position of the image on the canvas
imgWidth - the desired width of the image on the canvas (used for scaling)
imgHeight - the desired height of the image on the canvas (used for scaling)