[前端工坊] 微信小游戏|萌狗冠军之路,纯干货分享!

news/2025/1/15 14:57:39/
文章来自微信公众号:前端工坊(fe_workshop),不定期更新有趣、好玩的前端相关原创技术文章。如果喜欢,请关注公众号:前端工坊
版权归公众号所有,转载请注明出处。
作者:毛科 刘麒麟

**我们做了一个小游戏!快来看一下!纯干货分享!
阅读本文需要5分钟,只需5分钟,你就会跟我一样,爱上这款游戏~**

图片描述

策划上

这款游戏具体的玩法是通过点击屏幕左右区域来控制小狗的前进方向进行跳跃,而阶梯是无穷尽的,若遇到障碍物或者踩空、或者小狗脚下的阶梯陨落,游戏失败;这款游戏提供指尖娱乐,考验大家左右手的配合能力,和迅速反应能力,长时间训练,就能得高分。

技术上

「使用3D图形引擎three.js随机渲染阶梯」

阶梯由一个个方块随机组成「无障碍物的阶梯」和「有障碍物的阶梯」,无障碍物的阶梯随机组成一条畅通无阻的路径,有障碍物的阶梯随机生成世界杯32强旗子。

方块纹理的绘制:
直接看代码,一个方块由6个面组成
图片描述

// 创建纹理
new THREE.MeshBasicMaterial({map: assets.getTexture("cubeWallpaper"),overdraw: true,
})// 创建6个面
const materials = [new THREE.MeshBasicMaterial({map: assets.getTexture("cubeWallpaper"),overdraw: true,}),new THREE.MeshBasicMaterial({map: assets.getTexture("cubeWallpaper"),overdraw: true,}),new THREE.MeshBasicMaterial({map: assets.getTexture("cube"),overdraw: true,}),new THREE.MeshBasicMaterial({map: assets.getTexture("cubeWallpaper"),overdraw: true,}),new THREE.MeshBasicMaterial({map: assets.getTexture("cobeWallpaper2"),overdraw: true,}),new THREE.MeshBasicMaterial({map: assets.getTexture("cubeWallpaper"),overdraw: true,}),
]
// 创建盒子
const boxmat = new THREE.MeshFaceMaterial(materials)

「使用canvas绘制2D图形,渲染排行榜」
图片描述

排行榜本身的功能实现并不复杂,主要是因为开放数据域的限制,显示麻烦一些。
排行榜主要功能进行拆解后主要涉及以下功能点

  • 上报用户分数
  • 获取用户好友分数
  • 对好友分数进行排序
  • 获取用户好友头像
  • 绘制排行榜
  • 增加前端的分页功能
  • 获取当前用户自己的信息

微信的开放数据域
在微信小程序当中,当我们需要获取微信好友的关系链时,会受到一些限制。微信为了保护自己的用户关系链不被恶意获取盗用,同时又想要建立开放的小程序生态,巩固自身地位。在对信息的开放与封闭之间,微信的解决办法是这样的,通过建立一个开放的数据域,所有和微信好友关系相关的API一律被限制,只能在开放数据域当中使用。且获取的数据,禁止以任何方式直接传递给主域,只能将数据绘制在一个离屏的shareCanvas,而主域通过将这个离屏的shareCavans绘制到上屏canvas上,从而达到展示微信好友关系链的数据。

在绘制排行榜的过程中遇到的坑和解决方法:
主域可以通过wx.postMessage向开放数据域发送消息通信,开放数据域通过监听主域的消息来决定什么时机调用相关API并绘制相关数据。但因为实际的调用API以及绘制页面和加载微信用户头像等等操作都是完全异步的,而主域并不知道什么时候开放数据域完成了接口的调用,和图像的绘制。

解决办法:当用户触发了 点击排行榜按钮之后,主域先绘制一个全透明的蒙层阻止用户继续操作交互。然后通过postMessage通知开放数据域子域,开放数据域子域接收到消息后,先判断这是个什么消息。比如是绘制排行榜ShowRankingList还是游戏结束GameOver,上报用户分数。
如果是绘制排行榜,则先异步加载排行榜的素材资源,然后将黑色背景与排行榜素材先绘制到canvas上,而与此同时主域则每隔0.5秒就获取一次shareCanvas将纹理绘制到主屏上。在开放数据域绘制完素材之后,会立即通过getFriendCloudStorage获取微信好友数据,然后通过getTopDataList对数据进行解析处理,因为拿到的数据是没有排序的,此时还会进行按分数进行排序。代码如下

topDataList = Lodash.sortBy(topDataList, (item) => {return -item.score
})

得到数据之后,先克隆一份,然后用slice切割为6个元素传递给displayTopDataList,这个是上屏显示用的6个好友数据。然后通过promiseTopDataList,该方法会将这6个元素的头像进行异步的获取。

这里有几个坑,一次并行请求3个以上的微信头像时,网络上很容易出现562错误,此时会导致头像绘制失败,程序上对头像绘制失败进行了容错,如果获取不到用户头像则显示一个空白的头像。但这始终不是个办法,最后的解决办法是按顺序依次加载完头像,然后返回。两种实现代码如下:
异步获取所有用户的头像

const taskList = []
for (const item of topDataList) {taskList.push(this.promiseTopData(item))
}
return Promise.all(taskList)

按步列依次获取用户的头像

return new Promise((resolve, reject) => {let index = 0let taskList = []let task = (index) => {const item = topDataList[index]this.promiseTopData(item).then((itemReady) => {taskList.push(itemReady)if (index < topDataList.length - 1) {index++task(index)} else {resolve(taskList)}}).catch((error) => {reject(error)})}task(index)
})

当资源准备好之后,调用drawImage方法开始排行榜数据绘制,这个就是普通的2d绘制了,没有什么太多的技巧,主要就是有一个初始的initTop值,元素的位置会相对该Top值偏移以此来进行定位。关于头像画圆,主要是通过创建一个cavnas,然后把头像画上去,再用arc剪裁,然后再把头像纹理画到shareCanvas上。

// 头像剪裁成圆
const avatarCanvas = wx.createCanvas()
avatarCanvas.width = 80
avatarCanvas.height = 80
const avatarContext = avatarCanvas.getContext('2d')avatarContext.save();
avatarContext.arc(40, 40, 40, 0, Math.PI * 2);
// 从画布上裁剪出这个圆形
avatarContext.clip();
avatarContext.drawImage(topItem.avatarDOM, 0, 0, 80, 80)
avatarContext.restore();// 实际的画
shareContext.drawImage(avatarCanvas, 130, (initTop + (i * 105)))

有什么方法可以把开放数据域的数据带出来?

目前尝试有,给shareCanvas附加属性,innerHTML,appendChild,data属性,均没有用,在开放数据域是个受限的环境,几乎绝大部分api都无法使用,包括localStorage等,也不能通过canvas的getDataURL把数据转换成Image,之所以这样的限制,是因为为了防止部分人通过开放数据域获取用户信息之后,再用getDataURL拿到绘制的用户数据,传递给后端API,做OCR图像识别解析微信用户关系,所以微信限制shareCanvas输出成图像,且限制,必须只能画在主屏上面。然后再通过人工审核机制,防范恶意小程序偷数据。

「生成并保存战绩页」
图片描述

把用户玩游戏的成果,包装成一个专属的宣传卡片,是诱导并激发用户分享意愿的一个强有力的方法。我们使用canvas结合微信小游戏开发生态,保存当前用户昵称和当场游戏结果生成不同的文案和图片,完成了从画布到截屏和存入相册一系列动作。

微信小游戏开发生态已经为你打通了画布到截屏到存入相册这一系列动作。

在本游戏中使用了 toTempFilePathsaveImageToPhotosAlbum 方法。这两者方法都挂载在wx对象下。

toTempFilePath, 将当前 Canvas 保存为一个临时文件,并生成相应的临时文件路径。

saveImageToPhotosAlbum,保存图片到系统相册。
核心代码:

//  loadCanvas 为我们专门为渲染战绩卡片而初始化的一个 2d canvas。loadCanvas.toTempFilePath({fileType: 'jpg',success: function(res) {wx.saveImageToPhotosAlbum({filePath: res.tempFilePath,success: function() {}})}
})

注意事项:

saveImageToPhotosAlbum会弹出相册授权弹框,记得处理失败回调哦。

如果里面要引用用户相关信息,记得也要处理用户不授权的情况。

关于生成到相册的图片清晰度问题,经验是,再用canvas绘制背景图的时候,别用高清图,都会被wx压缩的。这个自己去压缩纹理素材,这样既不会触发微信的压缩,又比微信压缩的效果好。

「游戏声音开关的控制」

音频创建

    let audio = new Audio(`${path}`);//path 为音频文件地址

音频播放

  audio.play();

音频停止

  audio.pause();

小游戏有背景音乐/小狗撞击障碍物失败音乐/小狗踩空音乐/阶梯陨落音乐/游戏升级音乐5种不同类型的音频文件。我们的做法是统一将音频文件集中预加载,并缓存到一个音频库的对象中,后续按需调用。

音频库对象中,我们定义了, begin, change,off,on等方法,在全局需要的地方做统一调度,这样可以使得,所有音频管理来自于一个music center,不会因为逻辑而紊乱。

在music center中,change,on,off均做了变量锁,使得了全域的操控触发,唯一相应。

for (let t of soundassets) {let audio = new Audio(assetpath + t.url);audio.autoplay = t.setting.autoplay;audio.loop = t.setting.loop;audio.addEventListener("load", () => {soundonload(audio, t);loaded++;onProgress(t.url, loaded, itesmtotal);if (loaded === itesmtotal) {onLoad();}});audio.addEventListener("error", (e) => {onError(assetpath + t.url);});itesmtotal++;
}

「游戏难度方面」

游戏的第一版本,我们的游戏难度是每一层阶梯以1.3s的速度陨落,每完成50s加大一倍难度的策略;小伙伴们反应游戏难得分低之后,我们迅速的调整游戏策略,把阶梯陨落速度降低,并且改为每100s加大一倍难度的玩法。

BitmapFont游戏数字的绘制
图片描述

在游戏当中经常有需要绘制一些图形的文字数字等,这个时候可以使用BitmapFont,也就是准备好文字与数字的纹理,并用TexturePack组成一张大图及记录图片尺寸位置信息的font.json。在代码当中加载这两个资源,然后就能直接使用了。

在我们的游戏当中也涉及到需要绘制数字图形分数的部分,首先是制作了一个用于处理分数的Score类,该类负责对分数进行记录,清零以及管理和数字图形的渲染。

当玩家分数变化时,我们会调用render进行分数的绘制,先通过string pad,将数字型的分数补零为5位数字字符串

_pad(num, n) {var len = num.toString().length;while (len < n) {num = "0" + num;len++;}return num;
}

打散字符串,并进行循环,更新对应位数的纹理

// 得到当前数字的纹理
const texture =this.resources.getTexture(texturePath);
// 替换对应位置的material的纹理为分数的纹理
this.score[`n${i}`].material.map = texture;

其它一些方法,reset用于分数清零,init负责对分数部件的及初始位数初始化

reset() {this.render(0)
}

游戏体验,请扫小程序码:
如果你有更有趣的想法,欢迎留言区讨论~
图片描述

更多小游戏,请关注微信公众号:前端工坊
后续,我们会推出更多纯干货技术分享
图片描述


http://www.ppmy.cn/news/943211.html

相关文章

一位深度学习小萌新的学渣笔记(一)神经网络原理理解+pytorch框架理解

软工大一学生&#xff0c;9月升大二&#xff0c;在暑假期间申报项目&#xff0c;7月份开始学习&#xff0c;学习的项目关于到神经网络和注意力机制 自学python 首先通过廖雪峰老师的网站自学了python 简单了解神经网络 学习所需知识 配置开发环境 Anaconda3 python3.7&…

Linux——服务器和服务器之间的拷贝

1. scp&#xff08;secure copy&#xff09;安全拷贝 scp 可以实现服务器与服务器之间的数据拷贝。&#xff08;from server1 to server2&#xff09; scp -r $pdir/$fname $user$host:$pdir/$fname 命令 递归 要拷贝的文件路径/名称 目的地用户主机:目的地路径/名称2. rsync…

PDF怎么转换成WORD?3大方法助您PDF转Word!

还不知道PDF怎么转换成WORD吗&#xff0c;本文将提供完整的PDF转Word方案&#xff0c;包括离线、在线或者SDK API等各种方式&#xff0c;总有一款满足您的需求。 什么是PDF转Word&#xff1f; PDF是出版和图形领域的软件厂商Adobe制定的电子文档格式标准。PDF转Word就是把PDF文…

这几个相见恨晚的PDF转换成Word技巧,请收好

生活学习中&#xff0c;免不了上网查找一些专业资料或者学术论文等&#xff0c;下载下来发现几乎都是PDF格式&#xff0c;若想对PDF文档进行编辑&#xff0c;实属不易&#xff0c;如何有效地对PDF文档进行图片&#xff0c;文字等复制&#xff0c;粘帖使用呢&#xff1f; 答案很…

Word怎么转换成PDF?三步教你免费搞定

Word文档是我们日常工作和学习中最常用的文件格式之一&#xff0c;但是有时候我们需要将其转换成PDF格式&#xff0c;以便更方便地与他人共享和阅读。转换Word文档为PDF格式非常简单&#xff0c;下面将介绍一个简单三步免费搞定的方法。 步骤一&#xff1a;打开在线转换网站 在…

PDF怎么转成Word?安利几个转换小技巧

平时我们工作学习的时候&#xff0c;经常要跟文件打交道&#xff0c;并且接触最多的文件形式就是PDF与Word两种文件格式&#xff0c;它们各有各的好处&#xff0c;PDF的保密性以及兼容性好&#xff0c;便于我们进行文件分享查阅&#xff0c;而Word就方便我们进行编辑。如果我们…

怎么把Word转换成PDF?这几种转换神器分享给你

如何将Word文档转换成PDF格式文件呢&#xff1f;大家在日常中经常会使用这两种文件&#xff0c;有时候需要把PDF文件转换成Word文档来进行编辑&#xff0c;有时候又需要把Word文档转换成PDF来进行传输文件发送文件。PDF如何转换成Word的方法前面给大家分享过转换方法&#xff0…

PDF怎么转换成Word?两种PDF免费转Word方法推荐

不知道你们有没有发现&#xff0c;我们在网上下载的很多资料都是PDF格式的&#xff0c;尽管PDF文件也可以通过专门的PDF编辑器来编辑&#xff0c;但是PDF文档作为版式文档&#xff0c;编辑起来还是存在很多局限性&#xff0c;所有当我们需要大量编辑修改文档的时候&#xff0c;…