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 相关文章推荐
jquery 鼠标滑动显示详情应用示例
Jan 24 Javascript
jQuery实现定时读取分析xml文件的方法
Jul 16 Javascript
angularjs学习笔记之完整的项目结构
Sep 26 Javascript
jQuery实现的左右移动焦点图效果
Jan 14 Javascript
JS选取DOM元素的简单方法
Jul 08 Javascript
详解Vue使用 vue-cli 搭建项目
Apr 20 Javascript
node中Express 动态设置端口的方法
Aug 04 Javascript
vue项目中使用Hbuilder打包app 设置沉浸式状态栏的方法
Oct 22 Javascript
微信小程序实现获取用户信息并存入数据库操作示例
May 07 Javascript
解决iview table组件里的 固定列 表格不自适应的问题
Nov 13 Javascript
JavaScript实现简单动态表格
Dec 02 Javascript
在Angular项目使用socket.io实现通信的方法
Jan 05 Javascript
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
用PHP将数据导入到Foxmail的实现代码
2010/09/05 PHP
php+highchats生成动态统计图
2014/05/21 PHP
php使用Imagick生成图片的方法
2015/07/31 PHP
WordPress用户登录框密码的隐藏与部分显示技巧
2015/12/31 PHP
[原创]php使用curl判断网页404(不存在)的方法
2016/06/23 PHP
Yii安装与使用Excel扩展的方法
2016/07/13 PHP
jQuery 添加/移除CSS类实现代码
2010/02/11 Javascript
THREE.JS入门教程(5)你应当知道的十件事
2013/01/24 Javascript
Jquery自定义button按钮的几种方法
2014/06/11 Javascript
jQuery制作简单柱状图实例
2015/01/28 Javascript
js实现获取当前时间是本月第几周的方法
2015/08/11 Javascript
Jquery通过ajax请求NodeJS返回json数据实例
2016/11/08 NodeJs
nodejs的HTML分析利器node-jquery用法浅析
2016/11/08 NodeJs
vue2.0嵌套路由实现豆瓣电影分页功能(附demo)
2017/03/13 Javascript
在vue中实现点击选择框阻止弹出层消失的方法
2018/09/15 Javascript
150行Node.js实现的dns代理工具
2019/08/02 Javascript
[01:14:34]DOTA2上海特级锦标赛C组资格赛#2 LGD VS Newbee第一局
2016/02/28 DOTA
Python中类的定义、继承及使用对象实例详解
2015/04/30 Python
简单介绍Python中的try和finally和with方法
2015/05/05 Python
Python列表list解析操作示例【整数操作、字符操作、矩阵操作】
2017/07/25 Python
Python 装饰器实现DRY(不重复代码)原则
2018/03/05 Python
python内置数据类型之列表操作
2018/11/12 Python
python并发编程多进程之守护进程原理解析
2019/08/20 Python
Django中从mysql数据库中获取数据传到echarts方式
2020/04/07 Python
英国建筑用品在线:Building Supplies Online(BSO)
2018/04/30 全球购物
波兰购物网站:MALL.PL
2019/05/01 全球购物
GC是什么?为什么要有GC?
2013/12/08 面试题
会计电算化专业毕业生推荐信
2013/12/24 职场文书
优秀士兵先进事迹
2014/02/06 职场文书
品牌宣传方案
2014/03/21 职场文书
2014年人事工作总结范文
2014/11/19 职场文书
2014年副班长工作总结
2014/12/10 职场文书
写给导师的自荐信
2015/03/06 职场文书
毕业欢送会致辞
2015/07/29 职场文书
干部作风纪律整顿心得体会
2016/01/23 职场文书
关于CentOS 8 搭建MongoDB4.4分片集群的问题
2021/10/24 MongoDB