简介:面向 Cocos Creator 学习者的七款小游戏完整源码集合,涵盖 2048、小鸟躲避、黄金矿工、开心消消乐、跑酷、扫雷、飞机大战等可直接运行的项目,覆盖数字合并、障碍躲避、资源采集、消除匹配、横版跑酷、雷区推理、弹幕射击等典型玩法,可对应学习状态机、碰撞检测、动画控制、UI 事件与简单物理等常用开发技能。压缩包共包含 3978 个文件,以 json、meta、png、js、prefab、anim、mp3 等类型为主,其中 prefab 与 anim 便于查看场景搭建和动画配置,png、plist 与 mp3 分别对应图集与音效资源,整体体积 44.27MB,使用 Creator 2.3.2 可直接在浏览器查看运行效果。目前已有 6030 人学习下载。随包附带 Cocos 安装手册与游戏运行手册,可帮助新手快速完成环境配置并启动各项目;源码结构完整、模块划分清晰,既适合作为课程设计或毕业设计的参考,也便于在现有玩法基础上做二次开发,是理解完整小游戏项目组织与调试思路的实用素材。仅供学习交流,请勿滥用。
1. 拿到 CocosCreator 七个小游戏源码包:先别急着改代码
一套带有完整源码和使用手册的 CocosCreator 小游戏资源包,是很多人第一次接触 Creator 的入口。你可能是想做个毕业设计,可能是公司要在一周内出一版可演示的微信小游戏 demo,也可能只是想把“打地鼠”“飞机大战”这类经典玩法拆开看看。反直觉的结论是:这七个游戏的最大价值不是你把它原样跑起来,而是它提供了一条从“场景编辑器”到“微信小游戏打包”的最短路径——七个项目覆盖了触摸事件、碰撞检测、对象池、UI 刷新、音频播放这些 CocosCreator 必踩点,而使用手册真正该读的部分不是操作截图,而是每个游戏“参数在哪里改、资源怎么换”。这篇文章就按这个思路,把这个源码包拆到能直接复现的程度。
2. 先把环境跑通:版本选择、源码导入与命令行构建
2.1 版本匹配是第一个坑:Creator 2.x 与 3.x 不能混用
拿到源码包时,第一件事不是双击.scene文件,而是确认这个项目当初是用哪个 Creator 版本创建的。CocosCreator 2.x 和 3.x 的工程结构差异极大:2.x 的脚本用 JavaScript 编写,组件挂在节点上直接生效;3.x 换成了 TypeScript 为主,资源管线也改成了 Asset Bundle 体系。如果你用 3.8 直接打开一个 2.4 创建的旧项目,最常见的现象是场景黑屏、脚本全部提示“找不到”,甚至 Dashboard 里根本识别不了这个工程。
一个可靠的做法是:在 Creator 的 Dashboard 的“项目”标签页里点击“导入”按钮,选择源码包里的project.json所在目录。如果导入后版本号带黄色感叹号,说明版本不匹配。我一般会在本地同时保留 2.4.18 和 3.8.x 两个版本,先用 2.4 打开旧项目确认运行,再用 3.x 做新项目。这个“双版本保底”习惯能帮你省掉至少半天的排错时间。
2.2 导入源码后的目录识别:assets 和 build 先分清
项目打开后,你会看到 Creator 编辑器的资源管理器里有几个核心目录。assets是源码和资源的存放地,其中scenes里是.scene场景文件,scripts里是挂到节点上的组件脚本,res或resources里是图片、音频、字体等静态资源。如果你还看到build或web-mobile目录,那是别人构建后的产物,构建产物是可以直接删除的,它会在你重新构建时重新生成,不需要手动去改里面的文件。
使用手册里如果提到“修改代码后必须重新构建”,指的就是你改的是assets下的脚本,不是build里的 bundle。这里要养成一个习惯:改完脚本后先在 Creator 编辑器中按 Ctrl+Shift+B 预览,确认逻辑没问题再走构建流程,否则你反复手动拷贝构建产物,改十次会错九次。编辑器预览相当于一个随时刷新的小游戏模拟器,是排查问题最快的工具。
2.3 用命令行跑通一次微信小游戏构建
如果你最终目标是发微信小游戏,图形界面里点“构建发布”不是唯一路径,命令行模式才是可复现、可集成到 CI 的方式。CocosCreator 提供了命令行构建参数,常见做法是在项目根目录执行类似下面的命令(以 2.x 为例):
/Applications/CocosCreator/Creator/2.4.18/CocosCreator.app/Contents/MacOS/CocosCreator \ --project /Users/yourname/workspace/SevenMiniGames \ --build "platform=wechat;debug=true;buildMode=debug"参数说明:--project指向项目根目录,也就是包含project.json的那一层;--build后面跟分号分隔的构建参数,platform=wechat指定构建目标为微信小游戏,debug=true会生成带源码映射的调试包,方便在微信开发者工具里打断点。构建完成后,产物默认输出到build/wechatgame目录,直接用微信开发者工具打开这个目录即可预览。如果你用的是 3.x,命令格式略有差别,但核心思路一致:先打开终端,确认弹出的 Creator 进程是同一个版本,再去看build日志——日志文件在项目目录下的temp/building里,大多数构建失败原因都能在里面找到。
3. 七个游戏逐个拆解:每个项目都能学到什么
3.1 打地鼠:触摸事件与随机生成的节奏控制
打地鼠是源码包里最适合入门的一个。地鼠从洞里冒出来不是随机瞬间出现,而是带延迟的伪随机序列,这样才能保证玩家有反应时间。核心脚本里通常会有类似下面的逻辑,控制“地鼠出现”和“地鼠缩回”的循环:
startSpawn() { this.schedule(this.spawnOne, this.spawnInterval); } spawnOne() { let holes = this.holes.filter(h => !h.isOccupied); let target = holes[Math.floor(Math.random() * holes.length)]; target.isOccupied = true; target.node.active = true; target.node.scale = 0; cc.tween(target.node) .to(0.15, { scale: 1 }) .delay(this.showDuration) .to(0.1, { scale: 0 }) .call(() => { target.node.active = false; target.isOccupied = false; }) .start(); }逻辑说明:schedule是 Creator 内置的定时器,按spawnInterval秒间隔生成一次地鼠;spawnOne里先筛选没被占用的洞,再随机选一个,用cc.tween做弹跳动画,showDuration控制地鼠停留时长。参数说明:spawnInterval是生成间隔,初期设 0.8 秒左右比较友好,高手局可以改到 0.4 秒;showDuration是地鼠露面时间,默认 1 秒会比较简单,调到 0.6 秒难度立刻上来。这个脚本是七个游戏里最简单的定时器+动画组合,读懂了它,后面所有“节奏感”玩法都能套用。
3.2 飞机大战:对象池与碰撞检测
飞机大战是源码包里最有“含金量”的项目,因为它同时涉及对象池、帧更新和碰撞检测。子弹每帧生成新节点会导致内存抖动和卡顿,所以成熟写法是预创建一个子弹池,子弹用完不销毁,而是隐藏并回收到池里:
const BULLET_POOL_SIZE = 30; this.bulletPool = new cc.NodePool(); for (let i = 0; i < BULLET_POOL_SIZE; i++) { let bullet = cc.instantiate(this.bulletPrefab); this.bulletPool.put(bullet); } fire() { if (this.bulletPool.size() <= 0) return; let bullet = this.bulletPool.get(); bullet.setPosition(this.player.x, this.player.y + 50); bullet.active = true; bullet.getComponent('Bullet').move(); }逻辑说明:cc.NodePool是 Creator 自带的节点池工具,put回收节点,get从池中取出,池为空时提前返回避免报错。每次取出的子弹要手动设置位置和active,因为它从池里复用时还带着上次的位置。参数说明:子弹池大小BULLET_POOL_SIZE决定同一屏最多子弹数,设置太小会出现“发射没反应”的假卡顿,设置太大则占用内存,30 到 50 是个合理区间。碰撞检测在源码里一般用两个 2D 碰撞框配合onCollisionEnter回调实现,需要确保两个节点分别挂载了碰撞组件并且分组正确,否则子弹会“穿过”敌机。
3.3 2048 与消除类:数组运算和 UI 刷新的关系
2048 这类游戏考验的不是动画,而是棋盘状态的管理。核心是 4x4 数组的操作,以及合并后如何刷新 UI。源码里通常有一个mergeLine函数来压缩一行并合并相邻相同数字:
mergeLine(line) { let arr = line.filter(v => v !== 0); for (let i = 0; i < arr.length - 1; i++) { if (arr[i] === arr[i + 1]) { arr[i] *= 2; arr.splice(i + 1, 1); this.score += arr[i]; } } while (arr.length < 4) arr.push(0); return arr; }逻辑说明:先过滤掉 0,把非零数字压缩到一边,再从前到后合并相邻相同值,最后补齐 0。这种方法比逐格比对的写法清晰得多,也方便你修改成“金币消除”或“方块合成”的玩法。参数说明:this.score是全局分数,合并时累加,分数刷新直接改 UI Label 的string属性即可。消除类游戏最容易翻车的地方是“合并后产生的新值又参与合并”的误判,这一步通过splice后立即退出当前索引的循环规避,属于经典写法。
3.4 跑酷和跳跃类:手写位移还是物理引擎
源码包里如果有跑酷或跳跃类游戏,你会看到两种实现思路。第一种是直接修改节点的position实现位移,适合简单的左右跳跃;第二种是给主角挂cc.RigidBody和cc.BoxCollider,用物理引擎处理重力与碰撞。对初始阶段来说,手写位移更容易控制手感,代码看起来可能是这样:
update(dt) { if (!this.isJumping) { this.vy = this.jumpForce; this.isJumping = true; } this.vy -= this.gravity * dt; this.node.y += this.vy * dt; if (this.node.y <= this.groundY) { this.node.y = this.groundY; this.vy = 0; this.isJumping = false; } }逻辑说明:每一帧用dt计算速度变化,先给一个初始向上的jumpForce,再每帧减去gravity * dt,最后贴地时归零。参数说明:jumpForce控制起跳高度,数值越大跳得越高;gravity控制下落速度,数值太大玩家会感觉“被拽下去”,太小则手感飘。我一般会调成jumpForce = 400, gravity = 900作为起步点,然后根据屏幕手感微调。如果你看到源码里用到了贝塞尔曲线,那通常是用在敌人的移动路径上,让怪物或障碍物走 S 形曲线,这个复杂度比手写抛物线高一档,但更有观赏性。
4. 使用手册里被忽略的部分:把套件改成你自己的游戏
4.1 替换游戏资源:图片和音频的路径规则
使用手册一般会教你怎么跑通 Demo,但很少强调“如何替换资源而不改代码”。CocosCreator 的资源引用不是靠文件名,而是靠导入时生成的.meta文件里的 UUID。这意味着你随便删掉旧的 PNG 再放一张同名的 PNG,是接不上的。正确做法是在资源管理器里选中图片,按 Delete 删掉旧资源,再把新图片拖进资源管理器,然后把 Inspector 面板里 SpriteFrame 拖拽到场景节点的Sprite组件上。这样引用关系才重建成功。
音频替换同理。注意 Creator 对音频格式有要求:背景音乐用.mp3或.ogg,音效用.wav更稳定,长音乐用.mp3体积更小。替换后一定要在编辑器中双击播放确认,有些问题只在真机上出现,比如微信小游戏不支持直接播放本地音频文件的大小限制。
4.2 改游戏参数表和脚本顶部的配置区
七个小游戏源码包的另一个价值是参数集中配置。你会在脚本顶部看到像是SPEED = 200、MAX_LIFE = 3、SPAWN_INTERVAL = 0.8这样的常量定义,改这些值就能调整游戏难度和节奏。这是代码里最值得动手的“安全区域”,因为改动风险最低,效果却立竿见影。常见做法是把它们提成一个全局配置对象,方便统一管理:
const GameConfig = { speed: 200, maxLife: 3, spawnInterval: 0.8, soundEnabled: true, difficulty: 'normal' };逻辑说明:把所有可变参数集中到一个对象里,后续做难度选择时只需要在difficulty变化时重新赋值。参数说明:speed影响角色移动,spawnInterval影响怪物刷新频率,这些参数与场景速度的配合需要真机测试,不同手机性能不同,应在中端机型上作为基准调参,而不是只在高端机上看效果。
4.3 接入微信小游戏开放数据域:排行榜与分享
源码包如果面向微信小游戏,通常已经预留了开放数据域的接口,但手册不一定写清楚。微信小游戏有“主域”和“开放数据域”之分,排行榜相关的wx.getFriendCloudStorage只能在开放数据域里调用,而主域不能直接访问那里面的 Dom。接入时你需要单独构建一个开放数据域子包,并在game.json里声明openDataContext路径。
常见的做法是把排行榜绘制成一张 Canvas 贴图,再传给主域显示。代码大致结构是:
// 开放数据域脚本 open-data-context.js wx.getFriendCloudStorage({ keyList: ['score'], success: res => { let ranked = res.data.sort((a, b) => b.KVDataList[0].value - a.KVDataList[0].value); this.renderRankList(ranked); } });逻辑说明:这个脚本运行在开放数据域独立进程中,不能直接用主域的cc对象。keyList是榜单排行榜的键名,必须和主域上报分数时使用的 key 一致,漏了任何一端都查不到数据。参数说明:ranked排序逻辑是按分数降序排列,如果你要按“关卡层数”或“金币数”排,这里的value解析逻辑需要对应修改。分享功能则简单得多,主域代码里通过wx.shareAppMessage监听用户主动分享即可,不需要做复杂设置。
5. 从源码到上线:四个常见坑位与排查思路
5.1 坑一:用新版 Creator 打开旧项目黑屏
现象是项目能导入,但场景编辑器黑乎乎一片,或者运行时所有节点都不渲染。原因通常是 2.x 项目的渲染管线与 3.x 不兼容,旧版本的Canvas组件被新版本移除,导致渲染入口丢失。解决方法是先用旧版本 Creator(2.4.x)打开项目,如果需要迁移到 3.x,可以先用 Creator 3.x 的导入工具生成迁移后的新工程,再手动检查每个场景里的Canvas组件是否被正确替换为UI Canvas。不要在原工程上反复尝试“重新保存”,那是浪费时间。
5.2 坑二:构建出的微信小游戏包超过 4MB 主包限制
现象是微信开发者工具上传时报包体超限,且无法预览。原因往往是首场景里引用了所有音频、图片资源,尤其是大尺寸背景图。解决思路有两条:并行处理压缩和分包。先在构建配置里打开“MD5 缓存”并开启“压缩纹理”,把 PNG 转成 WebP 或 JPG;然后把非核心场景的资源放进 Asset Bundle,并勾选“按需加载”。最直接的检验方法是看构建日志里生成的config.json中各个 bundle 的体积占比,哪个包大就对哪个做资源瘦身。
5.3 坑三:不同手机上画面变形或被裁切
现象是同样一套代码,iPhone 上正常,安卓刘海屏上上下被裁了一截,横屏游戏还可能左右黑边。原因是设计分辨率与真机屏幕宽高比不一致,而项目里的 Canvas 适配策略只开了Fit Height或Fit Width一个选项。解决方式是勾选 Canvas 的Fit Width和Fit Height同时开启,再用Widget组件把关键 UI 锚定到安全区域。注意背景图要大于设计分辨率 120% 以上,否则适配后边缘会露白。这个坑在七个小游戏里最容易出现在跑酷类游戏的地面背景上。
5.4 坑四:音频短间隔重复播放导致卡顿
现象是连续点击按钮时,第一秒有声音,之后声音变得断断续续,甚至整个节点卡住。原因是每个音效都重新cc.audioEngine.playEffect创建了新的音频实例,短间隔高频播放时超过了系统限制。解决方式是给每个音效单独预加载,并且同一时刻最多复用两个音频实例,播放前先stopEffect再playEffect。微信小游戏端还额外建议把短音效转换成mp3格式,并将文件的码率压到 64kbps 以下,对听感影响可以接受,性能改善明显。
5.5 坑五:源码包里的脚本互相引用,删掉一个就报一连串错
现象是删掉一个不用的脚本后,运行时报 “require not found”,查看日志发现十几个文件都在引用它。原因是小游戏源码包的脚本之间通过require或import建立了隐式依赖,删除前必须先查引用关系。解决方式是在资源管理器里右键要删除的脚本,选择“查找引用”,确认没有其他节点使用后再删。如果确认删错了,可以用版本管理工具找回——这也是我反复强调的,拿到源码包第一件事是git init并提交一版,避免改坏了没后悔药。
6. 进阶玩法:把七个独立游戏整合成一个“游戏合集”
当七个游戏你已经能分别跑通,下一步自然是想把它们合成一个入口灵活切换合集。直接复制七个项目然后做跳转不是好办法,我这里给出一套能落地的方案:建一个大厅场景,大厅里放七个按钮,每个按钮绑定一个场景跳转逻辑,跳转前做场景预加载。
startGame(sceneName) { cc.director.preloadScene(sceneName, () => { cc.director.loadScene(sceneName); }); }逻辑说明:preloadScene会提前加载目标场景的场景资源和依赖,加载完成后才执行loadScene,这样切换时不会出现 1~2 秒的白屏卡顿。参数说明:sceneName要严格对应构建配置里添加的场景名字,如果漏在构建配置里添加,运行时就会报Scene [xxx] cannot be loaded。大厅里的七个按钮最好用同一个“按钮管理”脚本通过style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />