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

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

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

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

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

参考效果图如下:

详细代码如下:

<!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 实现的一个俄罗斯方块的实例,有兴趣的小伙伴可以参考下,谢谢大家对本站的支持!

    作者:lqh

    %s 个评论

    要回复文章请先登录注册