
简介微信小程序“合成大西瓜”的云开发版源码包含完整小游戏工程与流量主接入逻辑。游戏将经典合成玩法与俄罗斯方块式下落、消消乐元素结合用户只需拖动同样的水果让其碰撞合成逐步挑战最终的大西瓜操作简单、反馈直接适合新手快速上手体验也适合微信小游戏开发者和站长借鉴学习。资源内置流量主功能当玩家使用吸铁石或万能球道具时需观看激励视频广告可帮助已开通流量主的账号获得广告收益。包体共91个文件压缩包大小约2.93MB主要包含png游戏素材、json配置与场景文件、js核心逻辑代码、mp3音效以及安装教程、云服务器推荐等说明文档结构清晰导入微信开发者工具即可运行无需额外服务器和域名。目前已有1242人学习下载源码覆盖从界面资源、游戏逻辑到广告变现的完整链路便于二次开发与学习研究。1. 把“合成大西瓜”小游戏跑起来最关键的不是玩法而是工程结构拿到这份synthesis_watermelon-main微信小游戏源码时最容易看漏的是它并不是一个需要后端支撑的小程序而是一套 Cocos Creator 导出的微信小游戏工程。它把“水果从上方掉落相同水果碰撞后合成更高级水果”的玩法直接跑在客户端game.js负责启动assets存素材project.config.json存 appid。也就是说你的关注点其实要放在两处一个是合成玩法在微信运行时里怎么被算出来另一个是变现用的流量主激励视频挂在哪个交互点上。这段源码适合两类人一类是想快速上线“合成大西瓜”换皮产品的小程序开发者另一类是学习微信小游戏资源结构、物理合成和广告接入的进阶选手。后面会先从文件骨架拆起再给出可抄的代码实现。2. Cocos 构建产物与微信小游戏运行时的文件骨架解压后不要急着拖进微信开发者工具先看一遍顶级目录。synthesis_watermelon-main这一层里的文件顺序很典型game.js放在根目录作为小游戏入口game.json声明窗口和设备方向ccRequire.js是 Cocos Creator 构建时生成的模块加载器adapter-min.js把 Canvas、WebAudio 这些浏览器 API 映射到微信运行时。这套东西不是wxmljs的页面小程序而是“小游戏”类型导入时选错类型会直接报“找不到 game.js”。2.1 各文件的作用及可以手动改的位置文件/目录这个文件干了什么需要手动改吗game.js微信小游戏的启动入口加载引擎和主逻辑一般不改最多加调试输出game.json声明窗口尺寸、设备方向、子包列表可改竖屏/横屏和分辨率策略ccRequire.jsCocos 构建产物的模块注册与依赖加载器不碰adapter-min.js把微信 API 适配成浏览器/Cocos 需要的环境不碰main.js引擎启动后的初始化流程通常包含cc.game.run()可在run()后挂自定义脚本project.config.json工程配置包含 appid、编译类型、libVersion必须换成你自己的 appidproject.private.config.json开发者本地私有配置会覆盖公共配置只在调试时改别提交assetsCocos 的原始资源目录场景、脚本、贴图、音频都在这里替换素材和调整逻辑的地方img直接放置在项目根目录的图片素材早期版本常用替换水果贴图时可用这份文件关系看着简单但有一个容易踩的坑很多人直接改game.json里的appid以为跟页面小程序一样。微信小游戏不是这样的小游戏的项目 ID 在project.config.json的appid字段里game.json只负责运行时表现。比如game.json里常见的deviceOrientation设置为portrait表示强制竖屏改成landscape后整个游戏就会横过来。2.2 appid 与项目配置微信小游戏工程的 appid 不在game.json而在project.config.json。这份源码里它大约在第 50 行默认值通常是appid: touristappid。微信开发者工具可以用游客模式打开但你一旦要使用流量主就必须换成自己的小游戏 appid否则激励视频广告无法调试。project.config.json核心字段长这样{ description: 合成大西瓜 synthesis_watermelon, setting: { urlCheck: false, es6: true, minified: true }, appid: wx1234567890abcdef, compileType: game, libVersion: 2.30.0 }compileType固定是game不要改成miniprogram否则微信开发者工具不会走小游戏加载链路。libVersion表示基础库版本如果广告 API 或物理 API 不起作用先检查这里是不是被改成了一个太旧的版本。urlCheck是关掉合法域名校验的开关本地方便但如果以后接了自己的云函数或接口记得按需打开。如果你有project.private.config.json它也会携带一份appid并覆盖公共配置。所以只改project.config.json无效时别疑惑检查一下project.private.config.json里有没有残留旧 appid。直接用命令批量替换也可以# 备份后把 touristappid 替换成你自己的 appid cp project.config.json project.config.json.bak sed -i s/appid: touristappid/appid: wx1234567890abcdef/ project.config.json这个命令的含义是先把原配置备份成.bak再在同一文件内把touristappid替换成真实 appid。执行后再打开微信开发者工具在项目详情里看到 appid 已经是自己的才说明替换成功。2.3 game.js 启动链路与调试入口微信小游戏的执行顺序跟浏览器页面完全不同微信加载game.json后先执行根目录的game.js。Cocos 导出项目里game.js通常只有三条 require 语句// game.js 入口列表顺序不能乱 require(./adapter-min.js); // 先把微信 API 适配成浏览器环境 require(./ccRequire.js); // 再注册 Cocos Creator 的模块依赖表 require(./main.js); // 最后启动引擎和游戏场景第一条是为了让引擎里用到的window、document、canvas等对象在微信小游戏环境里能跑起来第二条是把 Cocos 的所有脚本模块注册进 CommonJS 运行时做到按需加载第三条才是真正进入cc.game生命周期。理解这个顺序之后你就知道微信开发者工具的控制台里报错时应该先看哪一层。如果你要验证入口脚本有没有正常加载最省事的做法是在main.js顶部加一句console.log([synthesis] main loaded)编译后看 vConsole 是否有输出。如果只有adapter-min.js的输出而没有main.js说明问题出在模块注册阶段优先检查ccRequire.js是否被破坏或assets/scripts里有没有语法错误。3. 合成规则、拖拽落点与碰撞合并的防抖实现合成大西瓜看似是物理游戏核心只有三块水果等级映射、投掷落点计算、碰撞后的合并判定。只要看懂这三块换素材、换难度都是分钟级。3.1 水果等级与合成目标映射常见版本从低到高是葡萄、橘子、柠檬、猕猴桃、番茄、桃子、菠萝、椰子、半西瓜、大西瓜。在这份源码里这些素材放在assets或img目录下脚本中通常只存整数level与缩放倍数不直接读图片名。这么做的好处是合成逻辑只关心数字不关心图片后期换“苹果版”“动物版”素材时脚本完全不用动。level水果缩放/半径倍数二次合成目标1葡萄0.5222橘子0.6233柠檬0.7244猕猴桃0.8255番茄0.8666桃子0.9477菠萝1.0088椰子1.0899半西瓜1.151010大西瓜1.25无表格里的缩放倍数会直接影响物理碰撞半径。比如level 1葡萄的半径只有世界基准值的 0.52而level 9半西瓜已经到 1.15。合成新水果时用nextLevel fruit.level 1查下一级并同时把碰撞半径乘上下一个缩放倍数。不需要为每种水果单独写一个“变大函数”只维护这个映射表就够了。这里有个容易被忽略的细节level 10 的大西瓜不应该参与合成所以代码里要给MAX_LEVEL一个上限判断。如果直接把level 1写成无条件执行大西瓜与另一个大西瓜相撞时会生成一个不存在的level 11表现通常是生成一个全黑或透明的水果贴图还会把物理世界里的碰撞半径撑得过大。3.2 拖拽与落点的坐标换算在 Cocos 加微信小游戏环境里屏幕坐标和游戏世界坐标是两个坐标系。手指在手机屏幕上触摸到的clientX不能直接当成游戏世界里的 x 坐标使用需要做一次比例换算同时限制左右边界避免水果落到 UI 按钮下面或者掉出屏幕。// 以引擎宽 GAME_WIDTH、屏幕宽 window.screenWidth 为例 function screenToGameX(clientX) { const ratio GAME_WIDTH / window.screenWidth; let x clientX * ratio; const halfRadius CURRENT_FRUIT_RADIUS / 2; const minX halfRadius; const maxX GAME_WIDTH - halfRadius; return Math.min(Math.max(x, minX), maxX); }ratio解决的是不同手机屏幕宽度不一致的问题。iPhone 和 Android 的物理像素不同但游戏世界坐标系固定是设计分辨率比如 750所以必须把真实触摸点映射到设计分辨率。halfRadius保证生成的水果不会半颗身子卡在屏幕外否则水果一半悬空一半卡墙后续碰撞合并表现会非常怪。手势监听一般放在wx.onTouchStart和wx.onTouchEnd里touchstart记录一个瞄准横线touchend时取当前clientX调用screenToGameX然后在该 x 位置生成水果并给一个向下的加速度。很多新手直接监听touchmove生成水果结果一次拖动生成了五六个水果这里要区分“瞄准移动”和“松手落子”两个动作。3.3 碰撞合并的防抖与替换顺序物理引擎每帧都在做碰撞检测。如果不加防抖两个水果碰撞时回调可能被触发两次导致level1被多算一遍、生成两个高等级水果。常见做法是给每个水果实体挂一个merged标记并且在合并时先关掉两个旧 body再延迟卸载节点。// 碰撞回调里只做合并登记下一帧再替换实体 function onPairCollide(a, b) { if (a.mergeTag ! b.mergeTag) return; // 等级 tag 不一致不合并 if (a.merged || b.merged) return; // 其中一个已参与合并就跳过 const nextLevel a.level 1; const midX (a.pos.x b.pos.x) * 0.5; const midY (a.pos.y b.pos.y) * 0.5; a.merged true; b.merged true; physics.removeBody(a.body); physics.removeBody(b.body); const newFruit spawnFruit(nextLevel, midX, midY); newFruit.body.setVelocity(0, 60); // 给一点 Y 向速度防止卡在底层 }mergeTag是判断两种水果是否同一品种的关键字段它可以直接等于level也可以用imageName。用level更简单因为合成逻辑本来就是数字相加。merged标记是防抖的核心一旦某个水果参与合并即使碰撞回调再触发一次也会因为merged true被跳过。midX/midY取两个碰撞体的中点新水果从这里生成视觉上就像两个水果长成了一个更大的。如果你用的是 Box2D 系列物理引擎API 会变成SetLinearVelocity思路完全一样拿到目标刚体设置一个 Y 方向小速度防止新旧刚体重叠时发生瞬时爆炸。注意不要在碰撞回调里同步销毁节点因为物理引擎内部还在遍历刚体列表直接destroy可能触发野指针正确做法是先removeBody再在update里完成节点销毁。另外合成后的新水果最好做一个“短暂不可再合并”保护。比如给newFruit设置一个mergeCooldown 0.5秒在update中递减。否则新生成的水果如果在同帧又碰到另一个同等级水果会瞬间产生连环合成一把游戏不到五秒就合成大西瓜难度直接崩掉。部分“合成版”源码里故意保留这种连锁是为了爽快感但如果你要做长线留存建议加这个冷却。4. 流量主激励视频接入吸铁石与万能球的广告变现链路这份资源最值钱的地方不是玩法而是已经接好的流量主变现功能。用户使用吸铁石和万能球时需要观看激励视频广告广告完整播完后道具才解锁。这个链路看着简单但背后有几个容易漏掉的点。4.1 先确认流量主开关再说广告接入广告前要明白流量主不是所有小程序默认就有的。没有开通流量主或没有广告位时wx.createRewardedVideoAd会直接抛错所以代码里不能裸调必须先判断当前环境是否允许展示广告。场景表现处理未开通流量主createRewardedVideoAd报错隐藏按钮或直接放道具广告拉取超时用户点击后无反应走降级逻辑用户中途关闭视频onClose回调res.isEnded false不发放道具用户完整观看res.isEnded true发放道具最稳妥的方式是在游戏配置里放一个isAdEnabled开关。你自己测试时关掉上架前打开也可以从后台接口下发。这样即使微信官方调整了流量主准入规则客户端也不会因为广告接口崩溃导致整个游戏白屏。4.2 一条广告实例给两个道具复用微信小游戏里同时创建多个RewardedVideoAd实例不是不行但没必要。吸铁石和万能球共用同一个实例减少内存也避免重复加载广告。关键是onClose回调要先用offClose清掉上次监听再挂新的防止同一个视频关闭后弹出两次奖励。let rewardedVideoAd null; const AD_UNIT_ID adunit-xxxxxxxxxxxx; function ensureRewardedVideo() { if (rewardedVideoAd) return rewardedVideoAd; rewardedVideoAd wx.createRewardedVideoAd({ adUnitId: AD_UNIT_ID }); rewardedVideoAd.onError((err) { console.warn(广告加载失败, err); }); return rewardedVideoAd; } function watchAdForItem(itemName) { const ad ensureRewardedVideo(); ad.show() .catch(() ad.load().then(() ad.show())) .catch(() { unlockItem(itemName); // 广告加载失败时降级不能让用户卡死 }); ad.offClose(); // 先清掉上一次回调避免重复发放 ad.onClose((res) { if (res res.isEnded) { unlockItem(itemName); } }); }adUnitId是你自己在微信公众平台流量主后台创建的广告单元 ID不是 appid。很多第一次接流量主的人会把广告位 ID 填成小游戏 appid结果控制台一直报adUnitId invalid。show()返回的是 Promise如果广告还没加载完成第一次show会 reject所以要 catch 后手动调load()再show()。res.isEnded是判断“是否完整观看”的唯一依据用户中途退出时不能发奖励。提示offClose在onClose前面调用顺序不能反过来。先 off 再 on能保证回调列表里只有一个处理函数。4.3 道具发放与本地次数存储道具发放不能只加一个全局变量因为小游戏在低内存手机上随时可能被微信回收重新进入游戏后道具就没了。至少要落到本地缓存wx.setStorageSync。function unlockItem(item) { const key synthesis_watermelon_items; const items wx.getStorageSync(key) || {}; items[item] (items[item] || 0) 1; wx.setStorageSync(key, items); refreshItemUI(item, items[item]); }wx.getStorageSync读不到时返回空对象所以用|| {}兜底。items[item]用|| 0防止第一次读取到 undefined。refreshItemUI负责更新游戏界面左上角的道具角标这样用户看完广告后立刻能看到数量变化不会产生“广告看了但道具没到账”的投诉。吸铁石和万能球在玩法层的作用是错开的吸铁石会把当前场上可合并的同等级水果配对并拉近制造一次或多组碰撞属于“提高合成效率”的辅助道具万能球则是生成一个能匹配任何水果的高等级前置水果碰到任意水果都能触发一次合成属于“保底容错”道具。把这两个道具放在广告后面就是典型的“用爽点换留存用广告换收益”设计。从收益角度看建议给两个道具加每日使用次数上限。比如每天免费看广告次数不超过 10 次超过后按钮置灰并提示“今日次数已用完”。否则单个重度用户会无限刷广告虽然广告收入增加但对用户体验损耗很大次日留存可能反而下降。5. 部署、参数调优与道具验收从touristappid到可上线5.1 用微信开发者工具导入小游戏打开微信开发者工具选择“小游戏”类型点击“导入项目”目录选中synthesis_watermelon-main。首次导入时如果提示 appid 无效先按第 2 章的命令替换 appid再重新导入。注意不要选择“小程序”类型否则工具会去找app.json这个工程里只有game.json直接报错。如果你确定要上线还需要在game.json里确认设备方向和 UI 安全区{ deviceOrientation: portrait, showStatusBar: false, networkTimeout: { request: 10000 } }deviceOrientation保持portrait就可以合成大西瓜这类下落玩法竖屏操作最自然。showStatusBar在小游戏里控制是否显示系统状态栏如果你的 canvas 没有适配刘海屏建议保留false用自己的 UI 顶部遮罩处理。5.2 常见报错与排查方向报错/现象原因解决appid is not validproject.config.json或私有配置文件里 appid 未替换两处都改成自己的 appidCan not find module cocos导入路径不是game.js所在目录选根目录确保game.js在项目根层级RewardedVideoAd is not defined广告 API 不可用或流量主未开通加typeof wx.createRewardedVideoAd function判断水果错位/贴图全黑缓存库和实际资源 UUID 不匹配删除根目录library和temp后重新编译物理穿透严重物理步长迭代次数太低或掉帧调低水果下落间隔检查是否有大数组频繁遍历5.3 调参点与素材替换技巧参数通常位置调整效果水果生成间隔生成器定时器setSeconds间隔越长越简单越短越难合成等级上限MAX_LEVEL控制是否能合成大西瓜物理迭代次数物理引擎初始化参数提高精度但会吃性能吸铁石吸附范围道具脚本里的半径阈值范围越大一次广告带来的合成越多替换水果素材时不建议直接在微信开发者工具里删掉旧图拖入新图因为 Cocos 的资源 UUID 变了会导致加载失败。正确做法是新图片导出为 PNG保持原文件名和原目录结构覆盖assets里的对应图片文件然后删除library和temp两个缓存目录再重新编译。这个流程比重新打开工程快也最不容易出现黑屏。改完参数后花一局时间把所有道具完整触发一遍确认吸铁石和万能球都在视频完整观看后发放、中途退出不发放再提交审核。本文还有配套的精品资源点击获取