继上一次介绍了《神奇的六边形》的完整游戏开发流程后(可点击这里查看),这次将为大家介绍另外一款魔性游戏《跳跃的方块》的完整开发流程。
(点击图片可进入游戏体验)
因内容太多,为方便大家阅读,所以分多次来讲解。
若要一次性查看所有文档,也可点击这里。
接上回(《跳跃的方块》Part 9)
(四)排行榜界面
排行榜榜单元素
排行榜的榜单也是一个TableView,所以我们先建立一个榜单元素的控制脚本:RankItem.js。
1 // define a user behaviour 2 var RankItem = qc.defineBehaviour('qc.JumpingBrick.RankItem', qc.Behaviour, function() { 3 // need this behaviour schedule in editor 4 //this.runInEditor = true; 5 }, { 6 positionImg : qc.Serializer.NODE, 7 positionText : qc.Serializer.NODE, 8 nameText : qc.Serializer.NODE, 9 score : qc.Serializer.NODE, 10 head : qc.Serializer.NODE, 11 headBack : qc.Serializer.NODE, 12 scoreBack : qc.Serializer.NODE 13 }); 14 15 RankItem._position = [ 16 'first.png', 17 'second.png', 18 'third.png' 19 ]; 20 21 RankItem._textTint = [ 22 0xffb16742, 23 0xff2899a7, 24 0xffa5b471, 25 0xff876712 26 ]; 27 28 RankItem._headBack = [ 29 'list_head_org.png', 30 'list_head_blu.png', 31 'list_head_green.png', 32 'list_head_yel.png' 33 ]; 34 35 RankItem._infoBack = [ 36 'list_bak_org.png', 37 'list_bak_blu.png', 38 'list_bak_green.png', 39 'list_bak_yel.png' 40 ]; 41 42 // Awake is called when the script instance is being loaded. 43 RankItem.prototype.awake = function() { 44 45 }; 46 47 RankItem.prototype.revoke = function() { 48 var self = this; 49 if (self.headKey) { 50 // 清理资源 51 self.game.assets.unload(self.headKey); 52 self.headKey = null; 53 } 54 }; 55 56 // Update is called every frame, if the behaviour is enabled. 57 RankItem.prototype.refreshData = function(index, data, cache) { 58 // 更新信息 59 var self = this; 60 self.headBack.frame = RankItem._headBack[index <4 ? (index - 1) : 3]; 61 self.scoreBack.frame = RankItem._infoBack[index <4 ? (index - 1) : 3]; 62 if (index <4) { 63 self.positionImg.visible = true; 64 self.positionImg.frame = RankItem._position[index - 1]; 65 self.positionText.visible = false; 66 self.nameText.stroke = new qc.Color(RankItem._textTint[index - 1]); 67 self.score.stroke = new qc.Color(RankItem._textTint[index - 1]); 68 } 69 else { 70 self.positionImg.visible = false; 71 self.positionText.visible = true; 72 self.positionText.text = index.toString(); 73 self.nameText.stroke = new qc.Color(RankItem._textTint[3]); 74 self.score.stroke = new qc.Color(RankItem._textTint[3]); 75 } 76 77 // 载入头像 78 // 获取64 * 64的头像尺寸 79 if (data.headurl) { 80 if (self.headKey) { 81 // 清理资源 82 self.game.assets.unload(self.headKey); 83 } 84 self.headKey = data.headurl; 85 self.game.assets.loadTexture(self.headKey, data.headurl + '64', function(assets) { 86 self.head.texture = assets; 87 if (cache) { 88 cache.dirty = true; 89 } 90 }); 91 } 92 self.nameText.text = data.name; 93 self.score.text = data.score.toString(); 94 };
创建榜单预制:sort_score.bin。并和脚本进行关联:
其中mask使用Pixel模式,使用自己的本身图片的透明通道作为子节点的透明通道,设置如下:
排行榜界面
- 管理脚本 使用ViewTable来管理榜单节点,需要提供TableViewAdapter来提供数据。创建一个Announcement.js,用来提供数据并管理界面。内容如下:
1 /** 2 * 排行榜界面 3 */ 4 var Announcement = qc.defineBehaviour('qc.JumpingBrick.Announcement', com.qici.extraUI.TableViewAdapter, function() { 5 6 }, { 7 closeButton: qc.Serializer.NODE, 8 showPanel : qc.Serializer.NODE, 9 myDesc : qc.Serializer.NODE, 10 myPosition : qc.Serializer.NODE, 11 myHeadPanel : qc.Serializer.NODE, 12 myHead : qc.Serializer.NODE, 13 myName : qc.Serializer.NODE, 14 myScore : qc.Serializer.NODE 15 }); 16 17 Announcement.prototype.awake = function() { 18 var self = this, 19 data = JumpingBrick.data; 20 self.addListener(self.closeButton.onClick, self.returnToGameOver, self); 21 self.addListener(self.gameObject.onClick, self.returnToGameOver, self); 22 self.addListener(self.showPanel.onClick, function() {}, self); 23 self.addListener(data.onRankUpdate, self.receiveRankData, self); 24 }; 25 26 /** 27 * 返回游戏结算界面 28 */ 29 Announcement.prototype.returnToGameOver = function() { 30 var self = this; 31 if (self.headKey) { 32 self.game.assets.unload(self.headKey); 33 } 34 JumpingBrick.uiManager.switchStateTo(qc.JumpingBrick.UIManager.GameOver); 35 }; 36 37 // 请求排行榜数据 38 Announcement.prototype.updateRank = function() { 39 var data = JumpingBrick.data; 40 data.queryRank(); 41 }; 42 43 /** 44 * 收到排行榜数据 45 */ 46 Announcement.prototype.receiveRankData = function(data) { 47 var self = this; 48 // 更新自己的信息 49 var selfData = data.selfRank; 50 if (!selfData) { 51 self.myPosition.text = '请登录游戏后查看'; 52 self.myHeadPanel.visible = false; 53 self.myName.visible = false; 54 self.myScore.visible = false; 55 self.myDesc.visible = false; 56 } 57 else { 58 self.myPosition.text = selfData.ranking ? selfData.ranking.toString() : '未上榜'; 59 self.myHeadPanel.visible = true; 60 self.myDesc.visible = true; 61 62 // 获取64 * 64的头像尺寸 63 if (selfData.headurl) { 64 if (self.headKey) { 65 self.game.assets.unload(self.headKey); 66 } 67 self.headKey = selfData.headurl; 68 self.game.assets.loadTexture(self.headKey, selfData.headurl + '64', function(assets) { 69 self.myHead.texture = assets; 70 }); 71 } 72 self.myName.text = selfData.name; 73 self.myScore.text = selfData.scorers.toString(); 74 } 75 76 var rankTop = data.rankTop; 77 self.rankTop = rankTop; 78 79 self.dispatchDataChange(); 80 }; 81 82 83 /** 84 * 获取表格大小,x、y同时只能有一个为Infinity 85 */ 86 Announcement.prototype.getTableSize = function() { 87 return { x: 1, y: this.rankTop ? this.rankTop.length : 0 }; 88 }; 89 90 /** 91 * 根据在Table中的点返回对应的单元格 92 * @param {number} x - x轴坐标 93 * @param {number} y - y轴坐标 94 */ 95 Announcement.prototype.findCellWithPos = function(x, y) { 96 return { 97 x: Math.floor(x / 540), 98 y: Math.floor(y / 90) 99 }; 100 }; 101 102 /** 103 * 获取节点的显示位置 104 */ 105 Announcement.prototype.getCellRect = function(col, row) { 106 return new qc.Rectangle(col * 540, row * 90, 540, 90); 107 }; 108 109 /** 110 * 节点处于不可见时,回收节点, 111 * @param {qc.Node} cell - 节点 112 * @param {number} col - 所在列 113 * @param {number} row - 所在行 114 */ 115 Announcement.prototype.revokeCell = function(cell, col, row) { 116 cell.getScript('qc.JumpingBrick.RankItem').revoke(); 117 }; 118 119 /** 120 * 节点处于可见时,创建节点, 121 * @param {qc.Node} cell - 节点 122 * @param {number} col - 所在列 123 * @param {number} row - 所在行 124 */ 125 Announcement.prototype.createCell = function(cell, col, row) { 126 if (this.rankTop) { 127 cell.getScript('qc.JumpingBrick.RankItem').refreshData(row + 1, this.rankTop[row]); 128 } 129 };
- 排行榜根节点 排行榜界面是一个固定大小的窗口,如果还是按游戏的方式以高度为准,可能在有些设备上就会超出屏幕区域。 所以,为了排行榜,需要再建立一个UIRoot,命名为announcement,Manual Type类型为Expand,建议的宽高还是设计使用的(640, 960)。 如图所示:
为了适应屏幕旋转,还是需要为announcement节点添加一个锁屏的组件。如图所示:
- 界面展示及脚本关联 在锁屏组件下开始拼界面,并将Announcement.js加到announcement节点上。如图所示:
其中tableView的脚本挂载scrollView节点上,配置如下:
五)界面管理
基本界面都已经做好,将这些界面节点同UIManager关联起来,设置如图所示:
这样界面部分也就都完成了。
结束语
到这里,所有的游戏功能开发已经全部完成了。总体的开发思路和时间安排是这样:
- 从设计方案出发,提炼配置。
- 优先完成主体玩法,尽早进行玩法迭代。
- 再逐步完善数据处理和界面逻辑。
感谢各位坚持看到最后,《跳跃的方块》到此就分享完了。若对工程示例或引擎的使用有任何问题,请一定告诉我们,我们会不断努力,继续完善。以后还将陆续分享其他好游戏的开发经验,望大家继续关注,谢谢!
其他相关链接
开源免费的HTML5游戏引擎——青瓷引擎(QICI Engine) 1.0正式版发布了!
JS开发HTML5游戏《神奇的六边形》(一)
青瓷引擎之纯Javascript打造HTML5游戏第二弹——《跳跃的方块》Part 1