Canvas俄罗斯方块小游戏

本文介绍了一个基于HTML5 Canvas的俄罗斯方块游戏实现,详细展示了游戏的初始化、绘图、移动、旋转等核心功能,并提供了完整的代码示例。
<canvas id="myCanvas" width="800" height="600" style="border: 1px solid #ccc;margin:50px;">您的浏览器不支持 HTML5 canvas 标签。</canvas>

 

//俄罗斯方块小游戏
var myTetris = {
    init: function (id) {
        var _this = this;
        this.canvas = document.getElementById(id.replace("#", ""));
        if (this.canvas.tagName.toLowerCase() !== "canvas") {
            throw new Error("该元素不是canvas标签!")
        }
        this.ctx = this.canvas.getContext("2d");
        this.width = this.canvas.width - 200 || 600;
        this.height = this.canvas.height || 600;
        this.unit = 30;
        this.ctx.font = "18px Microsoft Yahei";
        this.row = Math.floor(this.height / this.unit);
        this.col = Math.floor(this.width / this.unit);
        this.color = {
            strokeColor: "#ddd",
            backgroundColor: "#464741",
            textColor: "#fff",
            fillBlockColor: ["green"]
        };
        this.originRect = [
            [[0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0]],   // 竖一
            [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]],   // 田
            [[0, 0, 0, 0], [1, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]],   // 右T
            [[0, 1, 1, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 0, 0, 0]],   // 左下L
            [[0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]],   // 左上L
            [[0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 1, 0], [0, 0, 0, 0]],   // Z
            [[0, 0, 1, 0], [0, 1, 1, 0], [0, 1, 0, 0], [0, 0, 0, 0]],   // 反Z
            [[0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0], [0, 1, 0, 0]]    // 横一
        ];
        this.ctx.fillStyle = this.color.backgroundColor;
        this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.clearRect(0, 0, this.width, this.height);
        this.reset();
        // 绑定键盘方向键事件
        this.addEventHandler("keydown", window, function (e) {
            var e = e || window.event,
                keyCode = e.keycode || e.which;
            if (_this.keyEvents[keyCode]) {
                // 只有在当前状态是进行中时才会触发键盘事件
                if (_this.status !== 1) return
                _this.keyEvents[keyCode].call(_this);
                e.preventDefault();
            }
        });
        this.addEventHandler("click", document, function (e) {
            _this.ctx.rect(_this.width + 50, 330, 100, 40);
            if (_this.ctx.isPointInPath(e.layerX, e.layerY)) {
                switch (_this.status) {     //0:尚未开始、重新开始;1:正在进行中;2:暂停;
                    case 0:
                        _this.status = 1;
                        _this.text = "暂停";
                        _this.timer = setInterval(function () {
                            _this.move(0, 1)
                        }, _this.speed);
                        break
                    case 1:
                        _this.status = 2;
                        _this.text = "继续";
                        clearInterval(_this.timer);
                        break
                    case 2:
                        _this.status = 1;
                        _this.text = "暂停";
                        _this.timer = setInterval(function () {
                            _this.move(0, 1)
                        }, _this.speed);
                        break
                }
                _this.drawText();
            }
        });
        return this
    },
    reset: function () {    //初始化上次运行产生的变量和数据
        this.text = "开始";
        this.score = 0;
        this.status = 0;
        this.timer = null;
        this.speed = 1000;
        this.nowRect = [];
        this.nextRect = [];
        this.containerArr = [];
        for (var i = 0; i < this.row; i++) {    //清空画布
            var arr = [];
            for (var j = 0; j < this.col; j++) { arr.push(0); };
            this.containerArr.push(arr);
        };
        this.drawMap().drawText().drawNext();
        return this
    },
    drawMap: function () {  //左侧游戏主窗口
        var unit = this.unit;
        this.ctx.fillStyle = this.color.fillBlockColor;
        this.ctx.strokeStyle = this.color.strokeColor;
        this.ctx.clearRect(0, 0, this.width, this.height);
        for (var i = 0; i < this.containerArr.length; i++) {
            for (var j = 0; j < this.containerArr[i].length; j++) {
                this.drawCanvas(this.containerArr[i][j], j * unit, i * unit, unit);
            }
        }
        return this
    },
    drawText: function () { //右侧文字部分
        this.ctx.save();
        this.ctx.fillStyle = this.color.textColor;
        this.ctx.fillText("下一个:", this.width + 30, 50);
        this.ctx.fillStyle = this.color.backgroundColor;
        this.ctx.fillRect(this.width + 30, 250, 120, 40);
        this.ctx.fillStyle = this.color.textColor;
        this.ctx.fillText("分数 :" + this.score, this.width + 30, 270);
        this.ctx.fillStyle = this.color.fillBlockColor[0];
        this.ctx.fillRect(this.width + 50, 330, 100, 40);
        this.ctx.fillStyle = this.color.textColor;
        this.ctx.fillText(this.text, this.width + 84, 356);
        this.ctx.restore();
        return this;
    },
    drawNext: function () {  //右侧下一个方块部分
        var unit = this.unit;
        this.ctx.clearRect(this.width + 40, 80, unit * 4, unit * 4);
        this.ctx.fillStyle = this.color.fillBlockColor;
        this.ctx.strokeStyle = this.color.strokeColor;
        this.nowRect = (this.nextRect.length == 0) ? this.clone(this.getRandomRect(), true) : this.clone(this.nextRect, true);
        this.nowRect.left = 9;
        this.nowRect.top = -4;
        this.nextRect = this.clone(this.getRandomRect(), true);
        for (var i = 0; i < this.nextRect.length; i++) {
            for (var j = 0; j < this.nextRect[i].length; j++) {
                this.drawCanvas(this.nextRect[i][j], (i * unit + this.width + 40), (j * unit + 80), unit);
            }
        }
        return this
    },
    move: function (mx, my) {
        this.changeMapData(0);  //清空上次在画布上所占用位置
        var state = this.ifmove(mx, my);
        if (state) {
            if (state == 1) {
                this.nowRect.top += my;
                this.nowRect.left += mx;
            }
            // 在画布上填充值
            this.changeMapData(1);
        } else {
            // 不能移动,保存已固定在画布的值
            this.changeMapData(1);
            // 设置数组和变量保存满一行时消除
            var clr = [];
            for (i = 0; i < this.containerArr.length; i++) {
                var boo = 0;
                for (j = 0; j < this.containerArr[i].length; j++) {
                    if (this.containerArr[i][j]) { boo++ }
                    if (boo == 20) {
                        clr.unshift(i);
                    }
                }
            }
            // clr = [19, 18, 17, 16];
            // 消除并计算得分
            if (clr.length > 0) {
                this.score += 100 * (clr.length * 2 - 1);
                for (var i = 0; i < clr.length; i++) {
                    this.containerArr.splice(clr[i], 1);
                }
                for (i = 0; i < clr.length; i++) {
                    var arr = [];
                    for (j = 0; j < this.containerArr[0].length; j++) { 
                        arr.push(0); 
                    }
                    this.containerArr.unshift(arr);
                }
            }
            // 游戏结束
            for (i = 0; i < this.containerArr[0].length; i++) {
                if (this.containerArr[0][i]) {
                    clearInterval(this.timer);
                    this.reset();
                    break
                }
            }
            this.drawText().drawNext();
        }
        this.drawMap();
    },
    rotate: function () {   //旋转
        this.changeMapData(0);
        var rotateRect = [];
        var saver = [];
        for (var i = 0; i < this.nowRect.length; i++) { rotateRect.unshift([]); }
        for (i = 0; i < this.nowRect.length; i++) {
            for (var j = 0; j < this.nowRect[i].length; j++) {
                rotateRect[j].unshift(this.nowRect[i][j]);
            }
        }
        rotateRect.left = this.nowRect.left;
        rotateRect.top = this.nowRect.top;
        saver = this.nowRect;
        this.nowRect = rotateRect;
        if (this.ifmove(0, 0) != 1) {
            this.nowRect = saver;
        }
        this.changeMapData(1);
        this.drawMap();
    },
    ifmove: function (mx, my) {
        // 0:往下不可移动,1:可以移动,2:左右不可移动
        for (var i = 0; i < this.nowRect.length; i++) {
            for (var j = 0; j < this.nowRect[i].length; j++) {
                if (this.nowRect[i][j]) {
                    var px = j + this.nowRect.left + mx;
                    var py = i + this.nowRect.top + my;
                    if (px < 0) return 2
                    if (px > 19) return 2
                    if (py < 0) continue
                    if (py > 19) return 0
                    var fr = this.containerArr[py][px];
                    if (fr != 0) {
                        if (mx == 0) return 0
                        if (my == 0) return 2
                    }
                }
            }
        }
        return 1
    },
    keyEvents: {
        37: function () {
            this.move(-1, 0);
        },
        38: function () {
            this.rotate();
        },
        39: function () {
            this.move(1, 0);
        },
        40: function () {
            this.move(0, 1);
        }
    },
    drawCanvas: function (block, x, y, unit) {  //提取绘制俄罗斯方块通用方法
        this.ctx.save();
        if (block) {
            this.ctx.fillRect(x + 1, y + 1, unit - 1, unit - 1);
        }
        this.ctx.strokeRect(x, y, unit, unit);
        this.ctx.restore();
    },
    changeMapData: function (type) {    //提取改变画布对应方块值的通用方法
        for (i = 0; i < this.nowRect.length; i++) {
            for (j = 0; j < this.nowRect[i].length; j++) {
                if (i + this.nowRect.top < 0) continue
                if (this.nowRect[i][j]) {
                    this.containerArr[i + this.nowRect.top][j + this.nowRect.left] = type
                }
            }
        }
    },
    getRandomRect: function (min, max) {    //获取随机数
        var min = 0,
            max = this.originRect.length - 1,
            random = Math.round(Math.random() * (max - min) + min);
        return this.originRect[random]
    },
    addEventHandler: function (event, ele, callback) {  //添加事件绑定
        if (ele.addEventListener) {
            ele.addEventListener(event, callback, false)
        } else {
            ele.attachEvent("on" + event, callback)
        }
        return this
    },
    isObjectType: function (obj) {  //判断Object类型
        var types = ["Object", "Array", "String", "Function", "Boolean", "Number", "Undefined", "Null", "Window", "Date", "RegExp", "HTMLDocument"];
        for (var i = 0; i < types.length; i++) {
            var type = types[i];
            if (Object.prototype.toString.call(obj) === "[object " + type + "]") return type
        }
    },
    clone: function (obj, deep) {   //数组与纯对象的深拷贝
        if (this.isObjectType(obj) === "Object") {
            var target = {},
                deep = (this.isObjectType(deep) === "Boolean") ? deep : false;
            for (var key in obj) {
                target[key] = (deep && this.isObjectType(obj[key]) === "Object") ? this.clone(obj[key], deep) : obj[key];
            }
            return target
        } else if (this.isObjectType(obj) === "Array") {
            return obj.concat()
        } else {
            throw new Error("拷贝对象类型错误!")
        }
    }
}
myTetris.init("#myCanvas");

 

转载于:https://my.oschina.net/u/3288561/blog/1543457

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值