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实现的水平和垂直居中的div窗口
Aug 08 Javascript
javascript中对Attr(dom中属性)的操作示例讲解
Dec 02 Javascript
jQuery随机密码生成的方法
Mar 09 Javascript
javascript产生随机数方法汇总
Jan 25 Javascript
jquery css实现邮箱自动补全
Nov 14 Javascript
AngularJS入门教程之路由机制ngRoute实例分析
Dec 13 Javascript
使用angular帮你实现拖拽的示例
Jul 05 Javascript
基于JavaScript实现无限加载瀑布流
Jul 21 Javascript
vue实现双向绑定和依赖收集遇到的坑
Nov 29 Javascript
Vue代码整洁之去重方法整理
Aug 06 Javascript
vue quill editor 使用富文本添加上传音频功能
Jan 14 Javascript
Vite + React从零开始搭建一个开源组件库
Jun 25 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的计数器程序
2006/10/09 PHP
php 高效率写法 推荐
2010/02/21 PHP
组合算法的PHP解答方法
2012/02/04 PHP
C#使用PHP服务端的Web Service通信实例
2014/04/08 PHP
基于递归实现的php树形菜单代码
2014/11/19 PHP
PHP的关于变量和日期处理的一些面试题目整理
2015/08/10 PHP
YII Framework教程之异常处理详解
2016/03/14 PHP
thinkphp3.x中display方法及show方法的用法实例
2016/05/19 PHP
ThinkPHP框架实现定时执行任务的两种方法分析
2018/09/04 PHP
javascript 的Document属性和方法集合
2010/01/25 Javascript
JS实现n秒后自动跳转的两种方法
2020/11/30 Javascript
angular使用bootstrap方法手动启动的实例代码
2017/07/18 Javascript
vue动态改变背景图片demo分享
2018/09/13 Javascript
javascript 设计模式之享元模式原理与应用详解
2020/04/08 Javascript
vue 数据操作相关总结
2020/12/17 Vue.js
微信小程序实现购物车小功能
2020/12/30 Javascript
[02:28]DOTA2亚洲邀请赛 LGD战队巡礼
2015/02/03 DOTA
[02:17]2016国际邀请赛中国区预选赛VG战队领队采访
2016/06/26 DOTA
[05:20]2018DOTA2亚洲邀请赛主赛事第三日战况回顾 LGD率先挺进胜者组决赛
2018/04/06 DOTA
[52:03]Secret vs VG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
Python解析最简单的验证码
2016/01/07 Python
解析Python中的__getitem__专有方法
2016/06/27 Python
Python使用matplotlib填充图形指定区域代码示例
2018/01/16 Python
Python基于FTP模块实现ftp文件上传操作示例
2018/04/23 Python
对Python的zip函数妙用,旋转矩阵详解
2018/12/13 Python
softmax及python实现过程解析
2019/09/30 Python
python实现身份证实名认证的方法实例
2019/11/08 Python
Python实现括号匹配方法详解
2020/02/10 Python
Python自动采集微信联系人的实现示例
2020/02/28 Python
Python多线程通信queue队列用法实例分析
2020/03/24 Python
中华魂演讲稿
2014/05/13 职场文书
教师一帮一活动总结
2014/07/08 职场文书
2019单位介绍信怎么写
2019/06/24 职场文书
Mysql数据库按时间点恢复实战记录
2021/06/30 MySQL
java设计模式--三种工厂模式详解
2021/07/21 Java/Android
关于python3 opencv 图像二值化的问题(cv2.adaptiveThreshold函数)
2022/04/04 Python