HTML5 Canvas编写五彩连珠(3):设计
在看了几篇Canvas相关的文章后,发现前两节的代码实现还是有问题,因为知道的少,所以只能在自己已知的知识上做实现。不过还好,这是一个发现的过程,也是一个纠错和完善的过程。我第一次尝试一边学习一遍写博客,我想这也有助我的学习,可以把知识掌握的牢固些,起码忘的慢一些吧:)。
前两节学习了几个基本绘制的方法,lineTo moveTo和arc,也了解坐标的情况,但写的比较傻,只是单纯的实现。 比如棋盘的起始坐标如果有偏移量,我们还要计算他的具体开始坐标和结束坐标,实际上Canvas有现有的方法提供偏移的功能。 他叫 translate,另外还有缩放scale、旋转rotate,他们都可以用transform代替。所以,在代码方面还会有些调整。不过这个的学习恰巧也让我知道如何实现动画效果。如果多个元素在一个Canvas上,实现动画,必然会需要擦除重绘的情况,如果元素之间有覆盖的情况,擦除就需要多考虑了。当然,简单的办法就是把整个画布根据当然所有元素的位置重新绘制一遍。所以在代码设计方面,需要把不同的元素独立出来,每个元素都有自己的draw方法,并且要依照次序绘制Canvas。
分析一下游戏所需的元素:1、棋盘(地图)2、泡泡 3、等待区域(新的3个即将进入棋盘的泡泡)4、奖励区域(白搭星、超级百搭星、炸弹)5、统计信息 6、按钮
所以在对象的设计方面起码要有几类 棋盘(map)、新泡泡区(ready)、奖励区(awards)、泡泡(bubble)、星星1(star1) 、星星2(star2) 、炸弹(boom)、统计积分(score),还要包括游戏背后的数据(data)。 OK,先这么规划,挨个的去实现。先把map和bubble重写了。
之前把map写成了类,显然是不合适的,因为这个游戏不可能会有多个map,所以直接定义为对象更方便。而泡泡显然需要很多,所以需要写成类比较方便。游戏里面所有对象需要访问的全局变量和常量需要定义在一个game对象里,游戏开始则是调用game.start()的方法。所以先看下game的定义:
var game = { canvas: document.getElementById( "canvas" ), ctx: this .canvas.getContext( "2d" ), cellCount: 9, cellWidth: 30, lineCount: 5, mode: 7, colors: [ "red" , "#039518" , "#ff00dc" , "#ff6a00" , "gray" , "#0094ff" , "#d2ce00" ], over: function () { alert( "GAME OVER" ); }, getRandom: function (max) { return parseInt(Math.random() * 1000000 % (max)); }, }; |
cellCount就是格子的总数,cellwidth是每个格子的宽度,因为不光map里需要这个,所以就定义在了这里,mode 是游戏模式 5是简单 7是困难。
再看下map的代码:
game.map = { startX: 40.5, startY: 60.5, width: game.cellCount * game.cellWidth, height: game.cellCount * game.cellWidth, bubbles: [], init: function () { for ( var i = 0; i < game.cellCount; i++) { var row = []; for ( var j = 0; j < game.cellCount; j++) { row.push( new Bubble(i, j, null )); } this .bubbles.push(row); } }, draw: function () { var ctx = game.ctx; ctx.save(); ctx.translate( this .startX, this .startY); ctx.beginPath(); for ( var i = 0; i <= 9; i++) { var p1 = i * game.cellWidth;; ctx.moveTo(p1, 0); ctx.lineTo(p1, this .height); var p2 = i * game.cellWidth; ctx.moveTo(0, p2); ctx.lineTo( this .width, p2); } ctx.strokeStyle = "#555" ; ctx.stroke(); //绘制子元素(所有在棋盘上的泡) this .bubbles.forEach( function (row) { row.forEach( function (bubble) { bubble.draw(); }); }); ctx.restore(); }, addBubble: function (bubble) { var thisBubble = this .bubbles[bubble.x][bubble.y]; thisBubble.color = bubble.color; }, getBubble: function (x, y) { var thisBubble = this .bubbles[x][y]; if (!thisBubble.color) { return null ; } else { return thisBubble; } } }; |
map的init初始化方法里我先把所有的泡泡部署好了,但是都没有染色,我并没有在ui的背后维护一个数组,因为我觉得泡泡有没有颜色就代表 0,1了,所以就这样也行。
draw方法不再想之前那样把起始坐标计算进去了,而是使用了translate方法,这样就很方便写代码了。
addBubble其实就是染色而已,接收参数是一个泡泡对象,这个对象来自ready区域的泡泡。
Ready区域其实就像俄罗斯方块那样,有三个预备的泡泡即将进入map区域。
game.ready = { startX: 40.5, startY: 20.5, width: game.cellWidth * 3, height: game.cellWidth, bubbles: [], init: function () { this .genrate(); var me = this ; me.flyin(); }, genrate: function () { for ( var i = 0; i < 3; i++) { var color = game.colors[game.getRandom(game.mode)]; this .bubbles.push( new Bubble(i, 0, color)); } }, draw: function () { var ctx = game.ctx; ctx.save(); ctx.translate( this .startX, this .startY); ctx.beginPath(); ctx.strokeStyle = "#555" ; ctx.strokeRect(0, 0, this .width, this .height); ctx.stroke(); //绘制准备的泡 this .bubbles.forEach( function (bubble) { bubble.draw(); }); ctx.restore(); }, }; |
ready.init 初始化3个泡泡,并且把这3个泡泡“飞入”到map里,ready.draw很简单就是绘制一个小矩形和3个泡泡。
哦,对了,我们的泡泡的绘制代码也稍作了修改,现在的样子不是之前的纯色了,有了水晶效果。。。不妨看看:
Bubble.prototype.draw = function () { if (! this .color) { return ; } var ctx = game.ctx; ctx.beginPath(); //console.log("x:" + px + "y:" + py); var gradient = ctx.createRadialGradient( this .px - 5, this .py - 5, 0, this .px, this .py, this .light); gradient.addColorStop(0, "white" ); gradient.addColorStop(1, this .color); ctx.arc( this .px, this .py, 11, 0, Math.PI * 2); ctx.strokeStyle = this .color; ctx.fillStyle = gradient; ctx.fill(); ctx.stroke(); }; |
createRadialGradient方法是画一个放射性的圆,起始在左上角,这样就有个光照效果,还是不错的。 看下效果图吧
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 如何给本地部署的DeepSeek投喂数据,让他更懂你
· 超详细,DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方Dee
· 用 DeepSeek 给对象做个网站,她一定感动坏了
· .NET 8.0 + Linux 香橙派,实现高效的 IoT 数据采集与控制解决方案
· .NET中 泛型 + 依赖注入 的实现与应用