Sunday, February 28, 2010

Bullets and points.

I want to move this toy project further toward a game project, so it's pretty reasonable to throw in a new variable to keep track of score and a new variable to keep track of ammo, and modify some functions to increment and decrement those scores as needed.

...
   var score = 0;
   var ammo = 10;
...
   clicker = function(event){
    var x = 0;
    var y = 0;
    if(event){
     x = event.pageX - newCanvas.offsetLeft;
     y = event.pageY - newCanvas.offsetTop;
    }
    if(ammo > 0){
     ammo--;
     for(i=0;i<boxes.length;i++){
      box = boxes[i];
      if(Math.abs(x-box.x)<box.size/2 && 
         Math.abs(y-box.y)<box.size/2){
       explodeBox(i);
      }
     }
    }
   } 
...

   stickPhysics = function(stick,n){
...
    if(newy > 495){
     sticks.splice(n,1);
     ammo++;
     score++;
    }
...
   }
...
The bold text above is the new stuff. It will keep track of the total number of sticks that fall off the bottom of the screen, with the score variable, as well as the "ammo" the player has available. If ammo runs down to zero, the player can't shoot down any more boxes until more sticks fall off the bottom of the screen.

Another big step from toy to game would be an end condition. In fact, just by implementing ammo I've actually created a condition under which the game can't go any further. All that's left is to let he player know that it's all over and clicking on boxes is fruitless. To do this, a conditional sits in the draw() function and draws a "Game Over" screen if the player has run out of ammo and the sticks array is empty.

...
   draw = function(){
...
    if(ammo == 0 && sticks.length == 0){
     var y = 125;
     newContext.fillStyle='rgba(255,100,100,0.9)';
     newContext.fillRect(20,80,280,320);
     newContext.fillStyle="red";
     newContext.strokeStyle="#000000";
     newContext.font = "45px sans-serif bold";
     newContext.textAlign="center";
     newContext.fillText("Game Over",160,y);
     newContext.strokeText("Game Over",160,y);
     newContext.font = "25px sans-serif";
     newContext.fillStyle = 'black'
     var scoretxt = "Final Score: ";
     newContext.fillText(scoretxt + score,160,y+30);
    }
...
}

This takes care of the case in which the player runs out of ammo, but ignores the case in which the game runs out of boxes. Obviously, if there are no boxes, the game can't continue. Killing just ten boxes and being done, however, isn't all that fun. Rather, the boxes ought to be endless, but harder to kill, so that the player is eventually forced to use up all of his ammo. This means levels!

Levels means altering quite a few things, but without any really major changes. Since it's also nice to know what level a player is on, I'll go ahead and drop in a little scoreboard.

...
   var level = 0;
...
   makeBox = function(x,y){
    ...
    var vx = (level/2+1)*(Math.random()-1.0)/6;
    var vy = (level/2+1)*(Math.random()-1.0)/6;
    ...
   }
...
   init = function(){
    ...
    for(i=0;i<3;i++){
     var x = Math.random()*318 + 1;
     var y = Math.random()*478 + 1;
     makeBox(x,y);
    }
    ...
   }
...
   draw = function(){
    ...
    if(boxes.length == 0){
     level++;
     for(i=0;i<3;i++){
      var x = Math.random()*318 + 1;
      var y = Math.random()*478 + 1;
      makeBox(x,y);
     }
    }
    ...
    if(ammo == 0 && sticks.length == 0){
    ...
     newContext.fillText("You have run out of ammo",160,y+30);
     newContext.fillText("on level "+level,160,y+60);
     newContext.fillText("with a final score of "+ score,160,y+90);
    }
    ...
    // The scoreboard
    newContext.fillStyle="rgba(230,230,230,0.8)";
    newContext.fillRect(320-80,5,75,42);
    newContext.fillStyle="#000000";
    newContext.font="10px sans-serif";
    newContext.textAlign="left";
    newContext.fillText("Ammo: "+ammo,245,18);
    newContext.fillText("Score: "+score,245,30);
    newContext.fillText("Level: "+level,245,42);
    ...
   }

And so, I have a game! Hooray!

The only problem is that if someone plays it in its current form, it takes a click to shoot down a box. This means that to get rid of the hundreds of sticks of ammo a player ends up collecting, the player would have to nearly break his finger clicking the mouse button. A better approach is to have a machine-gun effect, where holding down the mouse button is enough to keep killing boxes.

...
   var lastShot = 0;
   var firing = false;
   var mousex = 0;
   var mousey = 0;
...
   clicker = function(event){ 
    var x = 5;
    var y = 5;
    newCanvas.onmouseup = up;
    newCanvas.onmousemove = move;
    if(event){
     x = event.pageX - newCanvas.offsetLeft;
     y = event.pageY - newCanvas.offsetTop;
     mousex = event.pageX - newCanvas.offsetLeft;
     mousey = event.pageY - newCanvas.offsetTop;
    }
    shoot();
    firing = true;
   }
   up = function(event){
    newCanvas.onmousemove = null;
    firing = false;
   }
   move = function(event){
    if(event){
     mousex = event.pageX-newCanvas.offsetLeft;
     mousey = event.pageY - newCanvas.offsetTop;
    }
   }
   shoot = function(){
    var x = mousex;
    var y = mousey;
    var t = new Date().getTime();
    if(t-lastShot > 50){
      lastShot = t;
      if(ammo > 0){
      ammo--;
      for(i=0;i<boxes.length;i++){
       var box = boxes[i]
       if(Math.abs(x-box.x)<45 && Math.abs(y-box.y)<45 && box.size == 90){
        explodeBox(i);
       }
       if(Math.abs(x-box.x)<15 && Math.abs(y-box.y)<15 && box.size == 30){
        explodeBox(i);
       }}}}
   }  
...
   draw(){
    ...
    if(firing) shoot();
   }

The problems, however, never end. Now that I've got a machine-gun going, it's pretty frustrating to have no real way of telling how fast your ammo's going. The firing rate is high enough, and the <audio> tag is annoying enough to use, that a sound for each shot isn't feasible. Rather, I want to use yet another array of visual elements: bullet holes.

...
   var bulletholes = new Array();
   
   Hole = function (x,y,t){
    this.x = x;
    this.y = y;
    this.time = t;
   }
...
   draw(){
    ...
     for(i=0;i<bulletholes.length;i++){
     var hole = bulletholes[i];
     var hc = 'rgba(0,0,0,';
     alpha = 1 - (time-hole.time)/3000
     hc = hc + alpha;
     hc = hc + ")";
     if (alpha <= 0){
      bulletholes.splice(i,1);
     } else {
      newContext.fillStyle = hc;
      newContext.fillRect(hole.x-1,hole.y-1,2,2);
     }
    }
...
   shoot = function(){
    ...
    if(t-lastShot > 50){
     ...
     if(ammo > 0){
      bulletholes.push(new Hole(x,y,t));
      ...
     }
     ...
    }
    ...
   }

And the final result is below. It's not quite the same script as in the first post, but it is the same game. That took a rather long time.

Canvas not supEported in your browser.

No comments:

Post a Comment