近期出现一款魔性的消除类HTML5游戏《神奇的六边形》,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏。
(点击这里可进入游戏体验)
因内容太多,为方便大家阅读,所以分成八部分来讲解。
本文为第六部分,主要包括:
15. 消除行
以下的行是可以被消除的:
逻辑实现
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接口,将所有可以消除的行返回:
// 取得可以消除的行 Board.prototype.getFullLines = function() { var self = this, lines = []; // 横向9条线 var pts = self.yLines; for (var i = 0; i < pts.length; i++) { var start = pts[i], end = [start[0] + start[2] - 1, start[1]]; var ok = true; for (var x = start[0], y = start[1]; x <= end[0];) { var pos = qc.Tetris.makePos(x, y); if (self.data[pos].value === 0) { // 不符合,不能消除 ok = false; break; } // 下一个点 x++; } if (ok) { // 这条线可以消除,添加进来 lines.push('y' + qc.Tetris.makePos(start[0], start[1])); } } // 右斜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游戏《神奇的六边形》(七)