如何用 JavaScript+Canvas 开发一款超级烧脑小游戏?
作者 | huangjianke
责编 | 伍杏玲
出品 | CSDN(ID:CSDNnews)
【CSDN 编者按】据微信最新数据,微信小游戏累计注册用户量已突破10亿。那么初学者如何开发一款好玩又烧脑的微信小游戏呢?本文作者将详细为大家讲解。
“启逻辑之高妙,因想象而自由。”层叠拼图Plus是一款需要空间想象力和逻辑推理能力完美结合的微信小游戏,偶消奇不消,在简单的游戏规则下却有着无数种可能性,需要你充分发挥想象力去探索,看似简单却具有极大的挑战性和趣味性,Talk is cheap. Show me the code!
层叠拼图Plus微信小游戏采用JavaScript+Canvas实现,没有使用任何游戏引擎,对于初学者来说,也比较容易入门。下面是小游戏页面:
如何解决Canvas绘图模糊?
Canvas 绘图时,会从两个物理像素的中间位置开始绘制并向两边扩散 0.5 个物理像素。当设备像素比为 1 时,一个 1px 的线条实际上占据了两个物理像素(每个像素实际上只占一半),由于不存在 0.5 个像素,所以这两个像素本来不应该被绘制的部分也被绘制了,于是 1 物理像素的线条变成了 2 物理像素,视觉上就造成了模糊
绘图模糊的原因知道了,在微信小游戏里面又该如何解决呢?
可以看到,我们先通过 wx.getSystemInfoSync().pixelRatio 获取设备的像素比ratio,然后将在屏 Canvas 的宽度和高度按照所获取的像素比ratio进行放大,在绘制文字、图片的时候,坐标点 x、y 和所要绘制图形的 width、height均需要按照像素比 ratio 进行缩放,这样我们就可以清晰的在高清屏中绘制想要的文字、图片。
可参考微信官方缩放策略调整
另外,需要注意的是,这里的 canvas 是由 weapp-adapter 预先调用 wx.createCanvas() 创建一个上屏 Canvas,并暴露为一个全局变量 canvas。
如何绘制任意多边形图形?
任意一个多边形图形,是由多个平面坐标点所组成的图形区域。
在游戏画布内,我们以左上角为坐标原点 {x: 0, y: 0} ,一个多边形包含多个单位长度的平面坐标点,如:[{ x: 1, y: 3 }, { x: 5, y: 3 }, { x: 3, y: 5 }] 表示为一个三角形的区域,需要注意的是,x、y 并不是真实的平面坐标值,而是通过屏幕宽度计算出来的单位长度,在画布内的真实坐标值则为 {x: x * itemWidth, y: y * itemWidth} 。
绘制多边形代码实现如下:
使用:
效果如下图:
CanvasRenderingContext2D其他使用方法可参考:CanvasRenderingContext2D API 列表
1 + 1 = 0,「偶消奇不消」的效果如何实现?
1 + 1 = 0,是层叠拼图Plus小游戏玩法的精髓所在。
有经验的同学,也许一眼就发现了,1 + 1 = 0 刚好符合通过异或运算得出的结果。当然,细心的同学也可能已经发现,上文有一句特殊的代码:this.ctx.globalCompositeOperation = 'xor',也正是通过设置 CanvasContext 的 globalCompositeOperation 属性值为 xor 便实现了「偶消奇不消」的神奇效果。
globalCompositeOperation 是指 在绘制新形状时应用的合成操作的类型,其他效果可参考:globalCompositeOperation 示例
如何判断一个点是否在任意多边形内部?
当回转数为 0 时,点在闭合曲线外部。
讲到这里,我们已经知道如何在Canvas画布内绘制出偶消奇不消效果的层叠图形了,接下来我们来看下玩家如何移动选中的图形。我们发现绘制出的图形对象并没有提供点击事件绑定之类的操作,那又如何判断玩家选中了哪个图形呢?这里我们就需要去实现如何判断玩家触摸事件的x,y坐标在哪个多边形图形内部区域,从而判断出玩家选中的是哪一个多边形图形。
判断一个点是否在任意多边形内部有多种方法,比如:
射线法
面积判别法
叉乘判别法
回转数法
...
在层叠拼图Plus小游戏内,采用的是回转数法来判断玩家触摸点是否在多边形内部。回转数是拓扑学中的一个基本概念,具有很重要的性质和用途。当然,展开讨论回转数的概念并不在该文的讨论范围内,我们仅需了解一个概念:当回转数为 0 时,点在闭合曲线外部。
图源:http://www.html-js.com/article/1538
上面面这张图动态演示了回转数的概念:图中红色曲线关于点(人所在位置)的回转数为 2。
对于给定的点和多边形,回转数应该怎么计算呢?
用线段分别连接点和多边形的全部顶点
图源:http://www.html-js.com/article/1538
计算所有点与相邻顶点连线的夹角
图源:http://www.html-js.com/article/1538
计算所有夹角和。注意每个夹角都是有方向的,所以有可能是负值
图源:http://www.html-js.com/article/1538
最后根据角度累加值计算回转数。360°(2π)相当于一次回转。
在使用 JavaScript 实现时,需要注意以下问题:
JavaScript 的数只有 64 位双精度浮点这一种。对于三角函数产生的无理数,浮点数计算不可避免会造成一些误差,因此在最后计算回转数需要做取整操作。
通常情况下,平面直角坐标系内一个角的取值范围是 -π 到 π 这个区间,这也是 JavaScript 三角函数 Math.atan2() 返回值的范围。但 JavaScript 并不能直接计算任意两条线的夹角,我们只能先计算两条线与 x 正轴夹角,再取两者差值。这个差值的结果就有可能超出 -π 到 π 这个区间,因此我们还需要处理差值超出取值区间的情况。
代码实现:
如何判断游戏结果是否正确?
探索的过程固然精彩,而结果却更令我们期待
通过前面的介绍我们可以知道,判断游戏结果是否正确其实就是比对玩家组合图形的 xor 结果与目标图形的 xor 结果。那么如何求多个多边形 xor 的结果呢?polygon-clipping 正是为此而生的。它不仅支持 xor 操作,还有其他的比如:union, intersection, difference 等操作。在层叠拼图Plus游戏内通过 polygon-clipping 又是怎样实现游戏结果判断的呢?
目标图形
多边形平面坐标点集合:
获取 多个多边形 xor 结果:
xor结果:
同理计算出玩家操作图形的xor结果进行比对即可得出答案正确与否。
需要注意的是,获取玩家的 xor 结果并不能直接拿来与目标图形xor 结果进行比较,我们需要将xor 的结果以左上角为参考点将图形平移至原点内,然后再进行比较,如果结果一致,则代表玩家答案正确。
排行榜的展示
有人的地方就有江湖,有江湖的地方就有排行
在看本章节内容之前,建议先浏览一遍排行榜相关的官方文档:好友排行榜、关系链数据,以便对相关内容有个大概的了解。
开放数据域
开放数据域是一个封闭、独立的 JavaScript 作用域。要让代码运行在开放数据域,需要在 game.json 中添加配置项 openDataContext 指定开放数据域的代码目录。添加该配置项表示小游戏启用了开放数据域,这将会导致一些限制。
在游戏内使用 wx.setUserCloudStorage(obj) 对玩家游戏数据进行托管。
在开放数据域内使用 wx.getFriendCloudStorage(obj)拉取当前用户所有同玩好友的托管数据
展示关系链数据
如果想要展示通过关系链 API 获取到的用户数据,如绘制排行榜等业务场景,需要将排行榜绘制到 sharedCanvas 上,再在主域将 sharedCanvas 渲染上屏。
sharedCanvas 是主域和开放数据域都可以访问的一个离屏画布。在开放数据域调用 wx.getSharedCanvas() 将返回 sharedCanvas。
在主域中可以通过开放数据域实例访问 sharedCanvas,通过 drawImage() 方法可以将 sharedCanvas 绘制到上屏画布。
sharedCanvas 本质上也是一个离屏 Canvas,而重设 Canvas 的宽高会清空 Canvas 上的内容。所以要通知开放数据域去重绘 sharedCanvas。
需要注意的是:sharedCanvas 的宽高只能在主域设置,不能在开放数据域中设置。
游戏性能优化
性能优化,简而言之,就是在不影响系统运行正确性的前提下,使之运行地更快,完成特定功能所需的时间更短。
一款能让人心情愉悦的游戏,性能问题必然不能成为绊脚石。那么可以从哪些方面对游戏进行性能优化呢?
离屏 Canvas
在层叠拼图Plus小游戏内,针对需要大量使用且绘图繁复的静态场景,都是使用离屏 Canvas进行绘制的,如首页网格背景、关卡列表、排名列表等。在微信内 wx.createCanvas() 首次调用创建的是显示在屏幕上的画布,之后调用创建的都是离屏画布。初始化时将静态场景绘制完备,需要时直接拷贝离屏Canvas的图像即可。Canvas 绘制本身就是不断的更新帧从而达到动画的效果,通过使用离屏 Canvas,就大大减少了一些静态内容在上屏Canvas的绘制,从而提升了绘制性能。
内存优化
玩家在游戏过程中拖动方块的移动其实就是不断更新多边形图形的坐标信息,然后不断的清空画布再重新绘制,可以想象,这个绘制是非常频繁的,按照普通的做法就需要不断去创建多个新的 Block 对象。针对游戏中需要频繁更新的对象,我们可以通过使用对象池的方法进行优化,对象池维护一个装着空闲对象的池子,如果需要对象的时候,不是直接new,而是从对象池中取出,如果对象池中没有空闲对象,则新建一个空闲对象,层叠拼图Plus小游戏内使用的是官方demo内已经实现的对象池类,实现如下:
垃圾回收
小游戏中,JavaScript 中的每一个 Canvas 或 Image 对象都会有一个客户端层的实际纹理储存,实际纹理储存中存放着 Canvas、Image 的真实纹理,通常会占用相当一部分内存。
每个客户端实际纹理储存的回收时机依赖于 JavaScript 中的 Canvas、Image 对象回收。在 JavaScript 的 Canvas、Image 对象被回收之前,客户端对应的实际纹理储存不会被回收。通过调用 wx.triggerGC() 方法,可以加快触发 JavaScriptCore Garbage Collection(垃圾回收),从而触发 JavaScript 中没有引用的 Canvas、Image 回收,释放对应的实际纹理储存。
但 GC 具体触发时机还要取决于 JavaScriptCore 自身机制,并不能保证调用 wx.triggerGC() 能马上触发回收,层叠拼图Plus小游戏在每局游戏开始或结束都会触发一下,及时回收内存垃圾,以保证最良好的游戏体验。
多线程 Worker
对于游戏来说,每帧 16ms 是极其宝贵的,如果有一些可以异步处理的任务,可以放置于 Worker 中运行,待运行结束后,再把结果返回到主线程。Worker 运行于一个单独的全局上下文与线程中,不能直接调用主线程的方法,Worker 也不具备渲染的能力。Worker与主线程之间的数据传输,双方使用 Worker.postMessage() 来发送数据,Worker.onMessage() 来接收数据,传输的数据并不是直接共享,而是被复制的。
需要注意的是:Worker 最大并发数量限制为 1 个,创建下一个前请用 Worker.terminate() 结束当前 Worker
其他 Worker
相关的内容请参考微信官方文档:多线程 Worker
结语
短短的一篇文章,定不能将层叠拼图Plus小游戏的前前后后讲明白讲透彻。其实最让人心累的还是软著的申请过程,由于各种原因前前后后花了将近三个月的时间,后续可以给大家分享软著申请相关的内容,希望可以帮助到需要的童鞋。
江湖不远,我们游戏里见!
作者简介:huangjianke,高级iOS开发/前端开发工程师,五年开发经验。
需要体验小游戏的童鞋可在微信小程序搜索层叠拼图Plus。
【END】
掌握这些技能,0基础的你也可以很快入门Python爬虫
https://edu.csdn.net/topic/python115?utm_source=csdn_bw
热 文 推 荐
☞Android 10 重磅来袭:支持 5G 与折叠屏、隐私安全全面升级!
这位博士都 50 多岁了,为啥还在敲代码?
☞2019 编程语言排行榜:Java、Python 龙争虎斗!PHP 屹立不倒!
☞2亿日活,日均千万级视频上传,快手推荐系统如何应对技术挑战?
☞Docker容器化部署Python应用
☞给面试官讲明白:一致性Hash的原理和实践
☞预警,CSW的50万枚尘封BTC即将重返市场?
☞她说:行!没事别嫁程序员!
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。
你点的每个“在看”,我都认真当成了喜欢
如何用 JavaScript+Canvas 开发一款超级烧脑小游戏?相关推荐
- 从技术角度谈如何开发一款微信联网小游戏
微信自从有了 "跳一跳" 后微信小游戏就开始迅速火爆起来,为跟上这股潮流,相信很多开发者都想探一探微信小游戏是如何开发的.下面从技术的角度说说一下微信小游戏如何开发的. 附:本文适 ...
- 开发一款C语言小游戏——骑士飞行棋
需求分析 游戏规则和传统的飞行棋一样,支持两人对战 采用100格小型游戏棋盘 游戏规则:对战双方轮流掷骰子控制自己的骑兵前进或后退,在游戏棋盘上设置有关卡 普通 地雷 暂停 时空隧道 幸运轮盘(提供两 ...
- c语言做简单的水果店程序,怎么开发一款生鲜水果小程序?水果店+小程序该如何组合运营?...
门店客流少,租金和人工成本不断上涨,仅靠线下门店获取客户,实在是难以生存. 对于线下的生鲜水果店,竞争也是非常激烈,接下来,我们针对生鲜行业如何搭建小程序和如何运营做实操讲述. 怎么开发一款生鲜水果小 ...
- Javascript开发的金山打字简易小游戏
记得以前读书的时候,由于课前都预习过了,所以听课一般都是为了给老师面子很"耐心"的坐在教室里,发着呆走着神.突然,那天上课讲了几天前预习的课题,采用JS可以识别键盘输入,嘿嘿,好玩 ...
- Android使用SurfaceView开发《捉小猪》小游戏 (一)
先上效果图: 哈哈, 说下实现思路: 我们可以把每一个树桩, 小猪, 车厢都看成是一个Drawable, 这个Drawable里面保存了x, y坐标, 我们的SurfaceView在draw的时候, ...
- 勇闯掘金小游戏为一款多个小游戏的合集游戏,有五个关卡:找掘金、石头剪刀布、寻找藏宝图、打地鼠、抽奖。基于Vue
游戏简介 勇闯掘金小游戏为一款多个小游戏的合集游戏,共有五个关卡,分别为:找掘金.石头剪刀布.寻找藏宝图.打地鼠.抽奖.每个环节20分,满分100分. 完整代码下载地址:勇闯掘金小游戏 快速体验 ht ...
- 云开发版合成大西瓜小游戏微信小程序源码 微信游戏小程序附带流量主功能
这是一款云开发版的合成大西瓜小游戏微信小程序源码,微信游戏小程序源码.该小游戏玩法简单,只需要拖动同样的水果落下合成新品众的水果,最终合成大西瓜,玩法酷似俄罗斯方块,相当于换一种形式的俄罗斯方块,简单 ...
- 11款手机微信小游戏源码特效
html5微信吃苹果游戏源码下载 html5手机淘宝万能时装屋小游戏源码下载 html5 3d拳王游戏制作3D拳击游戏源码下载 html5 3d拼图游戏制作3D魔方游戏源码下载 htm5 3d游戏制作 ...
- 20款Adobe AIR小游戏
20款Adobe AIR小游戏 什么时候有空去包装一下,跑在PlayBook上,我好成天天玩,嘿嘿 http://paranimage.com/20-adobe-air-mini-games/
最新文章
- python如何选择命令_python 解析命令行选项
- Freemarker模板引擎
- 代码传奇丨美女黑客张婉桥的“爱丽丝奇遇记”
- JAVA并发包内容_java并发包
- 机械师开机黑屏自动修复此计算机,机械师F117-V-BISO还原教程
- 支付宝又要改版了:首页顶栏新增了这个模块
- exchange 2010 relay设定
- 机载激光雷达测量技术及工程应用实践_倾斜摄影与激光雷达技术在实景三维测量应用中的比较...
- 浅谈Javascript -- 【嵌套函数及闭包】
- 主流芯片架构即将变天!
- Google Instant Apps VS 微信小程序
- Android签名 (二) 制作签名文件
- AForge Video
- 对称正定矩阵的Cholesky分解
- 最新智商测试html5,2017年最新智商测试题
- IPv4头部结构详解
- Redmi K20 Pro如何编译内核
- 【学习笔记】数组的地址分配及指针数组的使用
- 全站仪外业测绘数据导入CAD图中技巧
- 1 - 基于卡方检验的特征选择
热门文章
- SAS Viya调研概述
- ERROR: No matching distribution found for numpy
- vscode多开远程链接矩池云服务器
- 如何在矩池云GPU云中跑DeepFaceLab
- android intent跳转声音,android intent 跳转
- spss回归分析_SPSS之回归分析
- LSTM及其改进用于视觉任务中
- python显示shape为(224,224,64)的图像?plt.imshow
- 需求分析模板_看完总经理做的公司财务经营分析报告,怪不得人家能拿年薪150万...
- 记录——《C Primer Plus (第五版)》第十一章编程练习第三题