尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

微信小程序小游戏源码拆解:从游戏循环到碰撞检测实战指南

微信小程序小游戏源码拆解:从游戏循环到碰撞检测实战指南 经常有人问我能不能分享一些基于微信小程序开发的小游戏源码说想自己学着写一个或者课程设计、毕业设计要交个能跑的小游戏demo。我硬盘里确实存了不少早年跑过的源码贪吃蛇、俄罗斯方块、2048、飞机大战、打地鼠都有这次重新翻出来逐个跑了一遍把其中值得反复拆解的核心逻辑、跑源码时会踩的坑、以及怎么把一份“别人写的源码”改造成你自己的作品一次性整理出来。这篇文章不是丢个下载链接就完事而是把源码背后的运行机制讲清楚。适合两类人看一是刚接触微信小程序、想搞明白小程序和小游戏到底什么关系的初学者二是手头需要快速出一个可演示小游戏demo、但不想从零手写渲染循环和碰撞检测的开发者。我会从源码选型、环境搭建、核心逻辑拆解、坑点排查讲到后续上线路线尽量让你拿到源码后不只是“能跑”而是真正懂它怎么跑起来的。1. 小游戏源码怎么选先分清微信小程序的“游戏”和“应用”1.1 “小程序”和“小游戏”是两套运行时很多人一开始就把小程序和小游戏混为一谈实际上它们在微信里是两套完全不同的运行环境。普通小程序走的是Page WXML WXSS JS这套结构页面由组件树渲染开发者可以像写网页一样操作DOM层面的逻辑但小游戏不一样它没有WXML、WXSS和组件树只有一个全屏的Canvas和一个全局的canvas上下文所有画面都是你用代码一帧一帧画出来的。这意味着你平常积累的那套小程序开发经验在写小游戏时大多用不上。比如你没法在wxml里放一个view当游戏背景也没法用wx.createSelectorQuery去查某个元素的宽高你需要自己管理渲染坐标、帧循环和触摸事件。反过来也一样如果你找的源码是“xxx小游戏源码”但是里面全是.wxml文件那它大概率只是一个模拟游戏界面、用定时器改数据的小程序应用并不是真正基于Canvas渲染的小游戏。我整理源码时会把这两类分开本文说的小游戏特指基于Canvas直接绘制、入口是game.js的那一类。1.2 我筛选源码的三条标准网上一搜“微信小程序小游戏源码”能出来一大堆但质量参差不齐很多下载下来根本跑不起来。我筛选并保留到现在的源码基本都满足下面三条标准你可以按同样的思路去筛你要的库游戏玩法经典逻辑足够简单直白。贪吃蛇、2048、俄罗斯方块这类游戏的核心规则用几十行代码就能说清楚非常适合用来理解小游戏的基本架构。像那种大型RPG或者卡牌对战源码动辄几百个文件新手看一晚上只会更懵。纯前端实现不依赖后端接口。一跑起来就要连服务器、要websocket的源码本地根本调试不了而且现在很多免费接口已经失效拿回来反而是负担。包体小、入口清晰。一个合格的小游戏源码根目录下必须有game.js、game.json和project.config.json这是微信开发者工具识别项目的关键。如果连这三个文件都没有那它顶多算一段“代码片段”不是完整项目。1.3 建议的源码学习顺序如果你是完全没写过小游戏的新人我建议按照“贪吃蛇 → 2048 → 飞机大战”这个顺序去看源码。贪吃蛇最能帮你建立“游戏循环 状态更新 重绘”的整体认知2048的核心是二维数组的合并算法能训练你处理数据逻辑飞机大战则会涉及到碰撞检测、对象池、触控操作这些更进阶的东西。等把这三种都跑通一遍你再去看Cocos、Laya这类引擎生成的工程或者Unity导出的小游戏心里就有底了因为底层无非还是那套渲染循环。2. 新手30分钟跑通第一份小游戏源码2.1 需要准备的工具和AppID跑通小游戏源码需要三个东西微信开发者工具、一个小游戏类目的AppID、以及源码本身。微信开发者工具直接在官网下载稳定版就行安装时选“小游戏”项目类型即可。AppID需要到微信公众平台注册一个账号然后在后台“设置→基本设置→服务类目”里选择“小游戏”会自动生成一串以wx开头的AppID。如果你只是本地调试、不涉及真机预览和上传也可以先选“测试号”来跑但请注意测试号在使用云开发、支付、登录等接口时是有一定限制的。我自己的习惯是注册一个个人小游戏AppID虽然个人主体在类目上有一些限制但用来跑源码、做 demo 演示完全够用。2.2 导入源码后必做的三步检查拿到源码后不要急着点“编译”先做三步检查能帮你省下不少排错时间。第一步看根目录结构。你的项目根目录下应该有game.js这是小游戏的入口文件相当于网页里的index.html。如果源码里既有game.js又有app.js那说明可能是混着普通小程序写的需要你搞清楚哪个才是真正入口。第二步看game.json。这个文件里配置了小游戏的页面朝向、渲染模式、分包等信息常见的字段包括{ deviceOrientation: portrait, showStatusBar: false, networkTimeout: { request: 5000 } }showStatusBar控制是否显示状态栏做全屏类游戏一般设为false。如果文件不存在或格式错误工具会直接报错这是最常见的白屏原因之一。第三步确认project.config.json里的appid是不是你自己的。这个文件记录了项目级配置如果你用自己的AppID导入通常工具里重新填一下即可。你只需要知道小游戏里决定“项目能不能跑”的是game.json和project.config.json决定“代码入口在哪儿”的是game.js。2.3 真机预览与基础库设置在开发者工具里能跑起来只是第一步小游戏很多问题只有真机才暴露。真机预览前先到详情面板把“本地设置”里的调试基础库切高一点小游戏需要用Canvas、WebGL相关的API基础库版本太低会出现接口找不到的情况。我通常直接选当前最新稳定版然后开启“ES6转ES5”和“上传代码时样式自动补全”这两个选项能避免不少兼容性坑。真机预览点工具栏里的“预览”按钮手机会弹出二维码扫码后微信里打开就是小游戏的线上体验版本。如果遇到“打开失败”的情况先检查AppID是否正确再看基础库版本这两个问题占了绝大多数比例。3. 源码拆解游戏循环、碰撞检测与数据存储3.1 小游戏的主循环与帧率控制所有小游戏源码里最核心的一段代码就是主循环。小游戏没有浏览器里现成的DOM事件驱动而是一帧一帧地主动刷新画面。微信官方提供的requestAnimationFrame接口就是为了实现这个循环的。看一段典型的简易主循环实现// game.js const canvas wx.createCanvas() const ctx canvas.getContext(2d) let lastTime 0 function gameLoop(time) { const delta time - lastTime lastTime time // 更新逻辑 update(delta) // 渲染 render(ctx) requestAnimationFrame(gameLoop) } requestAnimationFrame(gameLoop)这里的delta是距离上一帧的时间差用来做物理运动和动画插值。没有这个差值游戏速度会受设备帧率影响同一段代码在60Hz和120Hz屏幕上表现完全不同。顺带一提网上有些分享的源码会用setInterval(fn, 1000 / 60)来模拟帧循环这在浏览器端可以但在微信小游戏里不建议这么做。setInterval的最小间隔精度、以及小程序切到后台时的行为都不可控官方也不推荐。你拿到源码后如果看到这个模式建议改成requestAnimationFrame。3.2 碰撞检测的朴素实现碰撞检测是小游戏绕不开的一环飞机大战打中敌机、贪吃蛇吃到食物、俄罗斯方块落到底部本质上都要回答一个问题“两个矩形有没有重叠”。在小游戏里Canvas上绘制的精灵图大多用矩形包围盒做粗略碰撞也就是AABB碰撞检测实现起来非常直接function hitTest(a, b) { return a.x b.x b.width a.x a.width b.x a.y b.y b.height a.y a.height b.y }这段代码的意思是如果a矩形的左边缘在b矩形右边缘的左边、a矩形的右边缘在b矩形左边缘的右边、a矩形的上边缘在b矩形下边缘的上边、a矩形的下边缘在b矩形上边缘的下边那它们一定重叠了。对于大多数入门级小游戏这个精度完全够用不需要做像素级碰撞检测。实际开发中为了让玩家体验更好我们通常不会用整个精灵图的外框而是把碰撞盒缩小一点。比如飞机大战里机身触碰到子弹判定死亡但飞机贴图的机翼是不算碰撞范围的所以你要在源码里看到类似{x: this.x 10, y: this.y 10, width: this.width - 20, height: this.height - 20}这种写法别觉得奇怪这是在给玩家“作弊”。3.3 2048的合并算法2048这款游戏特别适合练习数据抽象。它看起来是在处理方块的移动和合并本质上就是维护一个4x4的二维数组每一次滑动都是在处理数组里非零数字的聚集和相邻相等数的合并。很多源码里会单独抽出一个board.js来管这些逻辑我建议你也这么做把游戏逻辑和渲染分离代码会清爽很多。核心的合并逻辑长这样function mergeRow(row) { // 去掉所有0把数字前移 let arr row.filter(n n ! 0) // 相邻相等则合并 for (let i 0; i arr.length - 1; i) { if (arr[i] arr[i 1]) { arr[i] * 2 arr.splice(i 1, 1) } } // 末尾补0恢复固定长度 while (arr.length 4) { arr.push(0) } return arr }你如果拿到了2048的源码可以重点看它四个方向的滑动是怎么统一处理的。一般会先把二维数组旋转到同一个方向合并完再旋转回去这样就不需要写上下左右四套逻辑了。3.4 最高分与存档小游戏运行在小程序环境中天然自带一套本地存储接口。保存最高分最方便的方式是wx.setStorageSync和wx.getStorageSync它们是同步方法传进去的值会被自动序列化简单场景完全够用// 保存 wx.setStorageSync(bestScore, currentScore) // 读取 const bestScore wx.getStorageSync(bestScore) || 0有些源码还会用wx.getFileSystemManager配合wx.env.USER_DATA_PATH来写自定义文件存存档这种方式可以保存复杂的游戏状态比如关卡进度、背包数据比单纯的Storage更灵活。wx.env.USER_DATA_PATH是小游戏专属的用户目录路径类似于沙盒目录的绝对路径可以用来保存图片、文本等文件const fs wx.getFileSystemManager() const filePath ${wx.env.USER_DATA_PATH}/save.json // 写文件 fs.writeFileSync(filePath, JSON.stringify(gameData), utf-8) // 读文件 const data JSON.parse(fs.readFileSync(filePath, utf-8))需要留意的是小游戏本地缓存空间是有限制的不要无脑往里塞数据滥用会把空间撑爆。3.5 给源码加一个排行榜排行榜是小游戏拉升活跃度最常用也是需求最多排行功能之一。最简单的方案是纯本地排行榜把玩家历史最高分按降序排展示前10名这种适合单机自嗨的demo。如果想要让不同玩家之间比分数就需要一个后端存储这时微信云开发是最省事的方案。云开发里建一个score集合把玩家昵称、分数、时间写入再用云函数查询Top10返回即可。你需要申请开通云开发环境然后在源码里调用wx.cloud.callFunction。这套链路虽然看起来多但每一步都有微信官方文档不会比你自己买服务器再写接口慢多少。4. 把源码改造成你的游戏10个高频坑与排查思路4.1 白屏与入口文件报错导入源码后最常见的问题就是白屏。白屏的原因大多在入口文件执行报错但你打开控制台却什么都没打印这种情况先确认你有没有选中“小游戏”类型的项目模板。工具会默认创建普通小程序模板而普通小程序模板的入口是app.js根本不会找到game.js自然白屏。另一种可能是game.json里配置了不存在的分包或者引用路径导致首屏资源加载失败。解法是把源码根目录重新整理一下确保game.js在根目录并且在控制台看有没有红色报错日志。4.2 图片加载是异步的小游戏里用wx.createImage加载本地图片资源时要注意图片加载完成是异步的。如果你在img.src赋值后立刻去ctx.drawImage大概率画出来的是空白。必须把绘制放在onload回调里const bg wx.createImage() bg.onload () { ctx.drawImage(bg, 0, 0, canvas.width, canvas.height) } bg.src images/bg.png很多源码不严谨直接在初始化时用了图片对象导致真机上偶发白屏、图片闪烁其实就是没等onload。我建议拿到源码后做一次全面体检把所有图片资源都走一遍onload再进主循环你会发现稳定性提升明显。4.3 音效不播放音效不播放的问题十有八九不是代码问题而是微信对音频播放时机的限制。简洁说就是用户没有跟小程序产生交互前不能主动播放音频。所以游戏在拿到用户第一次点击后再去初始化背景音乐和音效基本都能解决。代码层面用wx.createInnerAudioContext这样的方式创建音频上下文然后循环播放入口const bgm wx.createInnerAudioContext() bgm.src audio/bgm.mp3 bgm.loop true // 用户第一次点击后调用 wx.onTouchStart(() { bgm.play() })另外注意iOS端音频格式最好用AAC/MP3有些设备上WAV格式支持不佳。4.4 长按拖拽与触摸坐标的换算在小游戏里做拖拽操作时很多人直接拿e.touches[0].clientX当游戏坐标结果发现拖拽的物体总是偏移。这是因为clientX和clientY是相对屏幕可视区域的坐标而游戏画布可能因为设备屏幕尺寸比例不一致被拉伸或留有黑边导致坐标被缩放。正确的做法是把触摸坐标换算成Canvas的逻辑坐标。你在拿到触摸坐标后用Canvas的宽高比做归一化处理或者提前计算好Canvas左上角在屏幕上的偏移再做减法。还有一个常见的坑是长按没反应——小游戏对长按的默认处理比较敏感如果你发现系统弹出了文本选择菜单需要在game.json里把disableLongPress之类的配置打开具体字段以当前基础库文档为准。4.5 顶部导航栏高度与安全区适配虽然小游戏默认全屏绘制但顶部状态栏、底部横条这些系统区域仍然存在。如果你在真机上发现游戏操作按钮被刘海屏挡住或者顶部文字被状态栏盖住就需要获取安全区信息来做适配const { safeArea } wx.getSystemInfoSync()safeArea会给出屏幕上安全区域的top、bottom等值你要手动调整UI元素的位置。拿到这个值后把游戏里最重要的操作按钮和计分文字布局在安全区内边缘装饰则可以放心铺满。如果你做的是普通小程序而不是小游戏顶部导航栏高度又是另一套逻辑要区分清楚。4.6 卡顿与Canvas优化小游戏跑起来越来越卡大多是因为每帧都创建了新的对象、执行了不必要的绘制。常见优化手段包括物体回收复用、脏矩形重绘只更新变化区域、避免每帧ctx.fillStyle赋值后再来一次全局清屏。Canvas绘制是同步的频繁调用fillRect会非常消耗性能。我实践下来最粗暴有效的一招是主循环里把不变的背景图单独画在不参与频繁更新的离屏Canvas上每帧先把背景图整块贴上去再画动态物体这样能有效减少绘制次数。如果你的游戏里同屏敌机很多用对象池管理子弹和敌机也比反复new和销毁对象流畅得多。4.7 uni-app/Cocos/Unity 打包小游戏的注意点越来越多开发者选择用uni-app或Cocos/Unity这类跨端引擎来开发小游戏然后导出微信小游戏版本。这个思路本身没问题但要注意两点。一是包体大小。微信小游戏主包体积目前限制是4M超过部分必须走分包加载。Unity导出的小游戏通常光引擎运行时就好几兆所以要对资源做压缩把图片压成WebP、音频用AAC减少首包体积。二是渲染适配。微信小游戏的渲染机制和浏览器不完全一样iOS端对WebGL的支持和Android也有差异Unity打包后一定要在真机上分别测一遍只在开发者工具里预览是远远不够的。如果只是做简单2D小游戏我个人的建议是不要一上来就用Unity这种重型引擎直接用Canvas手写或者用Cocos的2D模式折腾成本会低很多。4.8 基础库版本兼容与渲染引擎适配微信小游戏的运行时一直在更新开发工具默认用的基础库也许很新但用户手机上可能还跑着老版本微信导致一些新API不生效。处理办法是在开发工具里把调试基础库切到恰当版本测试同时在你用的关键API文档里确认它要求的最低基础库版本。另外微信最近几年在推进新版渲染引擎并且对普通小程序的渲染机制做了不少升级但小游戏作为Canvas方案相对稳定不过Cocos/Unity这类引擎导出的工程要特别关注引擎版本适配说明。遇到问题时优先看控制台输出的API报错信息它一般会直接告诉你哪个接口需要什么基础库版本。5. 从源码到上线的进阶路线5.1 接入云开发让游戏不再“单机”前面提到的排行榜只是一种场景云开发在小游戏里的可能性远不止这些。登录态、每日签到、好友对战、动态更新关卡数据都可以用云开发实现完全不用自己买服务器。对小游戏这种流量波动大、冷启动频繁的场景云开发的按量付费模式对小团队非常友好。接入方式很简单先在game.js顶部初始化wx.cloud.init({ env: your-env-id, traceUser: true })然后就能在小游戏里调用wx.cloud.callFunction、wx.cloud.database()了。源码里如果有接口调用通常第一步都是这个init不要漏掉。5.2 上线审核注意点游戏可玩、测试通过后要正式发布还需要走微信小游戏的审核流程。审核过程中最容易出现的问题是游戏内容涉及随机抽取但未做概率公示、虚拟支付未经允许、以及素材版权问题。个人开发者建议先严格使用免费可商用素材避免字体、音乐、图片方面的侵权风险。另外在填写类目时小游戏有单独的类目体系别选错。审核一般一两天内会出结果如果被驳回照着驳回理由逐条改大多数都是文案提示类问题不涉及代码改动。5.3 埋点与数据驱动源码跑通、上线发布这才只是游戏的开始。我见过很多开发者把游戏做好上线就撒手不管了结果日活几天后归零还找不到原因。建议你在源码里就规划好数据埋点启动次数、关卡失败率、分享按钮点击率、新增用户次日留存这些数据能告诉你玩家的流失点在哪里。微信开发者工具里的“数据助手”和“实验室”功能已经能提供大量基础指标如果你的游戏有自定义事件也可以通过wx.reportEvent上报。多改一版、多看一次数据比闷头写新玩法更能稳住你的用户盘子。我个人在实际操作中的体会是一套好的小游戏源码价值不在于“拿来就能跑”而在于它把游戏最底层的运行框架、数据结构和渲染逻辑摊开给你看。你花一晚上反复改其中某个参数、给角色换皮、调碰撞盒大小学到的比看十篇教程都多。这份源码整理我会持续更新下次再跑通新的游戏类型再继续把心得补充进来。
返回列表