topical media & game development

talk show tell print

graphic-canvas-example-canvascape-level.js / js



  
Copyright (c) 2009, Benjamin Joffe All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

  
  
  var map;
  var canvas;
  var overlay;
  //variables initiated at the bottom of the code...
  
  var pi=Math.PI;
  
  var total=0;
  
  Number.prototype.range=function(){
          return (this+2*pi)%(2*pi);
  }
  Number.prototype.roundC=function(){
          return Math.round(this*100)/100;
  }
  
  var total=0;
  
  var samples=200;
  
  var arena=[];
  arena[0]=[1,1,1,1,1,1,1,1,1,1]
  arena[1]=[1,0,0,0,0,0,0,0,0,1]
  arena[2]=[1,0,0,1,0,1,1,1,0,1]
  arena[3]=[1,0,1,0,0,0,0,1,0,1]
  arena[4]=[1,0,0,0,0,1,0,1,0,1]
  arena[5]=[1,0,1,1,0,0,0,0,0,1]
  arena[6]=[1,0,0,1,0,1,1,1,0,1]
  arena[7]=[1,1,0,1,0,0,0,1,0,1]
  arena[8]=[1,0,0,1,0,1,0,0,0,1]
  arena[9]=[1,1,1,1,1,1,1,1,1,1]
  
  var playerPos=[4,4]; // x,y (from top left)
  var playerDir=0.4; // theta, facing right=0=2pi
  var playerPosZ=1;
  var key=[0,0,0,0,0]; // left, right, up, down
  
  var playerVelY=0;
  
  var face=[];
  
  function wallDistance(theta){
  
          var data=[];
          face=[];
  
          var x = playerPos[0], y = playerPos[1];
          var deltaX, deltaY;
          var distX, distY;
          var stepX, stepY;
          var mapX, mapY
          
          var atX=Math.floor(x), atY=Math.floor(y);
  
          var thisRow=-1;
          var thisSide=-1;
  
          var lastHeight=0;
  
          for (var i=0; i<samples; i++) {
                  theta+=pi/(3*samples)+2*pi;
                  theta%=2*pi;
  
                  mapX = atX, mapY = atY;
  
                  deltaX=1/Math.cos(theta);
                  deltaY=1/Math.sin(theta);
  
                  if (deltaX>0) {
                          stepX = 1;
                          distX = (mapX + 1 - x) * deltaX;
                  }
                  else {
                          stepX = -1;
                          distX = (x - mapX) * (deltaX*=-1);                
                  }
                  if (deltaY>0) {
                          stepY = 1;
                          distY = (mapY + 1 - y) * deltaY;
                  }
                  else {
                          stepY = -1;
                          distY = (y - mapY) * (deltaY*=-1);
                  }
                  
  
                  for (var j=0; j<20; j++) {
                          if (distX < distY) {
                                  mapX += stepX;
                                  if (arena[mapX][mapY]) {
                                          if (thisRow!=mapX || thisSide!=0) {
                                                  if (i>0) {
                                                          data.push(i);
                                                          data.push(lastHeight);
                                                  }
                                                  data.push(i);
                                                  data.push(distX);
                                                  thisSide=0;
                                                  thisRow=mapX;
                                                  face.push(1+stepX);
                                          }
                                          lastHeight=distX;
                                          break;
                                  }
                                  distX += deltaX;
                          }
                          else {
                                  mapY += stepY;
                                  if (arena[mapX][mapY]) {
                                          if (thisRow!=mapY || thisSide!=1) {
                                                  if (i>0) {
                                                          data.push(i);
                                                          data.push(lastHeight);
                                                  }        
                                                  data.push(i);
                                                  data.push(distY);
                                                  thisSide=1;
                                                  thisRow=mapY;
                                                  face.push(2+stepY)
                                          }
                                          lastHeight=distY;
                                          break;
                                  }
                                  distY += deltaY;
                          }
                  }
          }
          data.push(i);
          data.push(lastHeight);
          
          return data;
  }
  
  function drawCanvas(){
  
          canvas.clearRect(0,0,400, 300);
  
          var theta = playerDir-pi/6;
  
          var wall=wallDistance(theta);
  
          map.beginPath();
          map.clearRect(0,0,80,80);
          map.fillStyle="#3366CC";
          map.arc(playerPos[0]*8, playerPos[1]*8, 3, 0, 2*pi, true);
          map.fill();
          map.beginPath();
          map.moveTo(8*playerPos[0], 8*playerPos[1]);
  
          var linGrad;
          
          var tl,tr,bl,br;
          
          var theta1,theta2,fix1,fix2;
          
          for (var i=0; i<wall.length; i+=4) {
  
                  theta1=playerDir-pi/6 + pi*wall[i]/(3*samples);
                  theta2=playerDir-pi/6 + pi*wall[i+2]/(3*samples);
                  
                  fix1 = Math.cos(theta1-playerDir);
                  fix2 = Math.cos(theta2-playerDir);
  
                  var h=2-playerPosZ;
  
                  var wallH1=100/(wall[i+1]*fix1);
                  var wallH2=100/(wall[i+3]*fix2);
  
                  tl=[wall[i]*2, 150-wallH1*h];
                  tr=[wall[i+2]*2, 150-wallH2*h]
                  br=[wall[i+2]*2, tr[1]+wallH2*2];
                  bl=[wall[i]*2, tl[1]+wallH1*2]
  
                  var shade1=Math.floor(wallH1*2+20); if (shade1>255) shade1=255;
                  var shade2=Math.floor(wallH2*2+20); if (shade2>255) shade2=255;
  
                  linGrad = canvas.createLinearGradient(tl[0],0,tr[0],0);
                  linGrad.addColorStop(0, 'rgba('+(face[i/4]%2==0 ? shade1 : 0)+','+(face[i/4]==1 ? shade1 : 0)+','+(face[i/4]==2 ? 0 : shade1)+',1.0)');
                  linGrad.addColorStop(1, 'rgba('+(face[i/4]%2==0 ? shade2 : 0)+','+(face[i/4]==1 ? shade2 : 0)+','+(face[i/4]==2 ? 0 : shade2)+',1.0)');
  
                  canvas.beginPath();
                  canvas.moveTo(tl[0], tl[1]);
                  canvas.lineTo(tr[0], tr[1]);
                  canvas.lineTo(br[0], br[1]);
                  canvas.lineTo(bl[0], bl[1]);
                  canvas.fillStyle = linGrad;
                  canvas.fill();
  
          
                  map.lineTo(playerPos[0]*8+Math.cos(theta1)*(wall[i+1])*8, playerPos[1]*8+Math.sin(theta1)*(wall[i+1])*8);
                  map.lineTo(playerPos[0]*8+Math.cos(theta2)*(wall[i+3])*8, playerPos[1]*8+Math.sin(theta2)*(wall[i+3])*8);
  
                  
          }
          map.fillStyle="#FF0000"
          map.fill();
          
  }
  
  function nearWall(x,y){
          var xx,yy;
          if (isNaN(x)) x=playerPos[0];
          if (isNaN(y)) y=playerPos[1];
          for (var i=-0.1; i<=0.1; i+=0.2) {
                  xx=Math.floor(x+i)
                  for (var j=-0.1; j<=0.1; j+=0.2) {
                          yy=Math.floor(y+j);
                          if (arena[xx][yy]) return true;
                  }
          }
          return false;
  }
  
  var xOff = 0;
  var yOff = 0;
  function wobbleGun(){
          var mag=playerVelY;
      xOff = (10+Math.cos(total/6.23)*mag*90)
      yOff = (10+Math.cos(total/5)*mag*90)
          overlay.style.backgroundPosition = xOff + "px " + yOff + "px";
  }
  
  var jumpCycle=0;
  
  var audio = window.Audio && new Audio("shoot.wav");
  
  function shoot()
  {
          audio && audio.play();
          canvas.save();
          canvas.strokeStyle = "#FFFF00";
          canvas.beginPath();
      
          canvas.moveTo(190+xOff, 140+yOff);
          canvas.lineTo(250+xOff, 200+yOff);
          canvas.closePath();
          canvas.stroke();
          canvas.restore();
          setTimeout('drawCanvas()',100);
  }
  
  function update(){
  
          total++;
  
          var change=false;
  
          if (jumpCycle) {
                  jumpCycle--;
                  change=true;
                  playerPosZ = 1 + jumpCycle*(20-jumpCycle)/110;
          }
          else if (key[4]) jumpCycle=20;
          
          if (key[0]) {
                  if (!key[1]) {
                          playerDir-=0.07; //left
                          change=true;
                  }
          }
          else if (key[1]) {
                  playerDir+=0.07; //right
                  change=true;
          }
  
          if (change) {
                  playerDir+=2*pi;
                  playerDir%=2*pi;
                  document.getElementById("sky").style.backgroundPosition=Math.floor(1-playerDir/(2*pi)*2400)+"px 0";
          }
  
          if (key[2] && !key[3]) {
                  if (playerVelY<0.1) playerVelY += 0.02;
          }
          else if (key[3] && !key[2]) {
                  if (playerVelY>-0.1) playerVelY -= 0.02;
          }
          else {
                  if (playerVelY<-0.02) playerVelY += 0.015;
                  else if (playerVelY>0.02) playerVelY -= 0.015;
                  else playerVelY=0;
          }
          
          
          if (playerVelY!=0) {
  
                  var oldX=playerPos[0];;
                  var oldY=playerPos[1];                
                  var newX=oldX+Math.cos(playerDir)*playerVelY;
                  var newY=oldY+Math.sin(playerDir)*playerVelY;
  
                  if (!nearWall(newX, oldY)) {
                          playerPos[0]=newX;
                          oldX=newX;
                          change=true;
                  }
                  if (!nearWall(oldX, newY)) {
                          playerPos[1]=newY;
                          change=true;
                  }
  
          }
          
          if (playerVelY) wobbleGun();
          if (change) drawCanvas();
  
  }
  
  function changeKey(which, to){
          switch (which){
                  case 65:case 37: key[0]=to; break; // left
                  case 87: case 38: key[2]=to; break; // up
                  case 68: case 39: key[1]=to; break; // right
                  case 83: case 40: key[3]=to; break;// down
                  case 32: key[4]=to; break; // space bar;
                  case 17: key[5]=to; break; // ctrl
                  case 66: if (to) { shoot() } break; // b
          }
  }
  document.onkeydown=function(e){changeKey((e||window.event).keyCode, 1);}
  document.onkeyup=function(e){changeKey((e||window.event).keyCode, 0);}
  
  function initUnderMap(){
          var underMap=document.getElementById("underMap").getContext("2d");
          underMap.fillStyle="#FFF";
          underMap.fillRect(0,0, 200, 200);
          underMap.fillStyle="#444";
          for (var i=0; i<arena.length; i++) {
                  for (var j=0; j<arena[i].length; j++) {
                          if (arena[i][j]) underMap.fillRect(i*8, j*8, 8, 8);
                  }        
          }
  }
  
  window.onerror=function(){
          alert('An error has occured, the most likely reason is because you are using an incompatible browser.\nYou must be using one of the following browsers or a newer version:\n\n- Internet Explorer 6\n- Firefox 1.5\n- Safari 1.3\n- Opera 9');
          window.onerror=function(){};
          return true;
  }
  
  window.onload=function(){
          map=document.getElementById("map").getContext("2d");
          canvas=document.getElementById("canvas").getContext("2d");
          overlay=document.getElementById("overlay");
          document.getElementById("sky").style.backgroundPosition=Math.floor(-playerDir/(2*pi)*2400)+"px 0";
          drawCanvas();
          initUnderMap();
          setInterval(update, 35);
  }
  


(C) Æliens 20/2/2008

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.