HTML5 实现的一个俄罗斯方块实例代码


Posted in Javascript onSeptember 19, 2016

示例简单,运行地址为:http://chendd.cn/demo/html/canvas/elsfk.html,得需要支持html5浏览器的环境。

实现的功能:方块旋转(W键)、自动下落、移动(ASD)、消行、快速下落(空格键)、下落阴影、游戏结束。

为实现功能:消行时的计分、等级、以及不同等级的下落速度等。

学习了xiaoE的Java版本的俄罗斯方块后,自己动手使用html5的canvas实现的,

参考效果图如下:

HTML5 实现的一个俄罗斯方块实例代码

详细代码如下:

<!DOCTYPE html>

<html>

 <head>

 <meta charset="utf-8">

 <title>俄罗斯方块</title>

 <style type="text/css">
  /*整个画布*/
  
  #tetris {
  border: 6px solid grey;
  }
  /*游戏面板*/
 </style>

 </head>

 <body>

 <canvas id="tetris" width="565" height="576"></canvas>

 <script type="text/javascript">
  var canvas = document.getElementById("tetris");
  var context = canvas.getContext("2d");
  var padding = 6,
  size = 32,
  minX = 0,
  maxX = 10,
  minY = 0,
  maxY = 18,
  score = 0,
  level = 1;
  var gameMap = new Array(); //游戏地图,二维数组
  var gameTimer;
  initGameMap();
  //绘制垂直线条
  drawGrid();
  var arrays = basicBlockType();
  var blockIndex = getRandomIndex();
  //随机画一个方块意思意思
  var block = getPointByCode(blockIndex);
  context.fillStyle = getBlockColorByIndex(blockIndex);
  drawBlock(block);
  /**

  * 初始化游戏地图

  */
  function initGameMap() {
  for (var i = 0; i < maxY; i++) {
   var row = new Array();
   for (var j = 0; j < maxX; j++) {
   row[j] = false;
   }
   gameMap[i] = row;
  }
  }
  /**

  * 方块旋转

  * 顺时针:

  * A.x =O.y + O.x - B.y

  * A.y =O.y - O.x + B.x

  */
  function round() {
  //正方形的方块不响应旋转  
  if (blockIndex == 4) {
   return;
  }
  //循环处理当前的方块,找新的旋转点
  for (var i = 1; i < block.length; i++) {
   var o = block[0];
   var point = block[i];
   //旋转后的位置不能与现有格子的方块冲突
   var tempX = o.y + o.x - point.y;
   var tempY = o.y - o.x + point.x;
   if (isOverZone(tempX, tempY)) {
   return; //不可旋转
   }
  }
  clearBlock();
  //可以旋转,设置新的旋转后的坐标
  for (var i = 1; i < block.length; i++) {
   var o = block[0];
   var point = block[i];
   //旋转后的位置不能与现有格子的方块冲突
   var tempX = o.y + o.x - point.y;
   var tempY = o.y - o.x + point.x;
   block[i] = {
   x: tempX,
   y: tempY
   };
  }
  drawBlock();
  }
  function moveDown() {
  
  var overFlag = canOver();
  if(overFlag){
   //如果不能向下移动了,将当前的方块坐标载入地图
   window.clearInterval(gameTimer);
   add2GameMap();
   //清除游戏区域内的不同颜色的格子,使用单一颜色重新绘制地图堆积物
   redrawGameMap();
   return;//游戏结束
  }
  
  var flag = moveTo(0, 1);
  //如果可以移动,则继续移动
  if (flag) {
   return;
  }
  //如果不能向下移动了,将当前的方块坐标载入地图
  add2GameMap();
  
  //进行消行动作
  clearLines();
  //清除游戏区域内的不同颜色的格子,使用单一颜色重新绘制地图堆积物
  redrawGameMap();
  //如果不能向下移动,则继续下一个方块
  nextBlock();
  }
  
  /**

  * 消行动作,返回消除的行数

  */
  function clearLines() {
  var clearRowList = new Array();
  for (var i = 0; i < maxY; i++) {
   var flag = true;
   for (var j = 0; j < maxX; j++) {
   if (gameMap[i][j] == false) {
    flag = false;
    break;
   }
   }
   if (flag) {
   clearRowList.push(i); //记录消除行号的索引
   }
  }
  var clearRows = clearRowList.length;
  //所谓的消行就是将待消除行的索引,下方所有的格子上移动
  for (var x = 0; x < clearRows; x++) {
   var index = clearRowList[x];
   for (var i = index; i > 0; i--) {
   for (var j = 0; j < maxX; j++) {
    gameMap[i][j] = gameMap[i - 1][j];
   }
   }
  }
  if (clearRows > 0) {
   for (var i = 0; i < maxY; i++) {
   //此处可以限制满足相关条件的方块进行清除操作&& j < clearRowList[clearRows - 1]
   for (var j = 0; j < maxX; j++) {
    if (gameMap[i][j] == false) {
    clearBlockByPoint(i, j);
    }
   }
   }
  }
  }
  /**

  * 重绘游戏地图

  */
  function redrawGameMap() {
  drawGrid();
  for (var i = 0; i < maxY; i++) {
   for (var j = 0; j < maxX; j++) {
   if (gameMap[i][j]) {
    roadBlock(j, i);
   }
   }
  }
  }
  /**

  * 打印阴影地图

  */
  function drawShadowBlock() {
  var currentBlock = block;
  var shadowPoints = getCanMoveDown();
  if (shadowPoints != null && shadowPoints.length > 0) {
   for (var i = 0; i < shadowPoints.length; i++) {
   var point = shadowPoints[i];
   if (point == null) {
    continue;
   }
   var start = point.x * size;
   var end = point.y * size;
   context.fillStyle = "#abcdef";
   context.fillRect(start, end, size, size);
   context.strokeStyle = "black";
   context.strokeRect(start, end, size, size);
   }
  }
  }
  /**

  * 返回最多可移动到的坐标位置(统计总共可以下落多少步骤)

  * @return最多可移动到的坐标位置

  */
  function getCanMoveDown() {
  var nps = canMove(0, 1, block);
  var last = null;
  if (nps != null) {
   last = new Array();
   while ((nps = canMove(0, 1, nps)) != null) {
   if (nps != null) {
    last = nps;
   }
   }
  }
  return last;
  }
  
  function canOver(){
  var flag = false;
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var x = point.x;
   var y = point.y;
   if(isOverZone(x , y)){
   flag = true;
   break;
   }
  }
  return flag;
  }
  
  function drawLevelScore() {
  
  }
  /**

  * 将不能移动的各种填充至地图

  */
  function add2GameMap() {
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var x = point.x;
   var y = point.y;
   var gameMapRow = gameMap[y]; //获取到地图的一行
   gameMapRow[x] = true; //将此行中的某个格子标记为堆积物
   gameMap[y] = gameMapRow; //再将行给设置回来
  }
  }
  function moveLeft() {
  moveTo(-1, 0);
  }
  function moveRight() {
  moveTo(1, 0);
  }
  function quickDown() {
  while (moveTo(0, 1));
  }
  function moveTo(moveX, moveY) {
  var move = canMove(moveX, moveY, block); //判定是否可以移动
  if (move == null) {
   return false;
  }
  clearBlock();
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   point.x = point.x + moveX;
   point.y = point.y + moveY;
  }
  drawBlock();
  return true;
  }
  /**

  * 下一个方块

  */
  function nextBlock() {
  blockIndex = getRandomIndex();
  block = getPointByCode(blockIndex);
  context.fillStyle = getBlockColorByIndex(blockIndex);
  drawBlock();
  }
  document.onkeypress = function(evt) {
  var key = window.event ? evt.keyCode : evt.which;
  switch (key) {
   case 119: //向上旋转 W
   round();
   break;
   case 115: //向下移动 S
   moveDown();
   break;
   case 97: //向左移动 A
   moveLeft();
   break;
   case 100: //向右移动 D
   moveRight();
   break;
   case 32: //空格键快速下落到底
   quickDown();
   break;
  }
  }
  /**

  * 判定是否可以移动

  * @parammoveX 横向移动的个数

  * @parammoveY 纵向移动的个数

  */
  function canMove(moveX, moveY, currentBlock) {
  var flag = true;
  var newPoints = new Array();
  for (var i = 0; i < currentBlock.length; i++) {
   var point = currentBlock[i];
   var tempX = point.x + moveX;
   var tempY = point.y + moveY;
   if (isOverZone(tempX, tempY)) {
   flag = false;
   break;
   }
  }
  if (flag) {
   for (var i = 0; i < currentBlock.length; i++) {
   var point = currentBlock[i];
   var tempX = point.x + moveX;
   var tempY = point.y + moveY;
   newPoints[i] = {
    x: tempX,
    y: tempY
   };
   }
   return newPoints;
  }
  return null;
  }
  /**

  * 判定是否可以移动

  * @paramx 预移动后的横坐标

  * @paramy 预移动后的纵坐标

  */
  function isOverZone(x, y) {
  return x < minX || x >= maxX || y < minY || y >= maxY || gameMap[y][x];
  }
  document.body.click();
  
  gameTimer = window.setInterval(moveDown , 800);
  
  /**

  * 初始化方块的基础数据

  */
  function basicBlockType() {
  var arrays = new Array();
  arrays[0] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 6,
   y: 0
  }];
  arrays[1] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 4,
   y: 1
  }];
  arrays[2] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 3,
   y: 1
  }];
  arrays[3] = [{
   x: 4,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 3,
   y: 1
  }, {
   x: 4,
   y: 1
  }];
  arrays[4] = [{
   x: 4,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 4,
   y: 1
  }, {
   x: 5,
   y: 1
  }];
  arrays[5] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 5,
   y: 0
  }, {
   x: 5,
   y: 1
  }];
  arrays[6] = [{
   x: 4,
   y: 0
  }, {
   x: 3,
   y: 0
  }, {
   x: 4,
   y: 1
  }, {
   x: 5,
   y: 1
  }];
  return arrays;
  }
  function basicBlockColor() {
  return ["#A00000", "#A05000", "#A0A000", "#00A000", "#00A0A0", "#0000A0", "#A000A0"];
  }
  function getBlockColorByIndex(typeCodeIndex) {
  var arrays = basicBlockColor();
  return arrays[typeCodeIndex];
  }
  /**

  * 根据编号返回指定编号的方块

  * @paramtypeCodeIndex 方块编号索引

  */
  function getPointByCode(typeCodeIndex) {
  var arrays = basicBlockType();
  return arrays[typeCodeIndex];
  }
  /**

  * 获取随即出现方块的范围值

  * @paramlens 随机数的范围

  */
  function getRandomIndex() {
  return parseInt(Math.random() * (arrays.length - 1), 10);
  }
  /**

  * 绘制方块,按格子单个绘制

  */
  function drawBlock() {
  drawGrid();
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var start = point.x * size;
   var end = point.y * size;
   context.fillStyle = getBlockColorByIndex(blockIndex);
   context.fillRect(start, end, size, size);
   context.strokeStyle = "black";
   context.strokeRect(start, end, size, size);
  }
  drawShadowBlock();
  }
  /**

  * 绘制障碍物

  */
  function roadBlock(x, y) {
  context.fillStyle = "darkgray";
  var start = x * size;
  var end = y * size;
  context.fillRect(start, end, size, size);
  }
  /**

  * 绘制新的方块先清除之前的方块

  */
  function clearBlock() {
  for (var i = 0; i < block.length; i++) {
   var point = block[i];
   var start = point.x * size;
   var end = point.y * size;
   context.clearRect(start, end, size, size);
  }
  }
  /**

  * 初始化一个新的行

  */
  function initGameMapRow() {
  var array = new Array();
  for (var i = 0; i < maxX; i++) {
   array[i] = false;
  }
  return array;
  }
  /**

  * 根据坐标清除指定格子的内容

  * @paramx 横坐标

  * @paramy 纵坐标

  */
  function clearBlockByPoint(x, y) {
  var start = y * size;
  var end = x * size;
  context.clearRect(start, end, size, size);
  }
  /**

  * 清掉所有位置的空白格的绘图

  */
  function clearAllNullPoint() {
  for (var i = 0; i < maxY; i++) {
   for (var j = 0; j < maxX; j++) {
   if (gameMap[i][j] == false) {
    clearBlockByPoint(i, j);
   }
   }
  }
  }
  /**

  * 绘制网格线

  * @paramcontext 绘图对象

  */
  function drawGrid() {
  clearAllNullPoint(); //清除掉当前方块下落位置造成的阴影
  context.strokeStyle = "grey"; //画笔颜色
  for (var i = 0; i <= maxX; i++) {
   var start = i * size;
   var end = start + size;
   context.beginPath();
   context.moveTo(start, 0);
   context.lineTo(size * i, size * maxY);
   context.stroke();
   context.closePath();
  }
  //绘制水平线条
  for (var i = 0; i <= maxY; i++) {
   var start = i * size;
   var end = start + size;
   context.beginPath();
   context.moveTo(0, size * i);
   context.lineTo(size * maxX, size * i);
   context.stroke();
   context.closePath();
  }
  }
 </script>

 </body>

</html>

以上就是HTML5 实现的一个俄罗斯方块的实例,有兴趣的小伙伴可以参考下,谢谢大家对本站的支持!

Javascript 相关文章推荐
prototype 中文参数乱码解决方案
Nov 09 Javascript
jquery 操作表格实现代码(多种操作打包)
Mar 20 Javascript
jquery.validate.js插件使用经验记录
Jul 02 Javascript
jQuery原生的动画效果
Jul 10 Javascript
javascript:void(0)是什么意思及href=#与href=javascriptvoid(0)的区别
Nov 13 Javascript
JS获取数组最大值、最小值及长度的方法
Nov 24 Javascript
深入学习jQuery Validate表单验证
Jan 18 Javascript
聊一聊jQuery插件uploadify使用方法
Aug 24 Javascript
JavaScript中Array的实用操作技巧分享
Sep 11 Javascript
Node.js的文件权限及读写flag详解
Oct 11 Javascript
JQuery学习总结【一】
Dec 01 Javascript
jQuery实现轮播图源码
Oct 23 jQuery
javascript this详细介绍
Sep 19 #Javascript
JS遍历ul下的li点击弹出li的索引的实现方法
Sep 19 #Javascript
vuejs在解析时出现闪烁的原因及防止闪烁的方法
Sep 19 #Javascript
javascript 判断页面访问方式电脑或者移动端
Sep 19 #Javascript
详解Node.Js如何处理post数据
Sep 19 #Javascript
React Native实现简单的登录功能(推荐)
Sep 19 #Javascript
在Web项目中引入Jquery插件报错的完美解决方案(图解)
Sep 19 #Javascript
You might like
fleaphp下不确定的多条件查询的巧妙解决方法
2008/09/11 PHP
php fsockopen解决办法 php实现多线程
2014/01/20 PHP
PHP学习笔记之字符串编码的转换和判断
2014/05/22 PHP
PHP四舍五入、取整、round函数使用示例
2015/02/06 PHP
PHP判断密码强度的方法详解
2017/05/26 PHP
PHP实现函数内修改外部变量值的方法示例
2018/12/28 PHP
js option删除代码集合
2008/11/12 Javascript
JavaScript中的匀速运动和变速(缓冲)运动详细介绍
2012/11/11 Javascript
jquery实现倒计时代码分享
2014/06/13 Javascript
JS实现清除指定cookies的方法
2014/09/20 Javascript
完美兼容各大浏览器的jQuery插件实现图片切换特效
2014/12/12 Javascript
jQuery中find()方法用法实例
2015/01/07 Javascript
使用bootstrap3开发响应式网站
2016/05/12 Javascript
js HTML5多图片上传及预览实例解析(不含前端的文件分割)
2016/08/26 Javascript
如何选择jQuery版本 1.x? 2.x? 3.x?
2017/04/01 jQuery
Node.js命令行/批处理中如何更改Linux用户密码浅析
2018/07/22 Javascript
jQuery zTree插件使用简单教程
2019/08/16 jQuery
使用axios发送post请求,将JSON数据改为form类型的示例
2019/10/31 Javascript
js实现点击上传图片并设为模糊背景
2020/08/02 Javascript
vue3中轻松实现switch功能组件的全过程
2021/01/07 Vue.js
Python实现国外赌场热门游戏Craps(双骰子)
2015/03/31 Python
Windows下Eclipse+PyDev配置Python+PyQt4开发环境
2016/05/17 Python
tensorflow学习笔记之简单的神经网络训练和测试
2018/04/15 Python
python中csv文件的若干读写方法小结
2018/07/04 Python
Python3+OpenCV2实现图像的几何变换(平移、镜像、缩放、旋转、仿射)
2019/05/13 Python
用Python调用win命令行提高工作效率的实例
2019/08/14 Python
为什么说Python可以实现所有的算法
2019/10/04 Python
PyTorch的自适应池化Adaptive Pooling实例
2020/01/03 Python
Python模块future用法原理详解
2020/01/20 Python
python使用for...else跳出双层嵌套循环的方法实例
2020/05/17 Python
护理自荐信
2013/10/22 职场文书
电大毕业自我鉴定
2014/02/03 职场文书
三人合伙协议书范本
2014/10/29 职场文书
稽核岗位职责范本
2015/04/13 职场文书
2015年学校总务处工作总结
2015/05/19 职场文书
pytorch 实现在测试的时候启用dropout
2021/05/27 Python