Cocos Creator 微信小游戏开发:小鸟源码解析与发布避坑指南
简介这是一份基于 Cocos Creator 开发的微信小游戏完整源代码面向使用 Cocos Creator 进行微信小游戏开发与打包的开发者尤其适合以“小鸟”类休闲游戏为切入点学习引擎构建与发布流程的读者。资源包共 592 个文件压缩包大小约 1.67MB包含 289 个 json 配置数据文件、180 个 png 图形素材、81 个 meta 资源元数据、17 个 js 与 2 个 ts 脚本文件另有 plist 图集、fire 场景文件、fnt 位图字体以及 gltf 模型文件覆盖场景搭建、素材管理、逻辑控制与资源配置等核心环节。目前已有 3301 人学习下载具备较高的参考热度。通过这份源码读者可以直观了解 Cocos Creator 工程中各类资源文件的分工与组织形式理解 json 配置如何驱动场景、png/plist 如何组织图片、js/ts 脚本如何挂接节点逻辑也能掌握将游戏发布为微信小游戏所需的目录结构与基础配置要点。基于现有“小鸟”游戏逻辑还可进行玩法扩展、UI 调整或素材替换是一份兼顾入门学习与二次开发的实用素材。1. 拿到 Cocos Creator 小鸟源码先别双击工程版本决定你能不能跑起来如果你手里也有一份Cocos Creator 打包成微信小游戏的小鸟游戏源代码最忌讳的第一件事就是双击工程文件直接打开。类似「Cocos Creator 小鸟」这种流传较广的练手项目很多是在 2.x 时代写的而你电脑上装的可能是 3.x绝大多数情况下工程直接打不开——脚本编译报错、场景资源丢失、构建按钮灰掉每一步都在消耗耐心。正确的顺序是先看引擎版本再决定是直接打开还是先做工程迁移。这份资源本质上是一个完整可运行的小游戏工程包含场景、预制体、游戏脚本、构建配置核心玩法就是点击起跳、穿过管道、计分、碰撞结束。它解决的问题很具体不用从零搭框架拿到手改一改素材和参数就能构建出在微信里跑的竖屏小游戏。适合两类人一类是第一次接微信小游戏、想知道整套发布链路怎么走的开发者另一类是想要一个干净模板、快速验证玩法原型的熟手。我的做法是先把它当成「可运行的参考实现」再逐步替换成自己的东西。2. 微信小游戏不是网页运行环境、屏幕适配与资源策略2.1 没有 DOM 的渲染环境引擎是怎么塞进微信的微信小游戏运行在一个阉割过的浏览器内核里没有document、没有window、没有 DOM 树只提供 Canvas 和 WebGL 渲染接口。Cocos Creator 之所以能把游戏打包进去靠的是一层适配层——把引擎内部对 DOM、Audio、本地存储的调用全部转接到微信小游戏提供的wx.*接口上。Cocos Creator 2.x 和 3.x 在这层的处理方式不同2.x 更像是在原有网页渲染体系外面包一层 polyfill不少浏览器 API 被强行模拟出来3.x 从架构上就把渲染层直接对接 WebGL适配更干净。你可以通过构建产物来验证这一点。# 构建完成后进入微信小游戏产物目录 cd build/wechatgame # 查看顶层结构 ls -la # 查找引擎适配层相关文件 find . -name *adapter* -o -name sys* | head -20构建产物里会有src/、assets/、engine/之类的目录engine/下就是被编译过的引擎适配代码。如果你看到大量wx.createCanvas、wx.getSystemInfoSync这类调用说明适配层工作正常。拿到任何一份 Cocos 小游戏源码建议先执行这一步确认构建产物完整再回过去看源码工程。这里有个容易忽略的点微信开发者工具模拟器能跑不代表所有接口在真机上可用。适配层代码在模拟器和真机上走的是同一套逻辑但真机上 Canvas 的创建时机、触摸事件的坐标换算、音频的加载策略都有差异后面第 5 章会专门讲。2.2 屏幕适配设计分辨率、适配策略与安全区小鸟游戏是竖屏玩法画面宽高比在不同手机上差异很大——iPhone 的全面屏比例接近 19.5:9老款安卓机是 16:9老 iPhone 是 16:9 左右。如果不做适配会出现两种情况要么画面被拉伸变形要么两侧被裁掉内容。Cocos Creator 的适配核心是cc.view.setDesignResolutionSize()加上 Canvas 组件的适配策略。设计分辨率就是你制作场景时用的逻辑尺寸比如720x1280真机渲染时引擎按策略换算成实际像素。适配策略行为适用场景风险SHOW_ALL完整显示设计分辨率内容留黑边拼图、棋牌类两侧或上下留边不美观FIXED_WIDTH宽度铺满高度方向可超出裁剪竖屏跑酷、射击、小鸟顶部或底部可能被裁掉FIXED_HEIGHT高度铺满宽度方向可超出裁剪横屏游戏左右可能被裁掉NO_BORDER按比例放大到铺满全屏四周裁剪全屏沉浸式裁剪最多UI 位置要小心小鸟这类竖屏游戏我一般用 FIXED_WIDTH。玩法核心是角色左右移动范围固定宽度保持一致能保证管道间距在每台设备上视觉一致高度方向多一些少一些影响不大。// 在 game.js 或首场景的启动脚本里设置 view.setDesignResolutionSize(720, 1280, ResolutionPolicy.FIXED_WIDTH);需要注意一个坑Cocos Creator 3.x 里designResolution的设置在构建面板的「适配屏幕」选项里也能配而且构建面板的配置会覆盖代码。所以如果你在脚本里改了分辨率但真机上没生效先检查构建面板的「适配屏幕宽度/高度」是不是被勾成了另一个策略。安全区问题同样不能漏。全面屏手机的底部有横条顶部有刘海如果你把分数 UI 放在屏幕最顶部或操作按钮放在最底部真机上很可能被遮挡。微信小游戏提供了wx.getSystemInfoSync().safeArea在 Cocos 里可以通过sys.getSafeAreaRect()拿到逻辑坐标下的安全区。拿到后把关键 UI 强制约束在该矩形内。2.3 资源打包策略主包 4MB 与 Bundle 拆分微信小游戏主包有 4MB 的体积限制超过就上传失败。小鸟游戏的资源其实很小几张鸟的序列帧、几张管道图片、一个背景、两三个音效加起来通常不到 1MB完全可以内嵌在主包里。但如果你替换了高清素材或者加了一堆 UI 贴图、粒子特效体积很快就会顶到上限。Cocos Creator 3.x 提供了 Bundle 机制可以把资源拆成多个子包主包只保留首屏必需内容其余资源在需要时动态加载。判断当前构建产物体积是否超限命令很简单# 在构建产物目录下查看体积 du -sh build/wechatgame # 如果只关心首包子包会单独列目录 du -sh build/wechatgame/assets我拿到这份小鸟源码后先看 assets 目录如果里面资源文件不多默认就是单包模式不需要额外拆分。真正要拆的场景是你打算把鸟的动画帧、背景音效全部替换成高质量素材。我遇到过一次替换了一套 2K 背景图主包直接到 5.2MB上传被拒后来把背景图和音效挪到子包启动时先显示 Loading 再动态下载问题才解决。拆 Bundle 的操作在编辑器里就能做选中资源文件夹在属性检查器里把「Bundle 名称」填上资源就归到对应子包。加载时用assetManager.loadBundle()配合bundle.load()。不要把所有东西都塞进首包也不要为了拆而拆——Bundle 的下载有延迟首屏需要的东西留在主包次要内容拆出去。3. 拆解小鸟游戏核心代码场景结构、状态机与碰撞判定3.1 拿到源码先看目录与场景结构一份标准的 Cocos Creator 工程最核心的目录是assets/里面按常规会分成scenes/、scripts/、prefabs/、resources/。拿到源码后我习惯先把assets/的目录树打出来快速建立全局认知。# 在工程根目录执行 tree assets -L 2正常会看到类似结构scenes/下放.scene文件本质是 JSON 格式的场景描述scripts/下放.ts或.js脚本prefabs/下放可复用的预制体。小鸟游戏一般会有一个主场景场景的 Canvas 节点上挂着游戏管理器脚本鸟角色节点上挂着角色控制脚本。识别入口场景有一个技巧打开assets/下的场景文件搜索GameManager或类似命名的脚本类那个场景大概率就是主入口。.scene文件很长不要全文读用编辑器打开后直接看节点树和组件列表比看 JSON 直观得多。3.2 游戏状态机与鸟的物理模型小鸟游戏看起来简单代码组织却需要一个清晰的状态机。原因很实际鸟在「待开始」「飞行中」「游戏结束」三种状态下响应完全不同——待开始时不掉下来飞行中点击会起跳结束后点击要重新开始。如果不做状态区分逻辑会乱成一团。// GameManager.ts - 状态机核心 enum GameState { Ready, Playing, Over } // 单例管理节点状态 export class GameManager extends Component { public state: GameState GameState.Ready; startGame() { this.state GameState.Playing; // 鸟开始受重力影响 bird.enableGravity(true); // 管道开始生成与移动 pipeManager.startSpawning(); } gameOver() { this.state GameState.Over; // 停止管道生成播放碰撞动画 pipeManager.stopSpawning(); audioManager.playHit(); } onTouched() { if (this.state GameState.Ready) { this.startGame(); } else if (this.state GameState.Playing) { bird.jump(); } else if (this.state GameState.Over) { this.restartGame(); } } }这段代码里onTouched被全局触摸事件调用通过判断state决定行为。这是小鸟游戏最核心的控制流——所有交互都收敛到这一个方法里状态切换的入口非常清楚。鸟的物理模型是伪重力和伪速度不需要引入完整的物理引擎。每一帧做两件事速度累加重力增量位置累加速度增量。// Bird.ts - 鸟的运动逻辑 public gravity: number -1200; // 重力加速度像素/秒²负值向下 public jumpSpeed: number 420; // 起跳瞬间的速度像素/秒正值向上 public rotationSpeed: number 180; private _velocity: number 0; update(dt: number) { // 未开始或已结束时不更新物理 if (gameManager.state ! GameState.Playing) return; // 重力改变速度速度改变位置 this._velocity this.gravity * dt; this.node.setPosition( this.node.position.x, this.node.position.y this._velocity * dt ); } jump() { // 每次点击把速度重置为起跳速度实现「拍打」效果 this._velocity this.jumpSpeed; }gravity和jumpSpeed是影响手感的第一优先级参数。重力越大下落越快跳跃速度越大单次抬升越高。我测试时习惯先把重力设到-1000、跳跃速度400跑几轮之后按「跳一下上升大概 80~100 像素」的手感目标去微调。这份源码里这两个值一定写在脚本顶部或 Inspector 暴露面板上改完保存就能在模拟器里直接感受差异不用重新编译。3.3 管道生成、移动与得分检测管道是另一个核心模块。它的工作模式不是每帧新建实例而是对象池复用——生成一组管道对移动到屏幕左侧边界后回收再放在右侧新的位置。这样做主要为了性能小鸟游戏场景里通常同时存在 4~5 组管道如果反复创建销毁节点会造成频繁的 GC 暂停。// PipeManager.ts - 管道生成与回收 public spawnInterval: number 1.4; // 每 1.4 秒生成一组管道 public gapHeight: number 200; // 上下管道之间的缝隙高度 public moveSpeed: number -160; // 管道左移速度像素/秒 private _pipes: Node[] []; update(dt: number) { for (let pipe of this._pipes) { pipe.position.x this.moveSpeed * dt; // 移出屏幕左侧后回收 if (pipe.position.x -900) this.recycle(pipe); } } generatePipe() { // 随机缝隙中心点保证缝隙在上下管道之间不会出界 const minY -400; const maxY 200; const gapCenter Math.random() * (maxY - minY) minY; // 上面管道底部 缝隙中心 - gapHeight/2 // 下面管道顶部 缝隙中心 gapHeight/2 // 分别设置两组管道的 y 坐标后加入 _pipes }gapHeight是难度核心。缝越大越容易过越小越劝退。spawnInterval决定了管道密集度越短越紧张。我见过很多游戏的难度曲线就是在spawnInterval上做文章——每过 10 分减少 0.05 秒配合gapHeight逐步缩小玩家很难察觉难度在爬升。碰撞判定是这份源码里最值得看的部分。小鸟游戏不需要引擎级物理碰撞用 AABB 包围盒相交检测就够了代码量少、性能好、调试直观。// Collision.ts - 轴对齐包围盒碰撞检测 function checkAABB(bird: Rect, pipeRect: Rect): boolean { // 两个矩形相交的条件X 方向与 Y 方向都重叠 return bird.x pipeRect.x pipeRect.width bird.x bird.width pipeRect.x bird.y pipeRect.y pipeRect.height bird.y bird.height pipeRect.y; }它的原理简单说把鸟和管道各看成一个矩形检查它们在 X 轴和 Y 轴上的投影是否同时重叠。如果只用管道组的中心点做检测会出现「鸟明明被碰到却被判定得分」的错觉——因为管道上下两根的矩形是分离的中间缝隙不算碰撞中心点检测会把缝隙和管道壁混为一谈。正确做法是对每根管道单独取矩形做检测。得分检测更简单——不用碰碰撞体只比较位置坐标当管道的 x 坐标第一次小于鸟的 x 坐标时得分加一。这时候管道和鸟还没发生碰撞玩家会有「刚好擦过去」的爽感。但要注意一定要加一个「已得分」标记否则同一组管道会在多帧内连续触发加分。4. 从 Cocos Creator 构建到微信开发者工具完整发布流程4.1 构建面板参数配置Cocos Creator 的构建面板是所有平台发布的统一入口选错参数会在后面浪费大量时间。构建平台的选项里选「微信小游戏」然后逐个确认参数。参数推荐值说明AppID填写小游戏 AppID 或用测试号正式发布必须真实 AppID测试号无法上传初始场景主场景含 GameManager 的场景启动后加载的第一个场景设备方向竖屏Portrait小鸟游戏必须是竖屏设错会横着运行适配屏幕勾选配合 FIXED_WIDTH 策略生效MD5 Cache勾选资源文件名加 MD5 哈希防止微信缓存旧资源调试模式开发时勾选发布前取消勾选后保留调试日志取消后体积更小主包压缩类型默认保持默认即可不需要手动改有一个参数容易被忽略初始场景。如果你做了一堆测试场景忘了把入口场景指定为正式主场景构建出来的包可能打开就是一个空白场景甚至直接报错。4.2 导出构建产物并导入微信开发者工具构建完成后产物默认在工程目录下的build/wechatgame。打开微信开发者工具导入项目时选择build/wechatgame目录而不是工程根目录——这是新手最容易搞混的地方。# 构建完成后先看一眼产物是否完整 cd build/wechatgame ls -la # 期待看到 game.json、project.config.json、src/、assets/ 等 # 如果没有 project.config.json说明构建不完整回到编辑器重新构建微信开发者工具导入后还需要检查两件事右上角详情里的「本地设置」要勾选「不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书」。这不是为了绕过什么审核而是因为本地调试时使用的是局域网 IP 和临时资源路径不校验才能正常加载。正式上传前要取消勾选否则线上环境资源加载策略不同可能出现本地能跑线上白屏的诡异现象。这步里最玄学的点在于微信开发者工具的模拟器有时和真机表现不一致。触摸事件的模拟、帧率的差异、音频的时序都可能不同。所以我在模拟器里能跑通后不会急着收工而是用开发者工具右上角的「预览」按钮生成二维码用手机微信扫码真机跑一遍。4.3 构建产物快速排错看日志与查进程真机预览报错时第一反应不应该是改代码重新构建。先打开微信开发者工具的「调试器」面板切换到 Console 标签页把报错信息里的文件名和行号记下来。最常见的情况是资源路径错误或脚本空引用日志会直接告诉你。还有一种情况是点了「预览」二维码但手机打开后长时间黑屏。不要急着删包先确认是否首次加载子包资源——如果你把资源放进了 Bundle首次进入会有一个下载过程在弱网下可能要等好几秒黑屏其实是资源下载中。这时候在工程代码里加一个 Loading 转圈提示比优化加载速度更重要——玩家不知道卡了还是挂了体验差别很大。5. 打包微信小游戏常见问题复盘五条现象、原因与处理5.1 构建后导入工具直接白屏报 Cannot read property of undefined现象构建产物导入微信开发者工具模拟器一片白Console 里报错指向引擎源码里的某一行。原因90% 的情况是 Cocos Creator 编辑器版本和构建产物引擎版本不一致。比如你前几天用 3.x 构建过一版今天换了 2.x 工程直接构建旧的构建缓存没清理产物理的引擎适配层被旧代码污染。解决在构建面板点右下角的「清除」按钮删掉整个build/wechatgame目录重新构建。这一步能解决绝大多数构建产物异常。如果还是白屏把微信开发者工具的「缓存目录」也清一遍——工具本身会缓存编译中间文件会导致代码更新不生效。5.2 真机上角色乱飞、位置偏移模拟器却一切正常现象模拟器里鸟的位置和点击响应完全正常换成真机后鸟飞到屏幕外、点击位置偏了半个屏幕。原因模拟器默认使用工具的窗口尺寸真机的屏幕物理尺寸、像素密度都不同。如果适配策略是 SHOW_ALL不同宽高比会产生黑边节点坐标和触摸坐标的换算就会被拉伸扭曲。解决确认竖屏游戏的适配策略统一为 FIXED_WIDTH 或 FIXED_HEIGHT不要混用。检查 Canvas 组件里的适配设置和代码里setDesignResolutionSize是否冲突以代码为准构建面板的「适配屏幕」保持勾选即可。另外真机调试时先执行wx.getSystemInfoSync()确认拿到的屏幕宽高比和设计分辨率是否是同一方向。5.3 音频真机无声模拟器正常播放现象点击起跳的音效、碰撞的音效在模拟器里都正常真机上完全无声。原因微信小游戏对音频有明确的自动播放限制——不允许在没有任何用户交互前播放音频。如果你的play()调用发生在场景启动、碰撞检测等没有用户手势触发的时机微信会直接丢弃这次播放请求。解决把音频初始化放在第一次用户触摸的回调里。比如在主场景的onTouched里先执行audioManager.resume()或audioSource.play()之后再播放任何音效都没问题。我通常会监听里一次触摸事件在触摸回调里调用wx.setInnerAudioOption({ obeyMuteSwitch: false })并预热音频实例。5.4 主包超过 4MB上传被拒现象构建成功但上传到微信公众平台时提示主包超限。原因资源文件体积过大主要是图片。PNG 原图动不动几 MB序列帧动画更是重量级。小鸟游戏的动画帧如果做成 512x512 的高清图很容易爆掉。解决图片用 TinyPNG 或 Cocos Creator 内置压缩工具压到 1024 以内音频如果是 WAV 转成 m4a 或 mp3码率控制在 128kbps。还放不下就把背景图和音效拆进 Bundle主包只留玩法必须的场景和脚本。判断主包体积的准确方式不是看整个build/wechatgame目录而是看assets子目录和src子目录的总和。5.5 每次改代码真机预览看到的还是旧版本现象代码改了一轮真机预览跑起来发现功能没变或者画面卡在最旧的资源版本。原因微信开发者工具和手机端都有资源缓存。工具缓存的是编译中间文件手机缓存的是已经下载的资源包。Cocos Creator 构建产物里的文件不带 hash 时同名文件会命中旧的微信缓存。解决开发阶段每次预览前在微信开发者工具菜单里执行「清除缓存 - 全部清除」再重新编译。正式发布时一定把 MD5 Cache 勾选上这样资源文件名带哈希值旧文件在微信服务器上无法命中玩家自动加载新版本——这是最稳妥的线上更新机制。6. 发布前用「真机三连」验证一次比跑十遍模拟器都管用有过一次深刻的翻车教训。某次我把一个改好的版本在模拟器里跑了几十遍感觉万无一失直接上传提审。结果审核人员在真机上打开发现游戏结束界面弹出的分数数字偏到屏幕外面整个 UI 被安全区挤压变形。问题本身不大但整套提审流程重走一遍的时间成本很高。从那以后我养成了一个强制习惯每次准备上传前都执行一遍「真机三连」验证。第一连微信开发者工具的「真机调试」模式打开手机扫码后在手机屏幕上操作桌面上同步看 Console 日志。重点观察有没有资源加载失败、空指针、警告信息。第二连在真机调试面板里打开性能监控盯两点帧率是否稳定在 30fps 以上Draw Call 是否在一个合理区间。小鸟这类 2D 轻量游戏Draw Call 超过 50 就说明有节点没有合批图片资源该做合图了。第三连关闭「不校验合法域名」用正式域名配置重新跑一遍真机预览——这一步模拟的是线上环境能提前发现资源域名校验问题。一套走完大约十分钟换来的是一份「这个包上传后大概率不会被退回」的信心。特别是经历过真机翻车之后我越来越认同一个观点模拟器跑通的只是黑匣子里的逻辑真机跑通的才是玩家实际体验。这期间也有后悔药可吃——微信开发者工具的「版本管理」功能可以回退到上一个上传的版本但回退会中断审核流程影响上线的时效。与其事后补救不如发布前把这三连当成固定的验收流程。希望帮到你。本文还有配套的精品资源点击获取