国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

JS+Canvas實現的俄羅斯方塊游戲完整實例

2019-11-19 18:33:50
字體:
來源:轉載
供稿:網友

本文實例講述了JS+Canvas實現的俄羅斯方塊游戲。分享給大家供大家參考,具體如下:

試玩(沒有考慮兼容低版本瀏覽器):

**********************************************************************
9月3日更新:
修復了隱藏的比較深的BUG
加上暫停、再來一次功能
速度隨分數增高而遞減
添加log日志
*********************************************************************

通過寫這個游戲收獲幾點:

1、canvas的isPointInPath方法不支持fillRect、strokeRect的上下文。

2、canvas有內邊距(padding)時,傳入isPointInPath方法的坐標需要減去padding值。

3、通過json的方法JSON.parse(JSON.stringify(Object))可以快速克隆Object對象,但是要注意:當Object對象里有方法時,克隆過來依然是引用關系。

源碼:

<!DOCTYPE html><html lang="zh-CN">  <head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>canvas版俄羅斯方塊</title>  <style>  #canvas{    background: #272822;    display: block;    margin: 0 auto;  }  </style></head><body><p style="text-align: center;">操作:↑變形;↓下移;←左移;→右移</p><canvas id="canvas" width="640" height="600">  您的瀏覽器不支持canvas!</canvas><script>/****************************作者:王美建 2015年9月3日20:30:35*后續可添加怪異變形,類似于L可變成Z*積分隨關卡遞增*初始化部分historyBlock****************************/var tetris = {  canvas : document.getElementById("canvas"),  ctx : this.canvas.getContext("2d"),  width : 481,  height : 600,  logLen: 8,  mapColor: "#464741",  logColor: "green",  status: 'ready',  unit : 30,  curText : "開始",  blockData : function(index, row, col){    var r = row || 1,      c = col || Math.floor((this.col - 3)/2) + 2,      block = [        [          {color: 'red', status: 1, data: [{x: r, y:c-1}, {x: r+1, y:c-1}, {x: r+1, y:c}, {x: r+1, y:c+1}], center: {x: r, y: c}},          {color: 'red', status: 2, data: [{x: r-1, y:c-1}, {x: r-1, y:c}, {x: r, y:c-1}, {x: r+1, y:c-1}], center: {x: r, y: c}},          {color: 'red', status: 3, data: [{x: r-1, y:c-1}, {x: r-1, y:c}, {x: r-1, y:c+1}, {x: r, y:c+1}], center: {x: r, y: c}},          {color: 'red', status: 4, data: [{x: r-1, y:c+1}, {x: r, y:c+1}, {x: r+1, y:c+1}, {x: r+1, y:c}], center: {x: r, y: c}}        ],        [          {color: 'green', status: 1, center: {x: r, y:c}, data: [{x: r, y:c+1}, {x: r+1, y:c-1}, {x: r+1, y:c}, {x: r+1, y:c+1}]},          {color: 'green', status: 2, center: {x: r, y:c}, data: [{x: r-1, y:c-1}, {x: r, y:c-1}, {x: r+1, y:c-1}, {x: r+1, y:c}]},          {color: 'green', status: 3, center: {x: r, y:c}, data: [{x: r, y:c-1}, {x: r-1, y:c-1}, {x: r-1, y:c}, {x: r-1, y:c+1}]},          {color: 'green', status: 4, center: {x: r, y:c}, data: [{x: r-1, y:c}, {x: r-1, y:c+1}, {x: r, y:c+1}, {x: r+1, y:c+1}]}        ],        [          {color: 'blue', status: 1, center: {x: r, y:c}, data: [{x: r, y:c-1}, {x: r, y:c}, {x: r+1, y:c}, {x: r+1, y:c+1}]},          {color: 'blue', status: 2, center: {x: r, y:c}, data: [{x: r+1, y:c-1}, {x: r, y:c-1}, {x: r, y:c}, {x: r-1, y:c}]}        ],        [          {color: 'orange', status: 1, center: {x: r, y:c}, data: [{x: r+1, y:c-1}, {x: r+1, y:c}, {x: r, y:c}, {x: r, y:c+1}]},          {color: 'orange', status: 2, center: {x: r, y:c}, data: [{x: r-1, y:c}, {x: r, y:c}, {x: r, y:c+1}, {x: r+1, y:c+1}]}        ],        [          {color: 'yellow', status: 1, center: {x: r, y:c}, data: [{x: r, y:c-1}, {x: r, y:c}, {x: r+1, y:c-1}, {x: r+1, y:c}]}        ],        [          {color: 'aqua', status: 1, center: {x: r, y:c}, data: [{x: r, y:c-1}, {x: r, y:c}, {x: r, y:c+1}, {x: r-1, y:c}]},          {color: 'aqua', status: 2, center: {x: r, y:c}, data: [{x: r+1, y:c}, {x: r, y:c}, {x: r, y:c+1}, {x: r-1, y:c}]},          {color: 'aqua', status: 3, center: {x: r, y:c}, data: [{x: r, y:c-1}, {x: r, y:c}, {x: r, y:c+1}, {x: r+1, y:c}]},          {color: 'aqua', status: 4, center: {x: r, y:c}, data: [{x: r, y:c-1}, {x: r, y:c}, {x: r+1, y:c}, {x: r-1, y:c}]}        ],        [          {color: 'indigo', status: 1, center: {x: r, y:c}, data: [{x: r, y:c-1}, {x: r, y:c}, {x: r, y:c+1}, {x: r, y:c+2}]},          {color: 'indigo', status: 2, center: {x: r, y:c}, data: [{x: r-2, y:c}, {x: r-1, y:c}, {x: r, y:c}, {x: r+1, y:c}]}        ]      ]    return block[index];  },  init : function(){    var self = this;    self.reset();    self.addEvent("keydown", window, function(ev){      var ev = ev || window.event,        code = ev.keycode || ev.which;      if(self.handle[code] && self.status === "play"){        self.handle[code].call(self);        self.createMap();        ev.preventDefault();      }    })    self.addEvent("click", document, function(ev){      self.createMap(ev);    })    return this;  },  reset: function(){    var self = this;    self.score = 0;    self.speed = 1000;    self.log = [];    self.historyBlock = [];    self.row = Math.floor(self.height/self.unit);    self.col = Math.floor(self.width/self.unit);    self.curBlockIndex = Math.round(Math.random()*6);    self.curBlocks = self.blockData(self.curBlockIndex);    self.curBlock = self.curBlocks[0];    self.createNext().createMap();    return this;  },  createNext: function(){    var self = this;    self.nextBlockIndex = self.status === "ready" ? self.curBlockIndex : Math.round(Math.random()*6);    self.nextBlocks = self.blockData(self.nextBlockIndex, 4, self.col+3);    self.nextBlock = self.nextBlocks[0];    return this;  },  addEvent : function(ev, ele, callback){    if( ele.addEventListener ){      ele.addEventListener(ev,callback,false);    }else{      ele.attachEvent("on"+ev, callback);    }  },  createMap : function(ev){    var self = this,      ctx = self.ctx;    ctx.clearRect(0, 0, self.canvas.width, self.canvas.height);    for (var i = 0; i < self.col; i++) {      for (var j = 0; j < self.row; j++) {        ctx.save();        ctx.strokeStyle = self.mapColor;        ctx.strokeRect(i*self.unit, j*self.unit, self.unit, self.unit);        ctx.stroke();        ctx.restore();      };    };    self.showText(ev).createBlock().createLog();    if(self.status !== "ready"){      self.drawRect(self.curBlock.data);    }    return this;  },  createBlock : function(){    var self = this,      block = self.curBlock.data;    self.drawRect(self.historyBlock);    if(self.collide(40, true)){      block.map(function(val){        val.x--;      })      setTimeout(function(){        clearInterval(self.timer);        if(localStorage.getItem("score") === null){          localStorage.setItem("score", self.score);        }else if(localStorage.getItem("score") - self.score < 0 ){          localStorage.setItem("score", self.score);          alert("新紀錄!"+self.score+"分!");          self.printLog({log:"新紀錄!"+self.score+"分!", color: 'red'});          return;        }        self.status = "over";        self.curText = "重來";        self.showText();        self.printLog({log:"GAME OVER", color: 'red'});      },10)    }    return this;  },  drawRect : function(block){    var self = this;    for (var i = 0; i < block.length; i++) {      self.ctx.save();      self.ctx.fillStyle = block[i].color || self.curBlock.color;      self.ctx.strokeStyle = self.mapColor;      self.ctx.fillRect((block[i].y - 1)*self.unit, (block[i].x - 1)*self.unit, self.unit, self.unit );      self.ctx.strokeRect((block[i].y - 1)*self.unit, (block[i].x - 1)*self.unit, self.unit, self.unit );      self.ctx.restore();    };  },  move : function(){    var self = this;    clearInterval(self.timer);    self.timer = setInterval(function(){      // 實時刷新數據 大坑!      var curBlock = self.curBlock,        data = self.curBlock.data;      if( self.collide() || data.some(function(val){        return val.x + 1 > self.row;      }) ){        clearInterval(self.timer);        self.historyBlock.push.apply(self.historyBlock, data.map(function(val){            val.color = curBlock.color;            return val;        }));        self.remove();        self.curBlockIndex = self.nextBlockIndex;        self.curBlocks = self.blockData(self.curBlockIndex);        self.curBlock = self.curBlocks[0];        self.createNext().createMap().move();        return false;      }      for (var i = 0; i < data.length; i++) {        data[i].x++;      };      self.curBlock.center.x++;      self.createMap();    }, self.speed)    return this;  },  remove : function(){    var self = this,      count = {},      n = 0,      maxRow = 0,      delArr = [],      block = self.historyBlock;    for (var i = 0; i < block.length; i++) {      if(count[block[i].x]){        count[block[i].x].length += 1;      }else{        count[block[i].x] = [1];      }    };    console.log( count )    for (var attr in count) {      if(count[attr].length === self.col){        n++;        //maxRow = attr > maxRow ? attr : maxRow;        for (var i = 0; i < block.length; i++) {          if(block[i].x == attr){            delArr = block.splice(i, 1);            i--;          }        };        count[attr].length = 0;        block.forEach(function(val){          val.x < attr && (val.x += 1);        })      }    };    // 邊消除邊下降會死循環    if(n > 0){      self.score += n*100;      self.printLog("得分+"+n*100);      // 一次消除3行獎勵100分      if(n === 3){        self.score += 100;        self.printLog("獎勵"+100+"分~");      }      // 一次消除4行獎勵200分      if(n === 4){        self.score += 200;        self.printLog("獎勵"+200+"分~");      }      /*block.forEach(function(val){        val.x < maxRow && (val.x += n);      })*/      self.changeSpeed();    }  },  changeSpeed: function(){    var self = this;    if( self.score >= 3000 && self.score < 5000 ){      self.speed = 800;    }else if( self.score >= 5000 && self.score < 10000 ){      self.speed = 600;      self.score += 100;    }else if( self.score >= 10000 && self.score < 20000 ){      self.speed = 400;      self.score += 150;    }else if( self.score >= 20000 && self.score < 40000 ){      self.speed = 200;      self.score += 200;    }else if( self.score >= 40000 ){      self.speed = 100;      self.score += 300;    }    return this;  },  collide : function(direction, isCreate){    var block = JSON.parse(JSON.stringify(this.curBlock)),      result = false,      self = this;    direction = direction || 40;    // direction:碰撞方向,默認下方    if(direction === 37){      self.mLeft(block);    }else if(direction === 38){      block = self.distortion(block);    }else if(direction === 39){      self.mRight(block);    }else if(direction === 40 && !isCreate){      // 非新增方塊則往下移動一格      block.data.forEach(function(val){        val.x++;      })    }    result = block.data.some(function(val){      return (val.x > self.row || val.y < 1 || val.y > self.col);    })    if(result){      return result;    }else{      return block.data.some(function(val){        return self.historyBlock.some(function(value){          return (value.x === val.x && value.y === val.y);        })      })    }  },  mLeft : function(block){    if(block.data.every(function(val){      return val.y - 1 >= 1;    })){      block.data.forEach(function(val){        val.y--;      })      block.center.y--;    }  },  mRight : function(block){    var self = this;    if(block.data.every(function(val){      return val.y + 1 <= self.col;    })){      block.data.forEach(function(val){        val.y++;      })      block.center.y++;    }  },  distortion : function(block){    var self = this,      curRow = block.center.x,      curCol = block.center.y,      status = block.status + 1 > self.curBlocks.length ? 1 : block.status + 1;    self.curBlocks = self.blockData(self.curBlockIndex, block.center.x, block.center.y);    return self.curBlocks[status-1];  },  // 控制:上(變形)、右(右移)、下(下移)、左(左移)  handle : {    // 左鍵 code 37    37: function(){      var self = this;      if(!self.collide(37)){        self.mLeft(self.curBlock);      }    },    // 上鍵 code 38    38: function(){      var self = this;      if(!self.collide(38)){        self.curBlock = self.distortion(self.curBlock);      }    },    // 右鍵 code 39    39: function(){      var self = this;      if(!self.collide(39)){        self.mRight(self.curBlock);      }    },    // 下鍵 code 40    40: function(){      var self = this;      if(!self.collide()){        self.curBlock.data.forEach(function(val){          val.x++;        })        self.curBlock.center.x++;      }    }  },  showText: function(ev){    var self = this,      ctx = self.ctx;    ctx.clearRect(self.width, 0, self.canvas.width - self.width, self.height);    ctx.save();    ctx.beginPath();    ctx.font = "20px Verdana";    ctx.fillStyle = "green";    ctx.fillText("下一個:", self.width+10, 30);    ctx.fillText("分數:", self.width+10, 200);    ctx.fillText("速度:", self.width+10, 260);    ctx.fillText("作者:王美建", self.width+10, 580);    ctx.fillStyle = "red";    ctx.fillText(self.score, self.width+10, 230);    ctx.fillText(self.speed + "毫秒", self.width+10, 290);    ctx.fillStyle = "green";    ctx.fillRect(self.width + 30, 320, 100, 40);    // isPointInPath對fillRect()兼容不好 又一個大坑!    ctx.rect(self.width + 30, 320, 100, 40);    if( ev && ctx.isPointInPath(ev.layerX, ev.layerY) ){      switch(self.status){        case "ready":        self.status = "play";        self.curText = "暫停";        self.log = ["開始游戲."];        self.createNext().move();        break;        case "play":        self.status = "paush";        self.curText = "繼續";        clearInterval(self.timer);        self.printLog("暫停.");        break;        case "paush":        self.status = "play";        self.curText = "暫停";        self.printLog("繼續游戲~");        self.move();        break;        case "over":        self.status = "play";        self.curText = "暫停";        self.reset().move();        self.printLog("開始游戲~");        break;      }    }    ctx.fillStyle = "black";    ctx.fillText(self.curText, self.width+60, 350);    ctx.restore();    ctx.closePath();    self.nextBlock.data.forEach(function(val){      val.color = self.nextBlock.color;    })    self.drawRect(self.nextBlock.data);    return this;  },  printLog: function(log){    var self = this;    if(log){      self.log.unshift(log);      self.log.length > self.logLen && (self.log.length = self.logLen);    }    self.createLog();    return this;  },  createLog: function(){    var self = this,      ctx = self.ctx;    // 清除log    ctx.clearRect(self.width+10, 380, 136, 170);    self.log.forEach(function(val, index){      if(val){        ctx.save();        ctx.font = "16px Verdana";        ctx.fillStyle = val.color || self.logColor,        ctx.fillText(val.log || val, self.width+10, 400+index*22);        ctx.restore();      }    })    return this;  }}tetris.init();</script></body></html>

持續優化中……

更多關于JavaScript相關內容感興趣的讀者可查看本站專題:《JavaScript數據結構與算法技巧總結》、《JavaScript數學運算用法總結》、《JavaScript切換特效與技巧總結》、《JavaScript查找算法技巧總結》、《JavaScript動畫特效與技巧匯總》、《JavaScript錯誤與調試技巧總結》及《JavaScript遍歷算法與技巧總結

希望本文所述對大家JavaScript程序設計有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 延安市| 台江县| 惠水县| 中阳县| 双辽市| 上思县| 区。| 乡宁县| 闵行区| 随州市| 荣昌县| 道孚县| 武乡县| 玉门市| 若羌县| 呼玛县| 广宗县| 读书| 兴宁市| 彭泽县| 青海省| 阳泉市| 厦门市| 阳东县| 香港| 蚌埠市| 长治市| 盘山县| 徐水县| 彭阳县| 隆尧县| 什邡市| 简阳市| 江西省| 阿拉尔市| 昆明市| 阿合奇县| 清镇市| 定西市| 吴旗县| 锦屏县|