Making HTML5 Canvas Canvas

Sunday, August 25, 2013

Part 4. Interaction with other elements

Besides interacting with our character in the game, the second most important point in a game is that elemets interact among themselves. To know if two elements are "touching each other" (If there is an intersection between them), knowing their coordinates is not enough, we need to know their height and width also.

In Javascript, functions do a double function. The allow not only events that can be called any time (as we have seen before), but they also allow to create objects (Which in other languages, are called "classes"). For example, we can create rectangles from a function that contains x-coordinate, y-coordinate, width and height.

In addition, Class-like functions can contain also their own functions. Therefore, we will add an "intersects" function to it, which will tell us if there is an intersections at the time with a second element, and a simple way to fill the rectangle. To know about this with more detail, read the Appendix 4: Object Oriented Programming; for now, just copy at the end of your code, the following function:
function Rectangle(x,y,width,height){
    this.x=(x==null)?0:x;
    this.y=(y==null)?0:y;
    this.width=(width==null)?0:width;
    this.height=(height==null)?this.width:height;
    
    this.intersects=function(rect){
        if(rect!=null){
            return(this.x<rect.x+rect.width&&
                this.x+this.width>rect.x&&
                this.y<rect.y+rect.height&&
                this.y+this.height>rect.y);
        }
    }
    
    this.fill=function(ctx){
        if(ctx!=null){
            ctx.fillRect(this.x,this.y,this.width,this.height);
        }
    }
}
As we can see, the "Rectangle" function is designed to get four variables: x, y, width and height. If we skip one, this one will be 0 by default to avoid errors, except for height, whose default value is the width (Which will allow us to send only three parameters if the width a height have the same value).

Now we are going to make some changes to our code. First, we delete the variables "x" and "y" at the beginning, and instead, we will create a "player" variable from our rectangle function:
var player=new Rectangle(40,40,10,10);
Then, we change the way the rectangle is painted:
    player.fill(ctx);
Finally, and be careful with this, we must replace all "x" and "y" variables that we used before, for "player.x" and "player.y".

After we check the code is running with no problem, we wil proceed to add a new element to interact with it. We declare a new rectangle object named "food":
var food=new Rectangle(80,80,10,10);
And paint it just like with do with our player, only this one in red:
    ctx.fillStyle='#f00';
    food.fill(ctx);
Now, we will find out if them both are in an intersection. If this is the case, we will add in one our score, and change the position of the food to somewhere else at random. For this, we will need first to declare our score:
var score=0;
We will also add this function, which will be helpful to get random integer numbers:
function random(max){
    return Math.floor(Math.random()*max);
}
Now, at the "act" function, right after moving our player, we will compare if both elements are in and intersection, and if this is the case, we will add the score in one and change the food position:
        // Food Intersects
        if(player.intersects(food)){
            score++;
            food.x=random(canvas.width/10-1)*10;
            food.y=random(canvas.height/10-1)*10;
        }
This little equation to divide the screen in 10 inside the random and multiply it again at the end, makes the food to appear in a place every 10 pixels, so it fix to "the grid". Finally, we will paint our score on the canvas:
    ctx.fillText('Score: '+score,0,10);
We save and test the code. Now, every time the green rectangle touches the red one, the score will raise.

Final code:

[Canvas not supported by your browser]
window.addEventListener('load',init,false);
var canvas=null,ctx=null;
var lastPress=null;
var pause=true;
var dir=0;
var score=0;
var player=new Rectangle(40,40,10,10);
var food=new Rectangle(80,80,10,10);

var KEY_ENTER=13;
var KEY_LEFT=37;
var KEY_UP=38;
var KEY_RIGHT=39;
var KEY_DOWN=40;

function random(max){
    return Math.floor(Math.random()*max);
}

function init(){
    canvas=document.getElementById('canvas');
    canvas.style.background='#000';
    ctx=canvas.getContext('2d');
    run();
    repaint();
}

function run(){
    setTimeout(run,50);
    act();
}

function repaint(){
    requestAnimationFrame(repaint);
    paint(ctx);
}

function act(){
    if(!pause){
        // Change Direction
        if(lastPress==KEY_UP)
            dir=0;
        if(lastPress==KEY_RIGHT)
            dir=1;
        if(lastPress==KEY_DOWN)
            dir=2;
        if(lastPress==KEY_LEFT)
            dir=3;

        // Move Rect
        if(dir==0)
            player.y-=10;
        if(dir==1)
            player.x+=10;
        if(dir==2)
            player.y+=10;
        if(dir==3)
            player.x-=10;

        // Out Screen
        if(player.x>canvas.width)
            player.x=0;
        if(player.y>canvas.height)
            player.y=0;
        if(player.x<0)
            player.x=canvas.width;
        if(player.y<0)
            player.y=canvas.height;
     
        // Food Intersects
        if(player.intersects(food)){
            score++;
            food.x=random(canvas.width/10-1)*10;
            food.y=random(canvas.height/10-1)*10;
        }
    }
    // Pause/Unpause
    if(lastPress==KEY_ENTER){
        pause=!pause;
        lastPress=null;
    }
}

function paint(ctx){
    ctx.clearRect(0,0,canvas.width,canvas.height);
    ctx.fillStyle='#0f0';
    player.fill(ctx);
    ctx.fillStyle='#f00';
    food.fill(ctx);
 
    ctx.fillStyle='#fff';
    //ctx.fillText('Last Press: '+lastPress,0,20);
    ctx.fillText('Score: '+score,0,10);
    if(pause){
        ctx.textAlign='center';
        ctx.fillText('PAUSE',150,75);
        ctx.textAlign='left';
    }
}

document.addEventListener('keydown',function(evt){
    lastPress=evt.keyCode;
},false);

function Rectangle(x,y,width,height){
    this.x=(x==null)?0:x;
    this.y=(y==null)?0:y;
    this.width=(width==null)?0:width;
    this.height=(height==null)?this.width:height;
 
    this.intersects=function(rect){
        if(rect!=null){
            return(this.x<rect.x+rect.width&&
                this.x+this.width>rect.x&&
                this.y<rect.y+rect.height&&
                this.y+this.height>rect.y);
        }
    }
 
    this.fill=function(ctx){
        if(ctx!=null){
            ctx.fillRect(this.x,this.y,this.width,this.height);
        }
    }
}

window.requestAnimationFrame=(function(){
    return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        function(callback){window.setTimeout(callback,17);};
})();

No comments:

Post a Comment