• 自己写了个H5版本的俄罗斯方块


    在实习公司做完项目后,实在无聊。就用H5写了几个游戏出来玩一下。从简单的做起,就搞了个经典的俄罗斯方块游戏。

    先上效果:

    上面的数字是得分,游戏没有考虑兼容性,只在chrome上测试过,不过大部分现代浏览器还是可以玩的。

    -------------------------------我是分割线--------------------------------------------

    忽然发现这篇草稿放了好久,赶紧补上。。。哈哈

    先上HTML代码

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title>俄罗斯方块</title>
        <style type="text/css">
            body {
                padding: 0;
                margin-top: 40px;
                text-align: center;
            }
            .tetris {
                border: 4px solid black;
            }
        </style>
    </head>
    <body>
        <div id="score"></div>
        <canvas id="tetris" class="tetris" width="240" height="420"></canvas>
        <script type="text/javascript" src="js/tetris2.js"></script>
    </body>
    </html>

    代码就不解释了哈,下面着重说一下js的实现方式(貌似有点挫)。

    首先是加载图片的函数:

    function dlImg(img) { // 返回一个img对象
        var oimg = new Image();
        oimg.src = 'images/' + img;
        return oimg;
    }
    
    /**
     * @param img img对象数组
     * @param sw 屏幕适应的一个比值
     * @param fun 程序入口函数
     */
    function loadAllImg(img, sw, fun) {
        var l = img.length,
            i,h = 0;
        for(i = 0; i < l; i ++){
            oimgarr[img[i]] = dlImg(img[i]);
            oimgarr[img[i]].onload = function () { // 加载是异步的
                this.width = this.width  * sw;
                this.height = this.height  * sw;
                h ++;
                h >= l && fun(); // 所有图片加载成功后调用fun函数
            }
        }
    }

     oimgarr是一个全局变量,暂存着游戏用到的img对象。

    然后就是主函数的代码了,包含俄罗斯方块主要的逻辑功能。

    主函数内有2个对象,Block对象是形状对象,参数type是形状的类型(上、l、L、田、转形状),Blocks是指整个俄罗斯盘(不知道用什么词形容。。。)的对象

    下面是Block对象的实现:

    function Block(type) {
        this.type = type;
        this.i = -1;
        this.j = 6;
        this.speed = 100;
        this.defer = 0;
        switch (this.type) {
            case 1: // l字
                this.outline = [{i: this.i, j: this.j},
                    {i: this.i - 1, j: this.j},
                    {i: this.i - 2, j: this.j},
                    {i: this.i - 3, j: this.j}];
                break;
            case 2: // 上字
                this.outline = [{i: this.i, j: this.j - 1},
                    {i: this.i - 1, j: this.j},
                    {i: this.i, j: this.j},
                    {i: this.i, j: this.j + 1}];
                break;
            case 3: // L字
                this.outline = [{i: this.i - 2, j: this.j - 1},
                    {i: this.i - 1, j: this.j - 1},
                    {i: this.i, j: this.j - 1},
                    {i: this.i, j: this.j}];
                break;
            case 4: // 田字
                this.outline = [{i: this.i - 1, j: this.j - 1},
                    {i: this.i, j: this.j - 1},
                    {i: this.i, j: this.j},
                    {i: this.i - 1, j: this.j}];
                break;
            case 5: // 转字
                this.outline = [{i: this.i - 1, j: this.j - 1},
                    {i: this.i, j: this.j - 1},
                    {i: this.i, j: this.j},
                    {i: this.i + 1, j: this.j}];
                break;
        }
        this.dropBlock = function () { // 下落方块
            var that = this;
            if(this.defer == this.speed) {
                this.outline.map(function (o) {
                    o.i = o.i + 1;
                });
                this.defer = 0;
            }
            else
                this.defer ++;
        };
        this.setSpeed = function () {
            this.speed = 2;
            this.defer = 0;
        };
        this.isReady = function () {
            return this.speed == this.defer;
        }
    }

     下面是Blocks对象的实现方式:

      var Blocks = {
                nullimg: imga['null.png'],
                cellimg: imga['cell.png'],
                pause: false, // 游戏是否处于暂停中
                matrix: new Array(21), // 矩阵,-1表示空,0表示正在移动,1表示已存在
                block: new Block(1), // 默认第一个出现的方块类型为1
                score: 0, // 分数累计
                init: function () {
                    var that = this, code = null;
                    for(var i = 0; i < 21; i ++) { // 初始化矩阵数组
                        this.matrix[i] = new Array(12);
                        for (var j = 0; j < 12; j ++) {
                            this.matrix[i][j] = -1;
                            ctx.drawImage(this.nullimg, j * cell, i * cell, this.nullimg.width, this.nullimg.height);
                        }
                    }
                    document.onkeydown = function (e) { // 按键事件
                        code = e.keyCode || e.which;
                        switch (code){
                            case 37: //
                                that.setSite(-1);
                                break;
                            case 38: //
                                that.rotateBlock();
                                break;
                            case 39: //
                                that.setSite(1);
                                break;
                            case 40: // ↓ 长按加速下滑
                                if(that.block.speed == config.SPEED)
                                    that.block.speedUp(); // 加速
                                break;
                            case 32: // 暂停
                                !that.pause ? that.suspend() : that.start();
                                break;
                            default :
                                return false;
                        }
                    };
                    document.onkeyup = function (e) {
                        if(e.keyCode == 40){ // 松开↓恢复速度
                            that.block.speed = config.SPEED;
                        }
                    }
                },
                start: function () { // 开始游戏
                    var that = this;
                    time = setInterval(function () {
                        console.time('all');
                        that.block.dropBlock(); // 下落方块
                        that.refreshMat(); // 刷新矩阵
                        that.reachBottom(); // 检测是否到达底部或者碰到已有方块
                        console.timeEnd('all');
                    }, config.TIME);
                    this.pause = false;
                },
                suspend: function () { // 暂停
                    this.pause = true;
                    clearInterval(time);
                },
                refreshMat: function () { // 执行一次矩阵刷新
                    var img = null, that = this;
                    that.block.outline.forEach(function (o) { // 将移动前的位置都置为-1
                        if(o.i > 0 && that.matrix[o.i - 1][o.j] != 1 )
                            that.matrix[o.i - 1][o.j] = -1;
                    });
                    that.block.outline.forEach(function (o) { // 刷新移动后的位置
                        if(o.i >= 0)
                            that.matrix[o.i][o.j] = 0;
                    });
                    this.matrix.forEach(function (l, i) { // 重绘矩阵
                        l.forEach(function (m, j) {
                            img = (m == -1 ? that.nullimg : that.cellimg);
                            ctx.drawImage(img, j * cell, i * cell, img.width, img.height);
                        });
                    });
                },
                rotatePoint: function (c, p) { // c点为旋转中心,p为旋转点,一次顺时针旋转90度。返回旋转后的坐标
                    return {j: p.i - c.i + c.j, i: -p.j + c.i + c.j};
                },
                rotateBlock: function () {
                    var that = this, i, o = null, ctr = that.block.outline[1], l = that.block.outline.length;
                    if (that.block.type != 4) { // 田字形无法旋转
                        for (i = 0; i < l; i++) {
                            o = that.rotatePoint(ctr, that.block.outline[i]);
                            if (o.j < 0 || o.j > 11 || o.i > 20) { // 旋转时不可以碰到边界
                                break;
                            }
                            else if (o.i > 0 && o.j >= 0 && o.j <= 20 && Blocks.matrix[o.i][o.j] == 1) { // 旋转时不可以已有方块的点
                                break;
                            }
                        }
                        if (i == 4) {
                            that.block.outline.forEach(function (o, i) {
                                if (o.i >= 0)
                                    that.matrix[o.i][o.j] = -1; // 清空变化前的位置
                                that.block.outline[i] = that.rotatePoint(ctr, o);
                            });
                        }
                    }
                },
                setSite: function (dir) { // 设置左右移动后的位置
                    var i, o, l = this.block.outline.length;
                    for(i = 0; i < l; i ++){
                        o = this.block.outline[i];
                        // 是否碰到已存在的方块,是否碰到左右边界
                        if(o.i >= 0 && ((Blocks.matrix[o.i][o.j + dir] == 1) || (o.j + dir == -1 || o.j + dir == 12))){
                            break; // 一旦发生碰撞,就退出循环,并不执行移动操作
                        }
                    }
                    if(i == l) { // 当count=l时,表明移动操作没有发生碰撞
                        this.block.outline.forEach(function (o) {
                            if (o.i >= 0) {
                                Blocks.matrix[o.i][o.j] = -1; // 将当前位置置为-1
                                o.j = (o.j + dir == -1 || o.j + dir == 12) ? o.j : o.j + dir; // 是否允许移动,允许则将o.j+dir的值赋予o.j
                                Blocks.matrix[o.i][o.j] = 0; // 刷新最新值
                            }
                            else { // 小于0时(在矩阵之外),也需进行左右移动
                                o.j = (o.j + dir == -1 || o.j + dir == 12) ? o.j : o.j + dir;
                            }
                        });
                    }
                },
                reachBottom: function () {
                    var that = this, i, j, o, l = that.block.outline.length;
                    if(that.block.isReady()) { // 当前方块下落帧结束时,然后进行检测是否到达了底部
                        for (j = 0; j < l; j ++) {
                            o = that.block.outline[j];
                            if (o.i >= 0 && (o.i == 20 || that.matrix[o.i + 1][o.j] == 1)) { // 向下移动时发生碰撞
                                break; // 方块到达底部或落在其他方块上,方块停止下落,产生新的方块
                            }
                        }
                        if (j < l) { // 当方块落在底部或其他方块时,进行检测
                            for(i = 0; i < l; i ++) {
                                o = that.block.outline[i];
                                if(o.i >= 0){
                                    that.matrix[o.i][o.j] = 1;  // 方块停止后,修改矩阵数据
                                }
                                else {
                                    that.gameOver(); // 游戏结束
                                    return;
                                }
                            }
                            that.ruinMat(); // 检测是否需要爆破行,如果有则执行爆破操作
                            that.block = new Block(parseInt(Math.random() * 5) + 1);
                        }
                    }
                },
                detectMat: function () { // 检测矩阵,判断是否有连续一行,返回一个数组
                    var count = 0, s,
                        detecta = []; // 需要爆破的行号
                    this.matrix.forEach(function (l, i) {
                        for(s = 0; s < l.length; s ++){
                            if(l[s] == 1) count ++; else break;
                        }
                        count == 12 && detecta.push(i);
                        count = 0;
                    });
                    return detecta.length == 0 ? false : detecta;
                },
                ruinMat: function () { // 爆破连续的一行
                    var dmat = this.detectMat(); // 返回整行都有方块的行号集合
                    if(dmat){
                        this.score = this.score + (dmat.length == 1 ? 100 : dmat.length == 2 ? 250 : dmat.length == 3 ? 450 : 700);
                        score.innerHTML = this.score.toString();
                        dmat.forEach(function (d) {
                            Blocks.matrix.splice(d, 1); // 删掉整行都有方块的行
                            Blocks.matrix.unshift([-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]); // 弥补被删的行
                        });
                    }
                    dmat = null;
                },
                gameOver: function () {
                    clearInterval(time);
                    alert('你挂了');
                }
            };
    View Code

    Block这个对象可以按照下图的坐标来理解:

    可以看到,棋盘左上角是坐标(0,0),每个小方格代表一个坐标点,坐标点的值对应状态值,即-1表示位置空闲,0表示位置正在刷新,1表示位置被占用。

    当然这里还有一个模块没有实现,这个模块就是提示下一个方块的形状,这个有兴趣的同学可以加下哈。

    附上github地址:https://github.com/zquancai/tetris

    PS。。。这篇草稿在草稿箱里躺了快半年,懒到无法自拔(所以描述啥估计有点乱乱的,不过这也没啥难度,纯属自己玩玩而已了)

  • 相关阅读:
    Redis学习-开始
    MongoDb学习1
    Git删除tag
    简单实现Windows服务 TopShelf
    autofac 注入普通服务和WCF服务
    MVC过滤器之 OnActionExcuted
    jquery的$.extend()、$.fn和$.fn.extend()
    quartz_jobs.xml标准配置
    常用工具类11-上传类
    常用工具类10-上传水印类
  • 原文地址:https://www.cnblogs.com/zquancai/p/4305003.html
Copyright © 2020-2023  润新知