当前位置: 首页 > news >正文

网页版俄罗斯方块

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

    本游戏基于HTML 5、JavaScript来完成。主要使用了canvas组件来绘制游戏界面, 使用了Local Storage记录游戏状态。

本网页游戏使用js开发,是学习js的好材料。通过项目来学习js是最好的方法,有趣又高效。代码不多,附在后面,代码上面有详细的注释。

运行效果如下:

151117_mtTU_3071470.png

代码如下:

html布局代码:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title> 疯狂俄罗斯方块 </title>

    <style type="text/css">
        @font-face {
            font-family: tmb;
            src: url("DS-DIGIB.TTF") format("TrueType")
        }

        body > div {
            font-size: 13pt;
            padding-bottom: 8px;
        }

        span {
            font-family: tmb;
            font-size: 20pt;
            color: green;
        }
    </style>
    <script type="text/javascript" src="Tetris.js">
    </script>


</head>
<body>
    <h2>疯狂俄罗斯方块</h2>
    <div style="width: 336px; border: 1px solid black; background: #ff9;">
        &nbsp;
        <div style="float: left;">
            速度:<span id="curSpeedEle"></span>
            当前积分:<span id="curScoreEle"></span>
        </div>
        <div style="float: right">
            最高积分:<span id="maxScoreEle"></span>
        </div>
    </div>

</body>
</html>

js游戏处理逻辑和界面代码:

/*
 * 本游戏基于HTML 5、JavaScript来完成。
 * 本游戏主要使用了canvas组件来绘制游戏界面,
 * 使用了Local Storage记录游戏状态
 *
 */
var TETRIS_ROWS = 20;
var TETRIS_COLS = 14;
var CELL_SIZE = 24;
// 没方块是0
var NO_BLOCK = 0;
var tetris_canvas;
var tetris_ctx;
// 记录当前积分
var curScore = 0;
// 记录当前速度
var curSpeed = 1;
// 记录曾经的最高积分
var maxScore = 0;
var curScoreEle, curSpeedEle, maxScoreEle;
var curTimer;
// 记录当前是否游戏中的旗标
var isPlaying = true;
// 记录正在下掉的四个方块
var currentFall;
// 该数组用于记录底下已经固定下来的方块。
var tetris_status = [];
for (var i = 0; i < TETRIS_ROWS; i++) {
    tetris_status[i] = [];
    for (var j = 0; j < TETRIS_COLS; j++) {
        tetris_status[i][j] = NO_BLOCK;
    }
}
// 定义方块的颜色
colors = ["#fff", "#f00", "#0f0", "#00f"
    , "#c60", "#f0f", "#0ff", "#609"];
// 定义几种可能出现的方块组合
var blockArr = [
    // 代表第一种可能出现的方块组合:Z
    [
        {x: TETRIS_COLS / 2 - 1, y: 0, color: 1},
        {x: TETRIS_COLS / 2, y: 0, color: 1},
        {x: TETRIS_COLS / 2, y: 1, color: 1},
        {x: TETRIS_COLS / 2 + 1, y: 1, color: 1}
    ],
    // 代表第二种可能出现的方块组合:反Z
    [
        {x: TETRIS_COLS / 2 + 1, y: 0, color: 2},
        {x: TETRIS_COLS / 2, y: 0, color: 2},
        {x: TETRIS_COLS / 2, y: 1, color: 2},
        {x: TETRIS_COLS / 2 - 1, y: 1, color: 2}
    ],
    // 代表第三种可能出现的方块组合: 田
    [
        {x: TETRIS_COLS / 2 - 1, y: 0, color: 3},
        {x: TETRIS_COLS / 2, y: 0, color: 3},
        {x: TETRIS_COLS / 2 - 1, y: 1, color: 3},
        {x: TETRIS_COLS / 2, y: 1, color: 3}
    ],
    // 代表第四种可能出现的方块组合:L
    [
        {x: TETRIS_COLS / 2 - 1, y: 0, color: 4},
        {x: TETRIS_COLS / 2 - 1, y: 1, color: 4},
        {x: TETRIS_COLS / 2 - 1, y: 2, color: 4},
        {x: TETRIS_COLS / 2, y: 2, color: 4}
    ],
    // 代表第五种可能出现的方块组合:J
    [
        {x: TETRIS_COLS / 2, y: 0, color: 5},
        {x: TETRIS_COLS / 2, y: 1, color: 5},
        {x: TETRIS_COLS / 2, y: 2, color: 5},
        {x: TETRIS_COLS / 2 - 1, y: 2, color: 5}
    ],
    // 代表第六种可能出现的方块组合 : 条
    [
        {x: TETRIS_COLS / 2, y: 0, color: 6},
        {x: TETRIS_COLS / 2, y: 1, color: 6},
        {x: TETRIS_COLS / 2, y: 2, color: 6},
        {x: TETRIS_COLS / 2, y: 3, color: 6}
    ],
    // 代表第七种可能出现的方块组合 : ┵
    [
        {x: TETRIS_COLS / 2, y: 0, color: 7},
        {x: TETRIS_COLS / 2 - 1, y: 1, color: 7},
        {x: TETRIS_COLS / 2, y: 1, color: 7},
        {x: TETRIS_COLS / 2 + 1, y: 1, color: 7}
    ]
];
// 定义初始化正在下掉的方块
var initBlock = function () {
    var rand = Math.floor(Math.random() * blockArr.length);
    // 随机生成正在下掉的方块
    currentFall = [
        {
            x: blockArr[rand][0].x, y: blockArr[rand][0].y
            , color: blockArr[rand][0].color
        },
        {
            x: blockArr[rand][1].x, y: blockArr[rand][1].y
            , color: blockArr[rand][1].color
        },
        {
            x: blockArr[rand][2].x, y: blockArr[rand][2].y
            , color: blockArr[rand][2].color
        },
        {
            x: blockArr[rand][3].x, y: blockArr[rand][3].y
            , color: blockArr[rand][3].color
        }
    ];
};
// 定义一个创建canvas组件的函数
var createCanvas = function (rows, cols, cellWidth, cellHeight) {
    tetris_canvas = document.createElement("canvas");
    // 设置canvas组件的高度、宽度
    tetris_canvas.width = cols * cellWidth;
    tetris_canvas.height = rows * cellHeight;
    // 设置canvas组件的边框
    tetris_canvas.style.border = "1px solid black";
    // 获取canvas上的绘图API
    tetris_ctx = tetris_canvas.getContext('2d');
    // 开始创建路径
    tetris_ctx.beginPath();
    // 绘制横向网络对应的路径
    for (var i = 1; i < TETRIS_ROWS; i++) {
        tetris_ctx.moveTo(0, i * CELL_SIZE);
        tetris_ctx.lineTo(TETRIS_COLS * CELL_SIZE, i * CELL_SIZE);
    }
    // 绘制竖向网络对应的路径
    for (var i = 1; i < TETRIS_COLS; i++) {
        tetris_ctx.moveTo(i * CELL_SIZE, 0);
        tetris_ctx.lineTo(i * CELL_SIZE, TETRIS_ROWS * CELL_SIZE);
    }
    tetris_ctx.closePath();
    // 设置笔触颜色
    tetris_ctx.strokeStyle = "#aaa";
    // 设置线条粗细
    tetris_ctx.lineWidth = 0.3;
    // 绘制线条
    tetris_ctx.stroke();
};
// 绘制俄罗斯方块的状态
var drawBlock = function () {
    for (var i = 0; i < TETRIS_ROWS; i++) {
        for (var j = 0; j < TETRIS_COLS; j++) {
            // 有方块的地方绘制颜色
            if (tetris_status[i][j] != NO_BLOCK) {
                // 设置填充颜色
                tetris_ctx.fillStyle = colors[tetris_status[i][j]];
                // 绘制矩形
                tetris_ctx.fillRect(j * CELL_SIZE + 1
                    , i * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
            }
            // 没有方块的地方绘制白色
            else {
                // 设置填充颜色
                tetris_ctx.fillStyle = 'white';
                // 绘制矩形
                tetris_ctx.fillRect(j * CELL_SIZE + 1
                    , i * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
            }
        }
    }
};
// 当页面加载完成时,执行该函数里的代码。
window.onload = function () {
    // 创建canvas组件
    createCanvas(TETRIS_ROWS, TETRIS_COLS, CELL_SIZE, CELL_SIZE);
    document.body.appendChild(tetris_canvas);
    curScoreEle = document.getElementById("curScoreEle");
    curSpeedEle = document.getElementById("curSpeedEle");
    maxScoreEle = document.getElementById("maxScoreEle");
    // 读取Local Storage里的tetris_status记录
    var tmpStatus = localStorage.getItem("tetris_status");
    tetris_status = tmpStatus == null ? tetris_status : JSON.parse(tmpStatus);
    // 把方块状态绘制出来
    drawBlock();
    // 读取Local Storage里的curScore记录
    curScore = localStorage.getItem("curScore");
    curScore = curScore == null ? 0 : parseInt(curScore);
    curScoreEle.innerHTML = curScore;
    // 读取Local Storage里的maxScore记录
    maxScore = localStorage.getItem("maxScore");
    maxScore = maxScore == null ? 0 : parseInt(maxScore);
    maxScoreEle.innerHTML = maxScore;
    // 读取Local Storage里的curSpeed记录
    curSpeed = localStorage.getItem("curSpeed");
    curSpeed = curSpeed == null ? 1 : parseInt(curSpeed);
    curSpeedEle.innerHTML = curSpeed;
    // 初始化正在下掉的方块
    initBlock();
    // 控制每隔固定时间执行一次向下”掉“
    curTimer = setInterval("moveDown();", 500 / curSpeed);
};
// 判断是否有一行已满
var lineFull = function () {
    // 依次遍历每一行
    for (var i = 0; i < TETRIS_ROWS; i++) {
        var flag = true;
        // 遍历当前行的每个单元格
        for (var j = 0; j < TETRIS_COLS; j++) {
            if (tetris_status[i][j] == NO_BLOCK) {
                flag = false;
                break;
            }
        }
        // 如果当前行已全部有方块了
        if (flag) {
            // 将当前积分增加100
            curScoreEle.innerHTML = curScore += 100;
            // 记录当前积分
            localStorage.setItem("curScore", curScore);
            // 如果当前积分达到升级极限。
            if (curScore >= curSpeed * curSpeed * 500) {
                curSpeedEle.innerHTML = curSpeed += 1;
                // 使用Local Storage记录curSpeed。
                localStorage.setItem("curSpeed", curSpeed);
                clearInterval(curTimer);
                curTimer = setInterval("moveDown();", 500 / curSpeed);
            }
            // 把当前行的所有方块下移一行。
            for (var k = i; k > 0; k--) {
                for (var l = 0; l < TETRIS_COLS; l++) {
                    tetris_status[k][l] = tetris_status[k - 1][l];
                }
            }
            // 消除方块后,重新绘制一遍方块
            drawBlock();      //②
        }
    }
};
// 控制方块向下掉。
var moveDown = function () {
    // 定义能否下掉的旗标
    var canDown = true;    //①
    // 遍历每个方块,判断是否能向下掉
    for (var i = 0; i < currentFall.length; i++) {
        // 判断是否已经到“最底下”
        if (currentFall[i].y >= TETRIS_ROWS - 1) {
            canDown = false;
            break;
        }
        // 判断下一格是否“有方块”, 如果下一格有方块,不能向下掉
        if (tetris_status[currentFall[i].y + 1][currentFall[i].x] != NO_BLOCK) {
            canDown = false;
            break;
        }
    }
    // 如果能向下“掉”
    if (canDown) {
        // 将下移前的每个方块的背景色涂成白色
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = 'white';
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
        // 遍历每个方块, 控制每个方块的y坐标加1。
        // 也就是控制方块都下掉一格
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            cur.y++;
        }
        // 将下移后的每个方块的背景色涂成该方块的颜色值
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = colors[cur.color];
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
    }
    // 不能向下掉
    else {
        // 遍历每个方块, 把每个方块的值记录到tetris_status数组中
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 如果有方块已经到最上面了,表明输了
            if (cur.y < 2) {
                // 清空Local Storage中的当前积分值、游戏状态、当前速度
                localStorage.removeItem("curScore");
                localStorage.removeItem("tetris_status");
                localStorage.removeItem("curSpeed");
                if (confirm("您已经输了!是否参数排名?")) {
                    // 读取Local Storage里的maxScore记录
                    maxScore = localStorage.getItem("maxScore");
                    maxScore = maxScore == null ? 0 : maxScore;
                    // 如果当前积分大于localStorage中记录的最高积分
                    if (curScore >= maxScore) {
                        // 记录最高积分
                        localStorage.setItem("maxScore", curScore);
                    }
                }
                // 游戏结束
                isPlaying = false;
                // 清除计时器
                clearInterval(curTimer);
                return;
            }
            // 把每个方块当前所在位置赋为当前方块的颜色值
            tetris_status[cur.y][cur.x] = cur.color;
        }
        // 判断是否有“可消除”的行
        lineFull();
        // 使用Local Storage记录俄罗斯方块的游戏状态
        localStorage.setItem("tetris_status", JSON.stringify(tetris_status));
        // 开始一组新的方块。
        initBlock();
    }
};
// 定义左移方块的函数
var moveLeft = function () {
    // 定义能否左移的旗标
    var canLeft = true;
    for (var i = 0; i < currentFall.length; i++) {
        // 如果已经到了最左边,不能左移
        if (currentFall[i].x <= 0) {
            canLeft = false;
            break;
        }
        // 或左边的位置已有方块,不能左移
        if (tetris_status[currentFall[i].y][currentFall[i].x - 1] != NO_BLOCK) {
            canLeft = false;
            break;
        }
    }
    // 如果能左移
    if (canLeft) {
        // 将左移前的每个方块的背景色涂成白色
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = 'white';
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
        // 左移所有正在下掉的方块
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            cur.x--;
        }
        // 将左移后的每个方块的背景色涂成方块对应的颜色
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = colors[cur.color];
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
    }
};
// 定义右移方块的函数
var moveRight = function () {
    // 定义能否右移的旗标
    var canRight = true;
    for (var i = 0; i < currentFall.length; i++) {
        // 如果已到了最右边,不能右移
        if (currentFall[i].x >= TETRIS_COLS - 1) {
            canRight = false;
            break;
        }
        // 如果右边的位置已有方块,不能右移
        if (tetris_status[currentFall[i].y][currentFall[i].x + 1] != NO_BLOCK) {
            canRight = false;
            break;
        }
    }
    // 如果能右移
    if (canRight) {
        // 将右移前的每个方块的背景色涂成白色
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = 'white';
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
        // 右移所有正在下掉的方块
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            cur.x++;
        }
        // 将右移后的每个方块的背景色涂成各方块对应的颜色
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = colors[cur.color];
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
    }
};
// 定义旋转方块的函数
var rotate = function () {
    // 定义记录能否旋转的旗标
    var canRotate = true;
    for (var i = 0; i < currentFall.length; i++) {
        var preX = currentFall[i].x;
        var preY = currentFall[i].y;
        // 始终以第三个方块作为旋转的中心,
        // i == 2时,说明是旋转的中心
        if (i != 2) {
            // 计算方块旋转后的x、y坐标
            var afterRotateX = currentFall[2].x + preY - currentFall[2].y;
            var afterRotateY = currentFall[2].y + currentFall[2].x - preX;
            // 如果旋转后所在位置已有方块,表明不能旋转
            if (tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) {
                canRotate = false;
                break;
            }
            // 如果旋转后的坐标已经超出了最左边边界
            if (afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK) {
                moveRight();
                afterRotateX = currentFall[2].x + preY - currentFall[2].y;
                afterRotateY = currentFall[2].y + currentFall[2].x - preX;
                break;
            }
            if (afterRotateX < 0 || tetris_status[afterRotateY - 1][afterRotateX] != NO_BLOCK) {
                moveRight();
                break;
            }
            // 如果旋转后的坐标已经超出了最右边边界
            if (afterRotateX >= TETRIS_COLS - 1 ||
                tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) {
                moveLeft();
                afterRotateX = currentFall[2].x + preY - currentFall[2].y;
                afterRotateY = currentFall[2].y + currentFall[2].x - preX;
                break;
            }
            if (afterRotateX >= TETRIS_COLS - 1 ||
                tetris_status[afterRotateY][afterRotateX + 1] != NO_BLOCK) {
                moveLeft();
                break;
            }
        }
    }
    // 如果能旋转
    if (canRotate) {
        // 将旋转移前的每个方块的背景色涂成白色
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = 'white';
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
        for (var i = 0; i < currentFall.length; i++) {
            var preX = currentFall[i].x;
            var preY = currentFall[i].y;
            // 始终以第三个方块作为旋转的中心,
            // i == 2时,说明是旋转的中心
            if (i != 2) {
                currentFall[i].x = currentFall[2].x +
                    preY - currentFall[2].y;
                currentFall[i].y = currentFall[2].y +
                    currentFall[2].x - preX;
            }
        }
        // 将旋转后的每个方块的背景色涂成各方块对应的颜色
        for (var i = 0; i < currentFall.length; i++) {
            var cur = currentFall[i];
            // 设置填充颜色
            tetris_ctx.fillStyle = colors[cur.color];
            // 绘制矩形
            tetris_ctx.fillRect(cur.x * CELL_SIZE + 1
                , cur.y * CELL_SIZE + 1, CELL_SIZE - 2, CELL_SIZE - 2);
        }
    }
};
window.focus();
// 为窗口的按键事件绑定事件监听器
window.onkeydown = function (evt) {
    switch (evt.keyCode) {
        // 按下了“向下”箭头
        case 40:
            if (!isPlaying)
                return;
            moveDown();
            break;
        // 按下了“向左”箭头
        case 37:
            if (!isPlaying)
                return;
            moveLeft();
            break;
        // 按下了“向右”箭头
        case 39:
            if (!isPlaying)
                return;
            moveRight();
            break;
        // 按下了“向上”箭头
        case 38:
            if (!isPlaying)
                return;
            rotate();
            break;
    }
};

 

转载于:https://my.oschina.net/hutaishi/blog/997802

相关文章:

  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 测试框架Jest
  • 《伟大的计算原理》一交互系统
  • Maven的配置文件pom.xml
  • Spring boot设置启动监听端口
  • 幂等设计
  • 高度不固定时垂直居中
  • HTML内联元素
  • Java四中控制访问符
  • py xrange
  • JSPWiki安装配置及FCKEditor的集成
  • 1163 访问艺术馆
  • 计划任务 at   batch   cron anacron
  • 区块链技术与应用回顾
  • WiFi万能钥匙张发有:WiFi的安全性是第一位的
  • [译]Python中的类属性与实例属性的区别
  • 《剑指offer》分解让复杂问题更简单
  • 【5+】跨webview多页面 触发事件(二)
  • 【剑指offer】让抽象问题具体化
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • fetch 从初识到应用
  • Iterator 和 for...of 循环
  • JAVA SE 6 GC调优笔记
  • Joomla 2.x, 3.x useful code cheatsheet
  • mongo索引构建
  • Node + FFmpeg 实现Canvas动画导出视频
  • opencv python Meanshift 和 Camshift
  • Redis字符串类型内部编码剖析
  • springboot_database项目介绍
  • swift基础之_对象 实例方法 对象方法。
  • vue:响应原理
  • VuePress 静态网站生成
  • vue的全局变量和全局拦截请求器
  • 理解 C# 泛型接口中的协变与逆变(抗变)
  • 前端之Sass/Scss实战笔记
  • 时间复杂度与空间复杂度分析
  • LevelDB 入门 —— 全面了解 LevelDB 的功能特性
  • ​如何防止网络攻击?
  • #HarmonyOS:基础语法
  • #微信小程序:微信小程序常见的配置传旨
  • #我与Java虚拟机的故事#连载14:挑战高薪面试必看
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (Bean工厂的后处理器入门)学习Spring的第七天
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (动态规划)5. 最长回文子串 java解决
  • (二十五)admin-boot项目之集成消息队列Rabbitmq
  • (附源码)ssm户外用品商城 毕业设计 112346
  • (官网安装) 基于CentOS 7安装MangoDB和MangoDB Shell
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (十一)图像的罗伯特梯度锐化
  • (数据结构)顺序表的定义
  • (转)mysql使用Navicat 导出和导入数据库
  • (转)总结使用Unity 3D优化游戏运行性能的经验
  • (转载)跟我一起学习VIM - The Life Changing Editor