整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

如何用 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】

上有很多有助于学习CSS的游戏,本文收集了一些非常实用的免费CSS游戏,希望这些游戏可以帮助你再次体验CSS的乐趣!

作者 | Andreas Müller

译者 | 弯月,责编 | 郭芮

出品 | CSDN(ID:CSDNnews)

以下为译文:

我必须承认,我的记性不是很好。特别是我记不住CSS,例如Flexbox布局等。flex容器的属性justify-content可以有12种以上的不同值,其中许多可以与关键字safe或unsafe组合。详细说明可以参照这篇文章《CSS技巧:flexbox的完整指南》(https://css-tricks.com/snippets/css/a-guide-to-flexbox/),这个页面只有两列,高度却超过了2万像素,虽然文章题目表明这是一篇完整的指南,但实际上文中并没有覆盖到所有内容。

最近,我在偶然间发现了一款塔防式的flexbox教学游戏,这款游戏真的是……

等等,你说什么?

你没听错,事实证明,网上的确有很多有助于学习CSS的游戏。我收集了一些非常实用的免费CSS游戏,希望这些游戏也可以帮助你再次体验CSS的乐趣!

Flexbox Defense

上述我提到的就是这款游戏。它涵盖了flex的属性align-items、justify-content、flex-direction、align-self和order,游戏本身总共有12关。特别是最后4关非常有趣,而且难度也很高。

游戏地址:http://www.flexboxdefense.com

代码库:https://github.com/channingallen/tower-defense

作者:Channing Allen

Flexbox Froggy

这也是一款涉及Flexbox的游戏,它涵盖的flex属性更多:align-items、justify-content、align-content、flex-direction、align-self、flex-wrap和flex-flow,而且游戏总共有24关,如果你打通关了,别忘了告诉我啊。

游戏地址:https://flexboxfroggy.com

代码库:https://github.com/thomaspark/flexboxfroggy

作者:Codepip

Grid Garden

这款游戏总共有28关,你可以从中学习CSS网格布局。它涵盖了以下网格属性:grid-column-start、grid-column-end、grid-column、grid-row-start、grid-row-end、grid-row、grid-area、order、grid-template-columns、grid-template-rows以及grid-template。

游戏地址:https://cssgridgarden.com

代码库:https://github.com/thomaspark/gridgarden

作者:Codepip

CSS Diner

这是一款有关各种CSS选择器的小游戏,总共有32关,打通关后你就可以自诩为CSS选择器专家了,而且你会越玩越饿。

游戏地址:http://flukeout.github.io

代码库:https://github.com/flukeout/css-diner

作者:Luke Pacholski

Unfold

这不完全是一款游戏,更像是一个有关CSS 3D变换的交互式演示。你可能会觉得无聊,但请相信我,游戏里面的动画非常燃,而且你肯定会觉得纯CSS不可能做出这样的效果。

游戏地址:https://rupl.github.io/unfold

代码库:https://github.com/rupl/unfold

作者:Chris Ruppel

Roadmap

你需要一定的技巧和速度才能打通关这款游戏,但游戏本身是只用CSS和HTML制作的。它并非直接地讲解了CSS,而且通过研究源代码学习了很多有关clip-path、transform和带有@keyframes的动画知识!请在下方留言,告诉我们你总共尝试了几次才通关,我试了8次!

游戏地址:http://victordarras.fr/cssgame

作者:Victor Darras

Carnival

你需要在8秒内击中所有目标!这是一款很不错的CSS小游戏,使用了复选框和CSS动画。

游戏地址:https://codepen.io/una/pen/NxZaNr

作者:Una Kravets

Tic-Tac-Toe (井字棋游戏)

这是一款经典的小游戏。这款纯CSS的井字棋游戏只有2关,也使用了复选框和CSS动画。

游戏地址:https://codepen.io/alvaromontoro/pen/BexWOw

作者:Alvaro Montoro

Flexbox Zombies

这款游戏带有故事情节,你可以从中学习如何使用Flexbox和弩来打僵尸。这款游戏需要注册。

游戏地址:https://mastery.games/p/flexbox-zombies

价格:179美元

作者:Dave Geddes

Service Workies

在这款冒险游戏中,你可以学习如何避免PWA的陷阱。你可以提高自己的技术力,并与游戏中的角色一起成长。也许你可以试着杀死在可怜的村子里肆虐了几个世纪的凶猛野兽!这款游戏需要注册。

游戏地址:https://serviceworkies.com

价格:179美元

作者:Dave Geddes

Grid Critters

在这款游戏中,你掌握CSS Grid的旅程始于这位神秘的Grid勇士。你的任务是使用强大的Grid工具来拯救外星生物,使其免于灭绝。这款游戏需要注册。

游戏地址:https://gridcritters.com

价格:179美元

作者:Dave Geddes

总结

不论你是初学者还是专家,我都希望你能够在玩游戏的同时又能学习一些有关CSS的知识!另外,你可以在Codepen上找到很多只用HTML和CSS创建的非常很棒的游戏。

你还知道哪些非常有趣的学习CSS的游戏?请在下方留言。

原文:https://dev.to/devmount/8-games-to-learn-css-the-fun-way-4e0f

本文为 CSDN 翻译,转载请注明来源出处。

【END】