Making HTML5 Canvas Canvas

Sunday, September 01, 2013

Part 5. Interacting with several alike elements

We just learnt how to make and object interact with another. The problem would be if we try, for example, do this with over 50 elements with the same behaviour (Like it would be with enemies, for example). Making this evaluation one by one, would be boring and complex. Luckily for us, there is an easier way to interact with several elements with alike properties through arrays.

For this example, we will declare an array variable called "wall":
var wall=new Array();
This array will contain all our wall-type elements. Now, we will add four elements to this array:
wall.push(new Rectangle(100,50,10,10));
wall.push(new Rectangle(100,100,10,10));
wall.push(new Rectangle(200,50,10,10));
wall.push(new Rectangle(200,100,10,10));
To paint the wall elements, we will go through the array with a "for":
    ctx.fillStyle='#999';
    for(var i=0,l=wall.length;i<l;i++){
        wall[i].fill(ctx);
    }
In a similar way, we will compare each element in the wall array with a "for", and check if there is an intersection with the food or the player:
        // Wall Intersects
        for(var i=0,l=wall.length;i<l;i++){
            if(food.intersects(wall[i])){
                food.x=random(canvas.width/10-1)*10;
                food.y=random(canvas.height/10-1)*10;
            }
            
            if(player.intersects(wall[i])){
                pause=true;
            }
       }
First we check if the food intersects with the wall. If this is true, we change the food from place, this will avoid it to get "stuck" on the wall. After that, we check is the player intersects the wall, and in such case, the game will stop. This is right, but the best would be that, when the player crashes, when we start over our game, this starts from the beginning.

Let's fix this. We start by knowing when the player has lost, with a new variable called "gameover":
var gameover=true;
Then, we add these lines just where the "act" function begins, right after "if(pause)":
        // GameOver Reset
        if(gameover)
            reset();
So, we call a function called "reset", where we will specify how we want the game to start. In this case, we will set the score at zero, the direction to the right, return the player to it's original coordinates and change the site of the food. Finally, of course, we will be sure to let the game not over any more:
function reset(){
    score=0;
    dir=1;
    player.x=40;
    player.y=40;
    food.x=random(canvas.width/10-1)*10;
    food.y=random(canvas.height/10-1)*10;
    gameover=false;
}
Finally, we will change the "if(pause)" in our "paint" function, to check if the game is at game over, or just pausing:
    if(pause){
        ctx.textAlign='center';
        if(gameover)
            ctx.fillText('GAME OVER',150,75);
        else
            ctx.fillText('PAUSE',150,75);
        ctx.textAlign='left';
    }
With this, we conclude with all the basic knowledge to make a game. In the last chapter, we will focus on the details to shape our game.

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 gameover=true;
var dir=0;
var score=0;
var player=new Rectangle(40,40,10,10);
var food=new Rectangle(80,80,10,10);
var wall=new Array();

wall.push(new Rectangle(100,50,10,10));
wall.push(new Rectangle(100,100,10,10));
wall.push(new Rectangle(200,50,10,10));
wall.push(new Rectangle(200,100,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 reset(){
    score=0;
    dir=1;
    player.x=40;
    player.y=40;
    food.x=random(canvas.width/10-1)*10;
    food.y=random(canvas.height/10-1)*10;
    gameover=false;
}

function act(){
    if(!pause){
        // GameOver Reset
        if(gameover)
            reset();
        
        // 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;
        }
        
        // Wall Intersects
        for(var i=0,l=wall.length;i<l;i++){
            if(food.intersects(wall[i])){
                food.x=random(canvas.width/10-1)*10;
                food.y=random(canvas.height/10-1)*10;
            }
            
            if(player.intersects(wall[i])){
                gameover=true;
                pause=true;
            }
        }
    }
    // 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='#999';
    for(var i=0,l=wall.length;i<l;i++){
        wall[i].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';
        if(gameover)
            ctx.fillText('GAME OVER',150,75);
        else
            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