JS开发HTML5游戏《神奇的六边形》(三)

近期出现一款魔性的消除类HTML5游戏《神奇的六边形》,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏。

(点击图片可进入游戏体验)

因内容太多,为方便大家阅读,所以分成部分来讲解。

本文为第三部分,主要包括:

11.显示出3个形状

12.形状的拖放处理

13.形状放入棋盘的实现

14.界面管理

15.消除行

若要一次性查看所有文档,也可点击这里

 

十一. 显示出3个形状

1. 在Scripts/ui创建文件:Pool.js,绘制3个形状。

var s = qc.Serializer;

 /**
  * 3个形状的绘制
  */
 var Pool = qc.defineBehaviour('qc.tetris.Pool', qc.Behaviour, function() {
     var self = this;

     /**
      * 形状的预置
      */ 
     self.blocksPrefab = null;

     /**
      * 记录下面3个形状的实例
      */
     self.shapes = [];
 }, {
     blocksPrefab: s.PREFAB
 });

 /**
  * 初始化处理
  */
 Pool.prototype.awake = function() {
     var self = this;
     self.redraw();
 };

 /**
  * 绘制3个形状
  */
 Pool.prototype.redraw = function() {
     var self = this;

     // 先干掉旧的形状数据
     for (var i = 0; i < self.shapes.length; i++) {
         self.shapes[i].destroy();
     }
     self.shapes = [];

     // 创建3个新的形状
     for (i = 0; i < 3; i++) {
         self.add(i);
     }
     self.resize();
 };

 /**
  * 调整位置
  */
 Pool.prototype.resize = function() {
     var self = this, o = self.gameObject;

     // 计算X方向的偏移
     var offset = o.width * (0.5 - 0.165);
     for (var i = 0; i < 3; i++) {
         var child = self.shapes[i];
         if (!child) return;
         child.anchoredX = offset * (i - 1);
         child.anchoredY = 0;
     }
 };

 /**
  * 添加一个形状
  */
 Pool.prototype.add = function(index) {
     var self = this;

     var o = self.game.add.clone(self.blocksPrefab, self.gameObject);
     var c = o.getScript('qc.tetris.BlocksUI');
     c.data = qc.Tetris.Shapes.pool[index];
     self.shapes[index] = o;
 };

 /**
  * 删除一个形状
  */
 Pool.prototype.remove = function(index) {
     var o = this.shapes[index];
     o.destroyImmediately();
     this.shapes.splice(index, 1);
 };


整个代码逻辑比较简单,根据3个形状的数据进行绘制。请参考注释进行理解。

 

2. 将此脚本挂载到UIRoot/pool节点,关联blocksPrefab属性:

3. 运行测试下效果,3个形状正确显示了:

 

十二. 形状的拖放处理

形状在被按下时,需要变大,如果是手机上需要向上做一定的位置偏移。拖拽时形状应该跟着鼠标或手指进行移动。

修改脚本Scripts/ui/BlocksUI.js,添加如下代码:

1. 修改reset函数,增加放大区块的逻辑:

BlocksUI.prototype.reset = function(fixToBoard) {
     var self = this, o = self.gameObject;
     for (var pos in self._blocks) {
         var p = qc.Tetris.readPos(pos);
         var pt = qc.Tetris.board.toWorld(p, fixToBoard ? qc.Tetris.BLOCK_H : qc.Tetris.POOL_DISTANCE_NORMAL);
         var block = self._blocks[pos];
         block.anchoredX = pt.x;
         block.anchoredY = pt.y;

         var scale = fixToBoard ? 1.13 : 1;
         block.find('shadow').scaleX = scale;
         block.find('shadow').scaleY = scale;
         block.find('block').scaleX = scale;
         block.find('block').scaleY = scale;
     }
 };


2. 添加按下的逻辑处理,放大区块:

 /**
  * 鼠标按下:放大区块
  */
 BlocksUI.prototype.onDown = function(e) {
     var self = this, o = self.gameObject;
     self.drop = false;
     self.reset(true);

     // 在手机下,需要往上做点偏移
     o.y -= self.offsetY;
 };


  • drop标记当前区块是否被放到棋盘了,刚开始按下清理下环境
  • 按下时需要向上做偏移offsetY

3. 添加鼠标松开或触摸结束的处理,还原区块的位置和大小: 

/**
  * 鼠标松开:重置区块大小
  */
 BlocksUI.prototype.onUp = function() {
     var self = this;
     self.reset();
 };

4. 添加开始拖拽的处理:

/**
  * 拖拽开始
  */
 BlocksUI.prototype.onDragStart = function(e) {
     var self = this;
     self.drop = false;
     self.drag = true;
     self.lastPos = '';
     self.game.input.nativeMode = true;
     self.reset(true);

     self.game.log.trace('Start drag:{0}', self.index);

     // 复制出可放入标记
     var ob = self.flagBlocks = self.game.add.clone(self.gameObject, qc.Tetris.boardUI.gameObject);
     ob.children.forEach(function(block) {
         block.find('shadow').visible = false;
         var b = block.find('block');
         b.width = qc.Tetris.BLOCK_W;
         b.height = qc.Tetris.BLOCK_H;
         b.scaleX = 1;
         b.scaleY = 1;
         b.frame = 'dark' + b.frame;
     });
     ob.scaleX = 1;
     ob.scaleY = 1;
     ob.interactive = false;
     self.hideFlag();
 };


  • 初始时,标记正在拖拽(drag = true),并且没有被放下(drop = false)
  • 当拖拽到棋盘时,需要实时指示是否可以放下本形状。拖拽开始先清理下最近一次检测的逻辑坐标点(last = '')
  • 设置输入模式nativeMode = true。确保输入事件能被实时处理(默认情况下延后一帧处理,运行效率比较高),本游戏对拖拽的实时响应比较重要。
  • 拖拽开始时,放大并偏移形状(和鼠标按下的逻辑一样)
  • 后续的逻辑:另外复制出本形状,并隐藏掉。这个形状在后续拖拽中,会在棋盘显示出来以指示当前是否可以放入。这个指示的格子图片,使用暗色的图片。

5. 添加拖拽的处理,每帧都会进行调度:

 /**
  * 拖拽中
  */
 BlocksUI.prototype.onDrag = function(e) {
     var self = this,
         o = self.gameObject;
     if (self.drag) {
         // 改变节点的目标位置
         var p = o.getWorldPosition();
         p.x += e.source.deltaX;
         p.y += e.source.deltaY;
         var lp = o.parent.toLocal(p);
         o.x = lp.x;
         o.y = lp.y;

         // 计算当前对应棋盘中心点的偏移
         var board = qc.Tetris.boardUI.gameObject;
         p = board.toLocal(p);
         p.y += board.height * 0.5;

         // 反算出对应的归一化坐标
         var xy = qc.Tetris.board.toLocal(p);
         var x = Math.round(xy.x),
             y = Math.round(xy.y),
             pos = qc.Tetris.makePos(x, y);
         if (self.lastPos !== pos) {
             self.lastPos = pos;
             if (qc.Tetris.board.data[pos] &&
                 qc.Tetris.board.checkPutIn(pos, self.data.list)) {
                 self.showFlag(pos);
             }
             else {
                 self.hideFlag();
             }
         }
     }
 };

· 在拖拽的事件e中,会指明本帧到上一帧的移动偏移量(屏幕坐标),本形状加上屏幕坐标偏移,这样就移动起来了

· 然后计算本形状的中心点,对应到棋盘的逻辑坐标。并检查目标是否可以放入,如果可以就需要显示指示

· 最近一次检测的逻辑坐标需要记录下来,防止每帧都对同一逻辑坐标检查是否可以放入(白耗CPU)

6. 打开脚本Scripts/logic/Board.js,实现checkPutIn方法:

 Board.prototype.checkPutIn = function(pos, list) {
     var self = this;
     var pt = qc.Tetris.readPos(pos),
         x = pt.x,
         y = pt.y;

     for (var i = 0; i < list.length; i++) {
         var x0 = x + list[i][0],
             y0 = y + list[i][1];

         // 这个点应该是空的
         var block = self.data[qc.Tetris.makePos(x0, y0)];
         if (!block) return false;
         if (block.value !== 0) return false;
     }
     return true;
 };

7. 继续打开Scripts/ui/Blocks.js,继续实现拖拽结束的逻辑:

/**
  * 拖拽结束
  */
 BlocksUI.prototype.onDragEnd = function(e) {
     var self = this,
         o = self.gameObject;
     self.drag = false;

     if (self.flagBlocks.visible && self.lastPos) {
         // 放到这个位置中去
         self.drop = true;
         qc.Tetris.operation.putIn(self.index, self.lastPos, self.data);
     }
     else {
         self.reset();
         o.parent.getScript('qc.tetris.Pool').resize();
     }

     // 显示标记可以干掉了
     self.flagBlocks.destroy();
     delete self.flagBlocks;
 };

 /**
  * 隐藏指示标记
  */
 BlocksUI.prototype.hideFlag = function() {
     this.flagBlocks.visible = false;
 };

 /**
  * 显示指示标记
  */
 BlocksUI.prototype.showFlag = function(pos) {
     this.flagBlocks.visible = true;
     var pt = qc.Tetris.board.data[pos];
     this.flagBlocks.anchoredX = pt.x;
     this.flagBlocks.anchoredY = pt.y;
 };

· 拖拽结束后,需要判定形状是否被放入目标节点

· 如果可以放入,则调用指令:qc.Tetris.operation.putIn(下步骤实现)

· 如果不能放入,则需要将位置和大小等还原

· 最后,指示对象需要被析构

8. 在Scripts/operation创建文件PutIn.js,实现放入形状指令:

/**
  * 请求放入指定格子,如果成功放入返回true,否则返回false
  */
 qc.Tetris.operation.putIn = function(index, pos) {
     // TODO: 逻辑待实现
 };


9. 在Blocks.js中,我们使用到了棋盘对象:qc.Tetris.boardUI.gameObject,但目前这个值(BoardUI)尚未被赋值。
打开BoardUI.js,在构造函数中加入代码赋值:

var BoardUI = qc.defineBehaviour('qc.tetris.BoardUI', qc.Behaviour, function() {
     var self = this;

     // 登记下本对象
     qc.Tetris.boardUI = self;

     /**
      * 棋盘的棋子元素
      */
     self.pieces = {};

     ...


10. 运行测试下,形状可以随意拖拽了,并且可以反弹回原来位置。不过还无法放入(因为PutIn我们还没实现),请继续后面教程。

十三. 形状放入棋盘的实现

处理流程如下图:

打开文件Scripts/operation/PutIn.js,实现上述代码:

/**
     * 请求放入指定格子,如果成功放入返回true,否则返回false
     */
    qc.Tetris.operation.putIn = function(index, pos) {
        var shape = qc.Tetris.Shapes.pool[index],
            board = qc.Tetris.board,
            ui = qc.Tetris.game.ui,
            log = qc.Tetris.game.log;
        log.trace('尝试将({0})放入({1})', index, pos);

        if (!board.checkPutIn(pos, shape.list)) {
            // 禁止放入
            return false;
        }
        log.trace('放入格子:({0})', pos);

        // 更新棋盘信息
        board.putIn(pos, shape.list, shape.value);

        // 计算可以消除的行,并同时消除掉
        var lines = board.getFullLines();
        lines.forEach(function(flag) {
            var children = ui.killLineEffect.find(flag).gameObject.children;
            var pts = [];
            children.forEach(function(child) { pts.push(child.name); })
            board.clearLine(pts);
        });

        // 计算分数明细,并添加之
        var scoreDetail = qc.Tetris.operation.calcScore(lines);
        qc.Tetris.score.current += scoreDetail.total;

        // 替换为新的形状
        qc.Tetris.Shapes.pool.splice(index, 1);
        qc.Tetris.Shapes.pool.push(qc.Tetris.Shapes.random());

        // 重新绘制棋盘
        ui.board.redraw();

        // 行消除与分数增加的动画表现
        if (lines.length > 0) {
            for (var i = 0; i < lines.length; i++) {
                ui.killLineEffect.play(i, lines[i], scoreDetail.lines[i]);
            }
        }
        else {
            ui.board.getScript('qc.tetris.FlyScore').play(pos, scoreDetail.total);
        }

        // 当前分数的动画表现
        ui.currentScore.setScore();

        // 形状飞入的动画表现,并将旧的形状删除掉
        ui.pool.remove(index);
        ui.pool.add(2);
        ui.pool.flyIn(index);

        // 死亡检测
        if (board.die) {
            // 延迟显示死亡界面
            log.trace('Game Over!');
            qc.Tetris.game.timer.add(3000, function() {
                ui.onDie();
            });
        }

        // 放入成功了
        return true;
    };

    /**
     * 计算分数明细
     * total: 总分数
     * lines: [各行的分数]
     */
    qc.Tetris.operation.calcScore = function(lines) {
        var scores = {
            total: 40,
            lines: []
        };
        if (lines.length < 1) return scores;

        // 计算加成
        var append = Math.max(0, lines.length - 1 * 10);

        for (var i = 0; i < lines.length; i++) {
            var flag = lines[i];

            var line = qc.Tetris.game.ui.killLineEffect.find(flag);
            var len = line.gameObject.children.length;
            scores.lines[i] = len * 20 + append * len;
            scores.total += scores.lines[i];

            // 40合并到第一行去做表现
            if (i === 0) {
                scores.lines[i] += 40;
            }
        }

        return scores;
    };


  • calcScore方法为计算分数的逻辑
  • 代码中出现了qc.Tetris.game.ui(即UIManager),在下文中陆续实现
  • 另外,本逻辑中加入了一些动画表现,在下文中也陆续实现之
  • 先大致理解下处理流程,细节可以后续章节中逐一理解

十四. 界面管理

1. 在Scripts/ui新建UIManager.js:

/**
  * 负责管理所有的游戏界面
  */
 var UIManager = qc.defineBehaviour('qc.tetris.UIManager', qc.Behaviour, function() {
     var self = this;
     self.game.ui = self;

     self.runInEditor = true;
 }, {
     bestScoreNode: qc.Serializer.NODE,
     currentScoreNode: qc.Serializer.NODE,
     boardNode: qc.Serializer.NODE,
     poolNode: qc.Serializer.NODE,
     killLineEffectNode: qc.Serializer.NODE,

     uiRoot: qc.Serializer.NODE,
     gameOverPrefab: qc.Serializer.PREFAB
 });

 /**
  * 初始化管理
  */
 UIManager.prototype.awake = function() {
     var self = this;

     /**
      * bestScore: BestScore组件
      */
     if (self.bestScoreNode)
         self.bestScore = self.bestScoreNode.getScript('qc.tetris.BestScore');

     /**
      * currentScore: CurrentScore组件
      */
     if (self.currentScoreNode)
         self.currentScore = self.currentScoreNode.getScript('qc.tetris.CurrentScore');

     /**
      * board: 棋盘绘制组件
      */
     if (self.boardNode)
         self.board = self.boardNode.getScript('qc.tetris.BoardUI');

     /**
      * pool: 3个形状的方块
      */
     if (self.poolNode)
         self.pool = self.poolNode.getScript('qc.tetris.Pool');

     /**
      * killLineEffect: 方块消除的动画组件
      */
     if (self.killLineEffectNode)
         self.killLineEffect = self.killLineEffectNode.getScript('qc.tetris.KillLineEffect');
 };

 /**
  * 游戏重新开始的界面处理
  */
 UIManager.prototype.restart = function() {
     var self = this;

     // 重新生成3个新的形状
     self.pool.redraw();

     // 棋盘重绘制
     self.board.redraw();

     // 重绘当前分数
     self.currentScore.setScore();
 };

 /**
  * 死亡的处理
  */
 UIManager.prototype.onDie = function() {
     // 显示失败页面
     this.game.add.clone(this.gameOverPrefab, this.uiRoot);
 };


  • UIManager引用了几个界面逻辑,其中KillLineEffect脚本下章节再实现
  • 同时,加入了死亡处理接口、重新开始游戏接口,具体的逻辑在后续章节中逐一实现

2. 将脚本挂载到UIRoot,并关联各属性:

部分属性先留空

 

十五. 消除行

以下的行是可以被消除的:

  

逻辑实现

1. 打开Scripts/logic/board.js,将上述3类型的行建立数据结构:

var Board = qc.Tetris.Board = function() {
     // 省略一堆代码
     ...

     // 左斜的9条线,指明起始点坐标
     self.xyLines = [
         [0, -4],
         [1, -4],
         [2, -4],
         [3, -4],
         [4, -4],

         [4, -3],
         [4, -2],
         [4, -1],
         [4, 0]
     ];

     // 横向9条线,指明起始点坐标和长度
     self.yLines = [
         [0, -4, 5],
         [-1, -3, 6],
         [-2, -2, 7],
         [-3, -1, 8],
         [-4, 0, 9],
         [-4, 1, 8],
         [-4, 2, 7],
         [-4, 3, 6],
         [-4, 4, 5]
     ];

     // 右斜9条线,指明起始点坐标和长度
     self.xLines = [
         [-4, 0, 5],
         [-3, -1, 6],
         [-2, -2, 7],
         [-1, -3, 8],
         [0, -4, 9],
         [1, -4, 8],
         [2, -4, 7],
         [3, -4, 6],
         [4, -4, 5]
     ];
 };

2. 实现putIn接口:

Board.prototype.putIn = function(pos, list, value) {
     var self = this;
     var pt = qc.Tetris.readPos(pos),
         x = pt.x,
         y = pt.y;

     for (var i = 0; i < list.length; i++) {
         var x0 = x + list[i][0],
             y0 = y + list[i][1];

         // 这个点应该是空的
         var block = self.data[qc.Tetris.makePos(x0, y0)];
         block.value = value;
     }
 };


3. 实现clearLine接口,干掉一行数据:

// 干掉一行
 Board.prototype.clearLine = function(pts) {
     var self = this;
     pts.forEach(function(pos) {
         self.data[pos].value = 0;
     });
 };


4. 实现getFullLines接口,将所有可以消除的行返回:

}

     // 右斜9条线
     var pts = self.xLines;
     for (var i = 0; i < pts.length; i++) {
         var start = pts[i], end = [start[0], start[1] + start[2] - 1];
         var ok = true;
         for (var x = start[0], y = start[1]; y <= end[1];) {
             var pos = qc.Tetris.makePos(x, y);
             if (self.data[pos].value === 0) {
                 // 不符合,不能消除
                 ok = false; break;
             }

             // 下一个点
             y++;
         }
         if (ok) {
             // 这条线可以消除,添加进来
             lines.push('x' + qc.Tetris.makePos(start[0], start[1]));
         }
     }

     // 左斜的9条线
     var pts = self.xyLines;
     for (var i = 0; i < pts.length; i++) {
         var start = pts[i], end = [start[1], start[0]];
         var ok = true;
         for (var x = start[0], y = start[1]; true;) {
             var pos = qc.Tetris.makePos(x, y);
             if (self.data[pos].value === 0) {
                 // 不符合,不能消除
                 ok = false; break;
             }

             // 下一个点
             if (end[0] > start[0]) {
                 x++, y--;
                 if (x > end[0]) break;
             }
             else {
                 x--, y++;
                 if (x < end[0]) break;
             }
         }
         if (ok) {
             // 这条线可以消除,添加进来
             lines.push('xy' + qc.Tetris.makePos(start[0], start[1]));
         }
     }

     return lines;
 };


界面实现

预先将所有的行创建出来,当行被删除时直接显示出来做动画表现。以下流程中,我们首先创建一个格子的预制,再创建一个行的预置。

1. 在board节点下,创建Image对象,设置属性如下图:

2.将新创建的block节点拖入Assets/prefab目录,创建预制。然后从场景中删除。

3. 在board节点下,创建Node对象,设置属性如下图:

4. 为节点挂载TweenAlpha动画组件,消失时需要淡出:

  • 透明度从1变化到0
  • 耗时0.5秒
  • 变化的曲线是:先平缓的做变化,然后在快速变化为0
  • 图片中from和to值设置反了,请手工设置下from=1,to=0

5. 在Scripts/ui下创建脚本Line.js,控制行的绘制和表现:

/**
  * 消除一行的表现界面
  */
 var LineUI = qc.defineBehaviour('qc.tetris.LineUI', qc.Behaviour, function() {
     var self = this;

     // 描述行的信息
     self.flag = 'xy';
     self.x = 0;
     self.y = 0;
 }, {
     blockPrefab: qc.Serializer.PREFAB
 });

 Object.defineProperties(LineUI.prototype, {
     /**
      * 取得行标记
      */
     key: {
         get: function() {
             return this.flag + qc.Tetris.makePos(this.x, this.y);
         }
     },

     /**
      * 取得本行的格子数量
      */
     count: {
         get: function() {
             return this.gameObject.children.length;
         }
     }
 });

 /**
  * 初始化行
  */
 LineUI.prototype.init = function(flag, start, end) {
     var self = this;
     self.flag = flag;
     self.x = start[0];
     self.y = start[1];

     // 创建一个格子
     var createBlock = function(pos) {
         var block = self.game.add.clone(self.blockPrefab, self.gameObject);
         block.frame = 'white.png';
         block.anchoredX = qc.Tetris.board.data[pos].x;
         block.anchoredY = qc.Tetris.board.data[pos].y;
         block.name = pos;
         return block;
     };

     switch (flag) {
     case 'xy':
         for (var x = self.x, y = self.y; true;) {
             createBlock(qc.Tetris.makePos(x, y));

             // 下一个点
             if (end[0] > start[0]) {
                 x++, y--;
                 if (x > end[0]) break;
             }
             else {
                 x--, y++;
                 if (x < end[0]) break;
             }
         }
         break;

     case 'y':
         for (var x = start[0], y = start[1]; x <= end[0];) {
             createBlock(qc.Tetris.makePos(x, y));
             x++;
         }
         break;

     case 'x':
         for (var x = start[0], y = start[1]; y <= end[1];) {
             createBlock(qc.Tetris.makePos(x, y));
             y++;
         }
     }

     // 初始时隐藏掉
     self.gameObject.name = self.key;
     self.gameObject.visible = false;
 };

 /**
  * 播放消失的动画
  */
 LineUI.prototype.playDisappear = function(index) {
     var self = this,
         o = self.gameObject,
         ta = self.getScript('qc.TweenAlpha');

     o.visible = true;
     o.alpha = 1;

     ta.delay = 0;
     ta.resetToBeginning();
     ta.onFinished.addOnce(function() {
         // 隐藏掉
         o.visible = false;
     });
     ta.playForward();
 };


  • flag和x、y属性描述了行的信息(左斜行、水平行还是右斜行,起始点的坐标)

6. 将此脚本挂载到Line节点,并设置blockPrefab为第一步骤创建的格子预置:

7. 将line拖进Assets/prefab目录,创建预制。然后从场景中删除。

8. 在Scripts/ui创建脚本KillLineEffect.js,处理行消失表现的逻辑

/**
  * 行消除的动画表现
  */
 var KillLineEffect = qc.defineBehaviour('qc.tetris.KillLineEffect', qc.Behaviour, function() {
     var self = this;

     /**
      * 所有的行
      */
     self.lines = {};

     /**
      * 两行之间的播放延迟
      */
     self.delay = 300;
 }, {
     delay: qc.Serializer.NUMBER,
     linePrefab: qc.Serializer.PREFAB
 });

 /**
  * 初始化:将用于表现的行全部创建出来放着
  */
 KillLineEffect.prototype.awake = function() {
     var self = this;

     // 创建用于消除表现的格子行
     var createLine = function(flag, start, end) {
         var ob = self.game.add.clone(self.linePrefab, self.gameObject);
         var line = ob.getScript('qc.tetris.LineUI');
         line.init(flag, start, end);
         self.lines[line.key] = line;
     };
     var pts = qc.Tetris.board.xyLines;
     for (var i = 0; i < pts.length; i++) {
         var start = pts[i], end = [start[1], start[0]];
         createLine('xy', start, end);
     }

     var pts = qc.Tetris.board.yLines;
     for (var i = 0; i < pts.length; i++) {
         var start = pts[i], end = [start[0] + start[2] - 1, start[1]];
         createLine('y', start, end);

     }
     var pts = qc.Tetris.board.xLines;
     for (var i = 0; i < pts.length; i++) {
         var start = pts[i], end = [start[0], start[1] + start[2] - 1];
         createLine('x', start, end);
     }
 };

 KillLineEffect.prototype.find = function(flag) {
     return this.lines[flag];
 };

 KillLineEffect.prototype.play = function(index, flag, score) {
     var self = this;
     var line = self.find(flag);
     var delay = index * self.delay;

     var playFunc = function() {
         // 冒出分数
         var children = line.gameObject.children;
         var pos = children[Math.round(children.length/2) - 1].name;
         self.getScript('qc.tetris.FlyScore').play(pos, score);

         // 消失动画
         line.playDisappear();
     };
     if (delay <= 0) {
         playFunc();
     }
     else {
         self.game.timer.add(delay, playFunc);
     }
 };

· 在脚本初始化时,将所有行的数据构建出来,并隐藏掉

· delay表示在多行消失时,其动画的间隔时间

· 在动画表现时,有分数表现,FlyScore下一章再补充 

9. 将KillLineEffect挂载到board节点(棋盘),并设置linePrefab:

10. 运行工程,就可以看到这些“行”了:

11. 选中UIRoot节点,设置UIManager的Kill Line Effect Node属性(board节点,因为board挂载了KillLineEffect脚本):

 

 

上一篇:JS开发HTML5游戏《神奇的六边形》(二) 

下一篇:JS开发HTML5游戏《神奇的六边形》(四)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
青瓷引擎是一套开源免费的JavaScript游戏引擎类库,其基于开源免费的Phaser游戏引擎,并提供了一套完全基于浏览器的跨平台集成式HTML5游戏编辑器。 采用青瓷引擎,开发HTML5游戏和传统Web网页开发一样,使用任何你喜欢的编辑器,使用任何你喜欢的浏览器,利用JavaScript语言和所有先进的Web开发工具,让青瓷引擎处理底层技术的复杂性,你只需要关注最重要的事情:做游戏! 青瓷引擎技术堆栈 青瓷引擎由部分组成:QICI Core、QICI Widget和QICI Editor QICI Core:一套JavaScript游戏引擎类库(qc-core.js),基于开源免费的Phaser游戏引擎。 QICI Widget:一套JavaScript图形组件库(qc-widget.js),为编辑器提供丰富强大的通用组件。 QICI Editor:一套基于浏览器的跨平台集成式游戏编辑器,包含基于Node.JS的后台服务。 QICI Core可用于编程方式开发HTML5游戏,无需QICI Editor的支持。但对于界面布局稍微有点复杂度的游戏,如果没有QICI Editor这样所见即所得的可视化开发工具,很难进行快速开发和维护,采用QICI Editor美术和策划甚至都可以参与帮助游戏界面的构建。QICI Widget主要内部使用,为QICI Editor提供基础通用组件支持。 QICI Core是基于JavaScript的游戏类库,QICI Widget是基于JavaScript的图形组件库,QICI Editor采用Node.JS进行资源文件IO处理,所以可以说青瓷引擎是全栈式的JavaScript游戏引擎。青瓷引擎特点 游戏无需浏览器安装额外插件,适应性更广,更利于传播 重新定义了HTML5游戏开发工作流,开发、调试尽在浏览器内 面向组件式编程,支持组件热拔插,方便扩展维护 强大的可视化编辑功能,让设计不再是凭空想象 先进的UI界面布局规则,使得屏幕适配更加简单 为国内手机浏览器进行了优化,减少非标准适配的烦恼 高效的渲染底层,自适应WebGl和Canvas两种模式 丰富的底层核心功能,涵盖绝大部分游戏开发需求 不断丰富的插件库,让游戏开发更加便捷、简单青瓷引擎功能 基于浏览器的编辑器 所见即所得的实时调试功能 网络资源管理,支持预加载、动态加载 时间调度系统,可控制帧率,游戏速度等 自适应Web Audio和Audio Tag,适配性更高的声音管理功能 提供了表格、拉条、滚动视图等丰富的界面控件 提供了游戏与HTML元素混合处理模式 提供基于Rect Tranform的UI布局套件 支持WebFont和BitmapFont等字体系统 优化文字对视网膜设备的适配 整合图集打包,帧动画编辑功能 高性能骨骼动画渲染 支持多种Filter着色器渲染 支持Excel数据导入功能 支持Tilemap的地图导入,并优化刷新性能 强大的可视化Tween曲线动画编辑功能 编辑器菜单和属性面板支持可自定义扩展功能 可扩展插件功能,提供物理、锁屏、微信接口和服务端通讯等内置插件 基于浏览器的编辑器,无需安装任何插件,开发、调试尽在浏览器内。 iPad iPhone 部分游戏示例(点击图片可体验游戏)《神奇六边形》(《神奇六边形》完整教程) 《蛇精病》    《跳跃的方块》 (《跳跃的方块》完整教程)        《2187》   Examples A wide range of source code examples for you to explore. Download all in one zip file.            标签:游戏引擎
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值