Canvas俄罗斯方块

写在前面

潜水博客园多年,从未写过博客。最近才注册博客,遂将很久前写的俄罗斯方块分享出来。第一次写博客,不喜勿喷。。。

游戏说明

  1. 游戏操作:J向左,L向右,I旋转,K快速下降
  2. 游戏基于HTML canvas开发,请在支持HTML5的浏览器中运行(IE9+, Chrome, Firefox...)
  3. 目前通过CodePen嵌入到页面中存在一个小BUG,请在页面加载完成后5秒左右在开始游戏(codepen的配置问题,已修复)。

数据结构

  1. 将整个游戏区域视为一个n*m的二维数组,数组中的每一个元素对应游戏区域中长为h的小正方形。如arr[3][4]对应左上角顶点坐标为(3*h,4*h)边长为h的正方形。数组中元素的值由0或1构成,0表示对应游戏区域没有方块1表示对应游戏区域已有方块。
  2. 游戏中的图形由若干个小方块构成,每一种图形有4种状态,可通过改变构成图形的小方块的坐标切换图形的状态。每一种图形有一个基点坐标,根据该基点坐标设置对应状态个小方块的坐标。代码如下所示:

  

var Shape = function () { };

    Shape.prototype.Init = function () {
        this.blocks = [];
        var random = parseInt(Math.random() * 100);
        this.status = random % 4;
        this["st"+this.status]();//random status
        this.fillStyle = _config.blockColors[random % _config.blockColors.length];//random color
    }

    Shape.prototype.Down = function () {
        this.x += 1;
        for (var i = 0; i < this.blocks.length; i++) {
            this.blocks[i].x += 1;
        }
    }

    Shape.prototype.Left = function () {
        this.y -= 1;
        for (var i = 0; i < this.blocks.length; i++) {
            this.blocks[i].y -= 1;
        }
    }

    Shape.prototype.Right = function () {
        this.y += 1
        for (var i = 0; i < this.blocks.length; i++) {
            this.blocks[i].y += 1;
        }
    }

    Shape.prototype.st0 = function () { };//abstract method
    Shape.prototype.st1 = function () { };
    Shape.prototype.st2 = function () { };
    Shape.prototype.st3 = function () { };
    Shape.prototype.Rotate = function (i) {//rotate to target status
        var st = (this.status + i+4) % 4;
        this.status = st;
        this["st"+this.status]()

    }
图形抽象类
var Triangle = function (x, y) {
        this.x = x;
        this.y = y;
        this.Init();
    };
    Triangle.InheritFrom(Shape, null, null);
    ShapeFactory.AddShape(Triangle);

    Triangle.prototype.st0 = function () {
        this.blocks[0] = new Block(this.x - 1, this.y, 1);
        this.blocks[1] = new Block(this.x, this.y - 1, 1);
        this.blocks[2] = new Block(this.x, this.y, 1);
        this.blocks[3] = new Block(this.x, this.y + 1, 1);
    }

    Triangle.prototype.st1 = function () {
        this.blocks[0] = new Block(this.x, this.y + 1, 1);
        this.blocks[1] = new Block(this.x - 1, this.y, 1);
        this.blocks[2] = new Block(this.x, this.y, 1);
        this.blocks[3] = new Block(this.x + 1, this.y, 1);
    }

    Triangle.prototype.st2 = function () {
        this.blocks[0] = new Block(this.x + 1, this.y, 1);
        this.blocks[1] = new Block(this.x, this.y + 1, 1);
        this.blocks[2] = new Block(this.x, this.y, 1);
        this.blocks[3] = new Block(this.x, this.y - 1, 1);
    }

    Triangle.prototype.st3 = function () {
        this.blocks[0] = new Block(this.x, this.y - 1, 1);
        this.blocks[1] = new Block(this.x + 1, this.y, 1);
        this.blocks[2] = new Block(this.x, this.y, 1);
        this.blocks[3] = new Block(this.x - 1, this.y, 1);
    }
图形子类(L)

类关系图

游戏中的设计模式

发布订阅模式

用于实现游戏中的事件通知,如得分、游戏结束等。

_events = (function () {//Pub&Sub 
        var topics = {},
         uuid = 0,
         event = function () {
             this.listen = function (topic, callback) {
                 if (typeof topic !== "string" || typeof callback !== "function")
                     return this;
                 if (!topics[topic]) {
                     topics[topic] = [];
                 }
                 callback.uuid = uuid++;
                 topics[topic].push(callback);
                 return this;
             };
             this.trigger = function (src, topic, data) {
                 if (!topics[topic] || topics[topic].length === 0)
                     return this;
                 var callbacks = topics[topic],
                     i = 0,
                     length = callbacks.length;
                 for (; i < length; i++) {
                     callbacks[i].call(src, data);
                 }
                 return this;
             };
             this.remove = function (topic, callback) {
                 if (!topics[topic] || topics[topic].length === 0)
                     return this;
                 var callbacks = topics[topic],
                     i = 0,
                     length = callbacks.length;
                 for (; i < length; i++) {
                     if (callback.uuid === callbacks[i].uuid)
                         callbacks.splice(i, 1);
                 }
                 return this;
             };
         };
        return event;
    })();
事件对象
BlockGame.prototype.ListenNewShapeEvents = function (fn) {
        this.events.listen(_eventEnum.newShape, fn);
        return this;
    }
事件对象应用

工厂模式

用于随机的创建下一个图形。

var ShapeFactory = (function () {//absolute factory
        var ShapeArr = [];
        return {
            CreateShape: function (x, y) {//create a random shape
                var random = Math.floor(Math.random() * 100);
                return new ShapeArr[random % ShapeArr.length](x, y);
            },
            AddShape: function (shape) {
                ShapeArr.push(shape);
            }
        };
    })();
工厂模式

游戏效果

See the Pen JGodp by victor zhang (@viczha) on CodePen.

原文地址:https://www.cnblogs.com/viczha/p/3542951.html