近期出现一款魔性的消除类HTML5游戏《神奇的六边形》,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏。
(点击图片可进入游戏体验)
因内容太多,为方便大家阅读,所以分成四部分来讲解。
本文为第二部分,主要包括:
6. 历史最高分显示
7. 当前分数显示
8. 绘制棋盘
9. 形状池设计与实现
10. 形状预制的实现
若要一次性查看所有文档,也可点击这里。
六. 历史最高分显示
对于DOM节点,其实就是个div,可以指定其样式,指定其样式表类名,也可以内嵌html元素。
1. 在Scripts/ui下新建文件:BestScore.js,处理最高分数的显示逻辑,并将此逻辑脚本挂载到UIRoot/best节点上。
/** * 绘制最高分数 */ var BestScore = qc.defineBehaviour('qc.tetris.BestScore', qc.Behaviour, function() { var self = this; self.runInEditor = true; }, { }); /** * 初始化处理 */ BestScore.prototype.awake = function() { var self = this, div = self.gameObject.div; div.className = 'score_best'; self.setScore(qc.Tetris.score.current); }; /** * 更新最新的高分 */ BestScore.prototype.setScore = function(best) { this.gameObject.div.innerHTML = 'Best: ' + best; };
本脚本可以在编辑器下运行(方便在编辑状态下查看效果),首先设置DOM的样式表类名为score_best,然后获取最高分数并显示之。
2. 增加score_best样式表。打开Assets/css/style.css,添加样式表:
.score_best { font-weight: 60; font-size:30px; color: #ffffff; text-align: right; }
3.刷新下页面,查看效果:
4. 注意:更改样式表目前需要刷新页面才能看到效果。
七. 当前分数显示
1. 在Scripts/ui下新建文件:CurrentScore.js,处理当前分的显示逻辑,并将此逻辑脚本挂载到UIRoot/score节点上。
/** * 绘制当前分数 */ var CurrentScore = qc.defineBehaviour('qc.tetris.CurrentScore', qc.Behaviour, function() { var self = this; self.runInEditor = true; }, { }); /** * 初始化处理 */ CurrentScore.prototype.awake = function() { var self = this, div = self.gameObject.div; div.className = 'score_current'; self.setScore(qc.Tetris.score.current); }; /** * 更新最新的分数 */ CurrentScore.prototype.setScore = function(best) { this.gameObject.div.innerHTML = '' + qc.Tetris.score.current; };
本脚本可以在编辑器下运行(方便在编辑状态下查看效果),首先设置DOM的样式表类名为score_current,然后获取当前分数并显示之。
2.增加score_current样式表。打开Assets/css/style.css,添加样式表:
.score_current{ color: #ffffff; font-weight: 100; font-size:50px; text-align: center; }
3.刷新下页面,查看效果
八. 绘制棋盘
DOM的层次
DOM节点可以绘制在最底层(当工程背景设置为非透明时,将不可见),也可以绘制在最上层(在所有精灵、UIImage等节点之上)。
本棋盘放在最底层,因为棋盘上面还需要拖拽形状、显示可放入指示等。方法是:选中board节点,在Inspector中设置Pos=BACK:
开始绘制棋盘
-
棋盘中的每个格子都作为一个div元素,采用绝对定位。
格子可能表现为7种图片形式(空时为灰色,其他为形状的对应颜色),这7种图片通过不同的样式类来区分。 -
打开Assets/css/style.css,加入如下几个样式表:
.board { width: 61px; height: 67px; position: absolute; background-repeat: no-repeat; } .board_gray { background-image: url("../raw/gray.png"); } .board_blue { background-image: url("../raw/blue.png"); } .board_cyan { background-image: url("../raw/cyan.png"); } .board_green { background-image: url("../raw/green.png"); } .board_lightyellow { background-image: url("../raw/lightyellow.png"); } .board_red { background-image: url("../raw/red.png"); } .board_yellow { background-image: url("../raw/yellow.png"); }
这些样式表指明了棋盘格子中的图片
3. 编辑Tetris.js,加入IMAGES常量,指明在不同的值下面格子所使用的className
window.Tetris = qc.Tetris = { // 棋盘的大小(半径) SIZE: 4, // 棋盘中,每个格子的宽度和高度 BLOCK_W: 61, BLOCK_H: 67, // 所有的格子图片 IMAGES: [ 'gray', // 0 'blue', // 1 'cyan', // 2 'green', // 3 'lightyellow', // 4 'red', // 5 'yellow' // 6 ], // 所有的操作指令集合 operation: {} };
4. 在Scripts/ui下创建脚本BoardUI.js,负责棋盘的绘制逻辑:
var s = qc.Serializer; /** * 管理棋盘的数据并绘制棋盘 */ var BoardUI = qc.defineBehaviour('qc.tetris.BoardUI', qc.Behaviour, function() { var self = this; /** * 棋盘的棋子元素 */ self.pieces = {}; // 本脚本在编辑模式下可以运行 self.runInEditor = true; }, { linePrefab: s.PREFAB }); /** * 初始化处理 */ BoardUI.prototype.awake = function() { var self = this; self.reset(); // 立刻重绘制下 self.redraw(); // 设置游戏画布的背景色 self.gameObject.div.parentNode.style.background = '#1F1E1E'; // 缓存图片,防止在图片切换过程中出现卡顿 if (self.game.device.editor) return; qc.Tetris.IMAGES.forEach(function(c) { var div = document.createElement('div'); div.className = 'board board_' + c; div.style.left = '-2000px'; div.style.left = '-2000px'; self.gameObject.div.appendChild(div); }); }; /** * 重绘棋盘 * @private */ BoardUI.prototype.redraw = function() { var self = this; // 绘制背景 self.drawBackground(); }; /** * 绘制棋盘背景 */ BoardUI.prototype.drawBackground = function() { var self = this; for (var pos in self.pieces) { var div = self.pieces[pos]; div.className = 'board board_' + qc.Tetris.IMAGES[qc.Tetris.board.data[pos].value]; } }; /** * 初始化棋盘 */ BoardUI.prototype.reset = function() { var self = this, o = self.gameObject; // 构建棋盘数据 if (o.children.length === 0) { for (var pos in qc.Tetris.board.data) { var info = qc.Tetris.board.data[pos]; var div = self.pieces[pos] = document.createElement('div'); div.className = 'board board_' + qc.Tetris.IMAGES[info.value]; div.style.left = Math.round(info.x + (o.width - qc.Tetris.BLOCK_W) / 2) + 'px'; div.style.top = Math.round(info.y + (o.height - qc.Tetris.BLOCK_H) / 2) + 'px'; o.div.appendChild(div); } } else { o.children.forEach(function(child) { self.pieces[child.name] = child; }); } };
本脚本运行在编辑模式下,这样就可以实时看到棋盘。对本脚本做一些解释:
- 属性pieces存放了下面所有的div元素(显示格子),key为其逻辑坐标
- 初始化时,会将底下的所有格子创建出来,并设置上合适的位置(由于在Board.js中计算坐标时,是以棋盘中心为原点;但格子div是以棋盘的左上角为原点,因此代码中做了换算)
- redraw方法中,以格子的值获取对应的className,对应的设置到格子div就可以了
- 工程设置的背景是为透明的,因此在初始化时,顺便将游戏背景的颜色设置为:1F1E1E(最底层div的background-color属性)
- 另外,初始时我们把所有格子的图片都加载进来(方法是创建个屏幕内不可见的div),这样在className切换过程中,就可以直接从本地缓存读取图片,避免加载图片影响体验
5. 将此脚本挂载到board节点上,保存场景并刷新页面,就可以看到效果了:
九. 形状池设计与实现
1. 本游戏中,总共有23种形状,每种类型的形状,其颜色是不同的。
在Scripts/logic下创建脚本Shapes.js,负责各种形状的配置、抽取等。
var Shapes = qc.Tetris.Shapes = { // 所有可能的形状 tiles: [ // 1个点的 { value: 1, list: [[[0, 0]]] }, { value: 2, list: [ [[1, -1], [0, 0], [1, 0], [0, 1]], [[0, 0], [1, 0], [-1, 1], [0, 1]], [[0, 0], [1, 0], [0, 1], [1, 1]] ] }, { value: 3, list: [ [[0, -1], [0, 0], [0, 1], [0, 2]], [[0, 0], [1, -1], [-1, 1], [-2, 2]], [[-1, 0], [0, 0], [1, 0], [2, 0]] ] }, { value: 4, list: [ [[0, 0], [0, 1], [0, -1], [-1, 0]], [[0, 0], [0, -1], [1, -1], [-1, 1]], [[0, 0], [0, 1], [0, -1], [1, 0]], [[0, 0], [1, 0], [-1, 0], [1, -1]], [[0, 0], [1, 0], [-1, 0], [-1, 1]] ] }, { value: 5, list: [ [[0, 0], [0, 1], [0, -1], [1, -1]], [[0, 0], [1, -1], [-1, 1], [-1, 0]], [[0, 0], [1, -1], [-1, 1], [1, 0]], [[0, 0], [1, 0], [-1, 0], [0, -1]], [[0, 0], [1, 0], [-1, 0], [0, 1]] ] }, { value: 6, list: [ [[0, -1], [-1, 0], [-1, 1], [0, 1]], [[-1, 0], [0, -1], [1, -1], [1, 0]], [[0, -1], [1, -1], [1, 0], [0, 1]], [[-1, 1], [0, 1], [1, 0], [1, -1]], [[-1, 0], [-1, 1], [0, -1], [1, -1]], [[-1, 0], [-1, 1], [0, 1], [1, 0]] ] } ], /** * 重新开始的逻辑 */ restart: function() { qc.Tetris.Shapes.pool = []; for (var i = 0; i < 3; i++) { qc.Tetris.Shapes.pool.push(qc.Tetris.Shapes.random()); } }, /** * 随机抽取一个形状 */ random: function() { // 先抽取分类 var math = qc.Tetris.game.math; var shapes = Shapes.tiles; var shape = shapes[math.random(0, shapes.length - 1)]; // 再抽子类 var list = shape.list[math.random(0, shape.list.length - 1)]; return { color: shape.color, value: shape.value, list: list }; }, /** * 当前的pool数据 */ pool: [] };
代码说明如下:
- value指明了格子应该使用哪个图片
- list包含了多个形状,形状由数组组成,每个元素指明了逻辑坐标
- pool属性存储了当前屏幕上3个形状的数据信息
2. 修改Tetris.js的qc.initGame方法,最后面添加3个形状的初始化逻辑:
qc.initGame = function(game) { // 将游戏实例记录下来,便于访问 Tetris.game = game; // 帧率显示为60帧(满帧) game.time.frameRate = 60; // 初始化分数信息 Tetris.score = new qc.Tetris.Score(); // 构建棋盘对象 Tetris.board = new qc.Tetris.Board(); // 3个形状 qc.Tetris.Shapes.restart(); };
十. 形状预制的实现
本章节我们进行形状的绘制实现,如下图:
1. 在UIRoot/pool节点下,创建一空的Node节点,设置如下图
- 定位在父亲的中心点
- 大小为200*100
- pivot设置为:(0.5, 0.5)
- 本对象需要可以交互(需要被拖放),勾选Interactive。并设置碰撞盒类型为Rectangle(正方形),按图示设置大小(碰撞盒大小会比节点实际大小更大)
2.在Scripts/ui创建文件BlocksUI.js
/** * 绘制一个形状 */ var BlocksUI = qc.defineBehaviour('qc.tetris.BlocksUI', qc.Behaviour, function() { var self = this; // 格子的预置,一个形状下有多个格子 self.blockPrefab = null; // 下属所有的格子 self._blocks = {}; }, { blockPrefab: qc.Serializer.PREFAB }); Object.defineProperties(BlocksUI.prototype, { /** * 关联的数据 */ data: { get: function() { return this._data; }, set: function(v) { this._data = v; this.redraw(); } }, /** * 第几个? */ index: { get: function() { return this.gameObject.parent.getChildIndex(this.gameObject); } } }); /** * 初始化 */ BlocksUI.prototype.awake = function() { // 点击时的偏移量 var self = this; self.offsetY = self.game.device.desktop ? 0 : 50; }; /** * 重新绘制区块 */ BlocksUI.prototype.redraw = function() { var self = this; var frame = qc.Tetris.IMAGES[self.data.value]; self.data.list.forEach(function(pos) { var x = pos[0], y = pos[1]; var block = self.game.add.clone(self.blockPrefab, self.gameObject); block.find('block').frame = frame + '.png'; block.name = x + '_' + y; self._blocks[qc.Tetris.makePos(x, y)] = block; }); self.reset(); }; /** * 重设区块大小和排列下属格子的位置 */ BlocksUI.prototype.reset = function() { 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, qc.Tetris.POOL_DISTANCE_NORMAL); var block = self._blocks[pos]; block.anchoredX = pt.x; block.anchoredY = pt.y; } };
- 本脚本根据形状的数据,动态创建出格子并显示出来
- blockPrefab为格子的预置,后续步骤创建
- 形状的大小会比棋盘中显示小,因此计算格子的屏幕坐标时,指明了常量:qc.Tetris.POOL_DISTANCE_NORMAL
- 在初始化时,设置了非PC模式下需要做偏移为50。(在手机上拖拽时方式被手指完全挡住,从而看不清形状)
3. 修改Tetris.js,加入POOL_DISTANCE_NORMAL配置:
window.Tetris = qc.Tetris = { // 棋盘的大小(半径) SIZE: 4, // 棋盘中,每个格子的宽度和高度 BLOCK_W: 61, BLOCK_H: 67, // 没有点击时,格子之间的距离 POOL_DISTANCE_NORMAL: 45, ...
4.在上述Blocks节点下,创建空的节点Node,名字修改为block2,属性为:
- 相对于父亲居中显示
- pivot=(0.5, 0.5)
5. 在block2节点下,创建Image节点,名字修改为shadow,属性设置如下:
- 相对于父亲居中显示
- pivot=(0.5, 0.5)
- 大小为60*65
- 使用ui图集
6. 在block2节点下,创建Image节点,名字修改为block,属性设置如下:
- 相对于父亲居中显示
- pivot=(0.5, 0.5)
- 大小为40*45
- 使用ui图集
至此,你的场景应该是:
7. 将block2节点拖入到目录Assets/prefab,创建预置。然后将节点从场景中删除。
8. 将BlocksUI.js挂载到Blocks节点,并设置blockPrefab的值为上步骤创建的预制。设置完成后,将此节点拖入Assets/prefab目录,创建预置。
9. 至此,形状的预置创建完毕了
TO BE CONTINUED...
相关文章:
青瓷引擎打造HTML5游戏第一弹——《神奇的六边形》Part 3
青瓷引擎打造HTML5游戏第一弹——《神奇的六边形》Part 1
开源免费的HTML5游戏引擎——青瓷引擎(QICI Engine) 1.0正式版发布了
相关推荐
青瓷引擎是一套开源免费的JavaScript游戏引擎类库,其基于开源免费的Phaser游戏引擎,并提供了一套完全基于浏览器的跨平台集成式HTML5游戏编辑器。 采用青瓷引擎,开发HTML5游戏和传统Web网页开发一样,使用任何...
这篇文章是关于一篇小说《祖传青瓷碗》的阅读理解及其答案。故事主要围绕一位固守传统的老者和他的儿子之间的冲突展开,这个冲突体现在对于新旧事物接受程度的不同上。 文章开头描绘了一位老人在夕阳下的船只上,...
【PHPWind 青瓷模板】是针对PHPWind论坛系统设计的一款主题模板,它以其独特的青瓷风格,为用户提供了优雅、简洁且富有东方韵味的界面体验。PHPWind是一款广泛应用于社区网站搭建的开源软件,它以其高效、稳定、易用...
青瓷游戏 上市招股说明书
2. 上市公司动态:包括完美世界、掌趣科技、祖龙娱乐等多家上市游戏厂商参与了虚幻引擎技术开放日活动,并分享了他们应用虚幻引擎进行游戏开发的经验。这些上市公司的参与显示了这些企业对于技术创新的重视,同时也...
尤其以浙江省西南部的龙泉市烧制的青瓷——龙泉水青最为著名。龙泉水青的烧制技艺源远流长,自古以来就深受人们的喜爱与推崇,它不仅是中国传统陶瓷艺术的瑰宝,也是世界陶瓷文化中的一颗璀璨明珠。 【历史渊源】 ...
青瓷游戏上市招股说明书 港股.pdf
本周的传媒行业周报聚焦了两个关键事件:一是摩尔庄园在游戏排行榜上的表现下滑,二是青瓷游戏宣布计划在香港进行首次公开募股(IPO)。这两个事件不仅揭示了当前游戏行业的竞争态势,也反映了资本市场的动态。 ...
首先,青瓷游戏的港股上市计划是一个值得关注的焦点。青瓷游戏作为国内知名的游戏开发商,以其独特的游戏设计和良好的市场表现赢得了用户的喜爱。此次拟港股上市,预示着公司可能寻求更大的资金支持以推动业务扩张、...
报告概述了2022年中国游戏行业的新变化,特别是聚焦于港股游戏新势力——青瓷游戏的发展情况。报告指出,2021年游戏行业在政策监管日益严格的情况下,面临挑战,但也孕育着新的机遇。短期内,严格的版号审批可能导致...
本周的影视传媒行业发生了两件备受瞩目的大事:一是中国电影股份有限公司(简称“中国电影”)宣布收购CINITY,二是青瓷游戏正式递交了IPO申请。这两项事件对于整个行业的发展具有深远的影响,让我们逐一剖析。 ...
在移动互联网时代,游戏已经成为重要的娱乐形式之一,而国内游戏开发商在产品创新、IP打造和全球化战略上展现出强大的竞争力,不断推动着整个行业向前发展。 总结来看,传媒行业的这三个热点事件展示了短视频、游戏...
传媒行业在近段时间备受关注,根据最新发布的传媒周报,传媒行业有两大重要事件:一是青瓷游戏发布了招股书,计划在港股上市;二是芒果超媒的定增计划获批。青瓷游戏的上市招股说明书揭示了该公司过去几年的经营状况...
7. 青瓷游戏拟香港IPO:青瓷游戏公司已向港交所提交IPO申请,这表明公司正在寻求资本市场的支持来进一步扩大业务和发展。招股书中显示其财务数据和股东架构信息。 8. A股游戏公司产品情况:报告列举了多个A股上市...
2. 有关“快手”、“青瓷游戏”和“完美世界”的数据信息,提供了这些公司当前的股价、每股收益(EPS)、市盈率(PE)以及未来的盈利预测(E)等关键财务数据,以及分析师的评级建议。这些数据有助于投资者了解公司现状...
### 馆藏高丽青瓷的时间及相关知识点 #### 一、高丽青瓷概述 高丽青瓷作为韩国古代陶瓷艺术的杰出代表之一,以其独特的制作工艺、精美的装饰艺术以及深厚的历史文化内涵闻名于世。根据提供的文档资料,我们可以...
影视传媒行业周报:中国电影收购CINITY,青瓷游戏递交IPO申请.pdf
传媒行业周报:摩尔庄园排名下滑,青瓷游戏拟赴港IPO.pdf
青瓷游戏的上市计划和组织升级是互联网传媒行业的一个亮点,而整个行业在2021年下半年面临国内外宏观环境的挑战。尽管如此,数字内容、本地生活服务、电商等领域仍存在投资机会。投资者应关注公司基本面、市场趋势和...
这份"人教小学五年级美术上册珍爱国宝——古代陶瓷艺术"的资料深入浅出地介绍了陶瓷艺术的发展历程、特点以及鉴赏要点,旨在让学生了解并热爱这一国宝级艺术。 陶器与瓷器是陶瓷艺术的两大类别。陶器主要由陶土制成...