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 相关文章推荐
再谈JavaScript线程
Jul 10 Javascript
javascript 中的console.log和弹出窗口alert
Aug 30 Javascript
javascript的几种写法总结
Sep 30 Javascript
JS中作用域和变量提升(hoisting)的深入理解
Oct 31 Javascript
关于Promise 异步编程的实例讲解
Sep 01 Javascript
Vue 2.0学习笔记之Vue中的computed属性
Oct 16 Javascript
js断点调试心得分享(必看篇)
Dec 08 Javascript
前端Vue项目详解--初始化及导航栏
Jun 24 Javascript
JavaScript JSON数据处理全集(小结)
Aug 15 Javascript
使用vue制作滑动标签
Sep 21 Javascript
微信小程序实现吸顶效果
Jan 08 Javascript
js事件机制----捕获与冒泡机制实例分析
May 22 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 class中self,parent,this的区别以及实例介绍
2013/04/24 PHP
php查看网页源代码的方法
2015/03/13 PHP
PHP通过bypass disable functions执行系统命令的方法汇总
2018/05/02 PHP
gearman中任务的优先级和返回状态实例分析
2020/02/27 PHP
script标签属性type与language使用选择
2012/12/02 Javascript
js时间戳格式化成日期格式的多种方法
2013/11/11 Javascript
跟我学习javascript的for循环和for...in循环
2015/11/18 Javascript
js实现C#的StringBuilder效果完整实例
2015/12/22 Javascript
javascript实现可键盘控制的抽奖系统
2016/03/10 Javascript
bootstrap-wysiwyg结合ajax实现图片上传实时刷新功能
2016/05/27 Javascript
AngularJs学习第五篇从Controller控制器谈谈$scope作用域
2016/06/08 Javascript
深入浅出ES6新特性之函数默认参数和箭头函数
2016/08/01 Javascript
js 点击a标签 获取a的自定义属性方法
2016/11/21 Javascript
完美解决spring websocket自动断开连接再创建引发的问题
2017/03/02 Javascript
JavaScript 数据类型详解
2017/03/13 Javascript
百度地图JavascriptApi Marker平滑移动及车头指向行径方向
2017/03/13 Javascript
关于vue.js过渡css类名的理解(推荐)
2017/04/10 Javascript
值得分享和收藏的xmlplus组件学习教程
2017/05/05 Javascript
Vue resource中的GET与POST请求的实例代码
2017/07/21 Javascript
JavaScript编写的网页小游戏,很给力
2017/08/18 Javascript
解决layui 三级联动下拉框更新时回显的问题
2019/09/03 Javascript
[03:08]TI9战队档案 - Vici Gaming
2019/08/20 DOTA
python实现电子词典
2020/04/23 Python
python任务调度实例分析
2015/05/19 Python
分享一下Python 开发者节省时间的10个方法
2015/10/02 Python
简介Python的collections模块中defaultdict类型的用法
2016/07/07 Python
Django 自定义分页器的实现代码
2019/11/24 Python
tensorflow 重置/清除计算图的实现
2020/01/19 Python
localStorage 设置过期时间的方法实现
2018/12/21 HTML / CSS
全球摩托车装备领导者:RevZilla
2017/09/04 全球购物
意大利包包和行李箱销售网站:Bagaglio.it
2021/03/02 全球购物
学生个人自我鉴定范文
2014/03/28 职场文书
小学班主任寄语大全
2014/04/04 职场文书
广告艺术设计专业自荐书
2014/07/08 职场文书
Pandas 数据编码的十种方法
2022/04/20 Python
Apache SkyWalking 监控 MySQL Server 实战解析
2022/09/23 Servers