36 KiB
报恩榴莲 - 完整项目开发指南
从零基础到微信小游戏上架的完整开发流程文档
📋 目录
一、项目概述
1.1 游戏简介
报恩榴莲是一款三三合成休闲小游戏,玩家通过点击屏幕掉落水果,三个相同等级的水果自动合成为更高等级的水果,最终目标是合成传说中的"报恩榴莲"来投喂Boss。
- 平台:微信小游戏
- 引擎:Cocos Creator 3.8.x + TypeScript
- 类型:休闲 / 合成 / 物理模拟
- 时长:单局 1-3 分钟
- 变现:纯广告(激励视频 + 插屏)
1.2 核心玩法
1. 点击屏幕顶部 → 水果从指定X坐标掉落
2. 三个相同等级水果接触 → 自动合成为高一级水果
3. 合成高等级水果 → Boss饱食度增加
4. Boss饱食度满 → 通关,进入下一关
5. 水果超过警戒线 → 失败,可选择看广告复活
1.3 技术亮点
- ✅ 纯逻辑层分离:MergeCore 无任何 Cocos 依赖,可独立单元测试
- ✅ 组件化架构:Fruit、BossManager、GoldenLegendEffect 等独立组件
- ✅ 单例模式:AudioManager、SaveManager 全局唯一实例
- ✅ 对象池优化:音效 AudioSource 复用,避免频繁创建销毁
- ✅ 异步加载:音频资源按需加载,减少首屏加载时间
- ✅ 存档系统:localStorage 持久化,支持断点续玩
二、开发环境准备
2.1 必需软件
| 软件 | 版本要求 | 下载地址 |
|---|---|---|
| Cocos Creator | 3.8.0+ | https://www.cocos.com/creator-download |
| Node.js | 14.x+ | https://nodejs.org/ |
| VS Code | 最新版 | https://code.visualstudio.com/ |
| 微信开发者工具 | 稳定版 | https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html |
2.2 Cocos Creator 安装
- 下载 Cocos Creator 3.8.x 安装包
- 运行安装程序,选择安装路径
- 首次启动需登录 Cocos 账号(可使用 GitHub 快捷登录)
- 在 Dashboard 中确认版本号 ≥ 3.8.0
2.3 VS Code 插件推荐
安装以下插件提升开发效率:
- Cocos Creator API - Cocos API 智能提示
- TypeScript Importer - 自动导入 TS 模块
- ESLint - 代码规范检查
- Prettier - 代码格式化
2.4 环境变量配置(可选)
如果需要调用外部 API(如排行榜),在项目根目录创建 .env 文件:
API_BASE_URL=https://your-api.com
APP_ID=wx1234567890abcdef
三、项目结构说明
3.1 目录树
cocos-prototype/
├── assets/ # ★ 资源目录(Cocos 识别)
│ ├── scripts/ # TypeScript 脚本
│ │ ├── config/ # 配置文件
│ │ │ └── GameConfig.ts # 游戏数值配置(水果链、关卡、物理参数)
│ │ ├── core/ # 核心系统
│ │ │ ├── GameManager.ts # 游戏主控制器
│ │ │ ├── MergeCore.ts # 纯逻辑合成引擎
│ │ │ ├── AudioManager.ts # 音频管理器
│ │ │ ├── SaveManager.ts # 存档管理器
│ │ │ └── AdManager.ts # 广告管理器
│ │ ├── components/ # 组件脚本
│ │ │ ├── Fruit.ts # 水果组件
│ │ │ ├── BossManager.ts # Boss管理组件
│ │ │ └── GoldenLegendEffect.ts # 金色传说特效组件
│ │ └── ui/ # UI脚本
│ │ ├── MainMenuUI.ts # 主菜单
│ │ ├── ResultPanel.ts # 结算面板
│ │ └── CollectionUI.ts # 图鉴界面
│ ├── textures/ # 图片资源
│ │ ├── fruits/ # 9个水果精灵
│ │ ├── boss/ # Boss图片
│ │ ├── ui/ # UI元素
│ │ └── effects/ # 特效图片
│ ├── audio/ # 音频资源
│ │ ├── bgm_*.mp3 # BGM
│ │ ├── sfx_*.mp3 # 音效
│ │ └── voice_*.mp3 # 语音
│ ├── prefabs/ # 预制体
│ │ ├── Fruit.prefab # 水果预制体
│ │ └── Boss.prefab # Boss预制体
│ └── scenes/ # 场景文件
│ └── MainGame.scene # 主游戏场景
│
├── settings/ # 项目设置
│ └── v2/packages/... # Cocos 内部配置
│
├── build/ # 构建输出(自动生成)
│
├── SCENE_SETUP_GUIDE.md # 场景搭建指南
├── PROJECT_GUIDE.md # 本文档
└── README.md # 快速入门
3.2 关键文件职责
| 文件 | 职责 | 修改频率 |
|---|---|---|
| GameConfig.ts | 所有数值配置(水果属性、关卡数据、物理参数) | 低(调优时修改) |
| GameManager.ts | 游戏流程控制、状态切换 | 中 |
| MergeCore.ts | 合成逻辑核心 | 极低(稳定后不改) |
| AudioManager.ts | 音频播放管理 | 低 |
| SaveManager.ts | 存档读写 | 极低 |
| AdManager.ts | 广告SDK封装 | 低 |
| Fruit.ts | 水果动画表现 | 中 |
| BossManager.ts | Boss饱食度逻辑 | 低 |
四、快速开始(5分钟运行)
步骤 1:导入项目
- 打开 Cocos Creator 3.8
- 点击 "打开其他项目"
- 选择
cocos-prototype文件夹 - 等待资源导入完成(首次约 1-2 分钟)
步骤 2:创建场景
- 在资源管理器中右键
assets/scenes/→ 新建 → Scene - 命名为
MainGame - 双击打开场景编辑
步骤 3:搭建场景节点
按照 SCENE_SETUP_GUIDE.md 中的节点层级结构创建节点:
最简版本(仅测试核心玩法):
Canvas
└── GameScene
├── GameContainer (Node)
│ ├── LeftWall (Node + BoxCollider2D)
│ ├── RightWall (Node + BoxCollider2D)
│ ├── BottomWall (Node + BoxCollider2D)
│ └── FruitLayer (Node)
├── DropArea (Node + UITransform)
└── GameManagerNode (空节点,挂载 GameManager.ts)
步骤 4:制作 Fruit 预制体
- 右键
assets/prefabs/→ 新建 → Prefab → 命名Fruit - 双击进入预制体编辑
- 创建节点结构:
FruitRoot ├── FruitSprite (Sprite) ├── CircleCollider2D ├── RigidBody2D (Type=Dynamic, Fixed Rotation=true) └── Fruit.ts (脚本组件) - 为 FruitSprite 指定一个临时图片(可用 Cocos 默认 spriteFrame)
- 保存预制体
步骤 5:绑定 GameManager 属性
- 在场景中选中
GameManagerNode - 在属性检查器中点击 "添加组件" → 搜索
GameManager - 将以下节点拖拽到对应属性槽:
gameContainer→ GameContainer 节点dropArea→ DropArea 节点fruitLayer→ FruitLayer 节点fruitPrefab→ Fruit.prefab 资源
- 暂时留空其他属性(Boss、UI等后续补充)
步骤 6:运行测试
- 点击编辑器顶部的 "预览" 按钮(或按 Ctrl+P)
- 浏览器自动打开游戏页面
- 点击 DropArea 区域(场景顶部)
- 观察水果掉落和物理碰撞
如果看到水果掉落并堆积在底部,恭喜!核心玩法已跑通!
五、核心系统详解
5.1 MergeCore 合成引擎
位置:assets/scripts/core/MergeCore.ts
设计理念:纯逻辑层,无任何 Cocos 依赖,便于单元测试和移植。
核心数据结构:
interface FruitData {
id: string; // 唯一ID(如 "fruit_001")
level: number; // 等级(1-9)
x: number; // X坐标
y: number; // Y坐标
}
合成算法流程:
tryMerge() 被调用(通常在 Fruit 落地静止后)
↓
遍历所有水果,按 level 分组
↓
对每个等级,检查数量是否 ≥ 3
↓
如果是,取出3个水果(优先取高等级)
↓
计算3个水果的质心坐标(centroid)
↓
生成 MergeEvent 事件:
- removeIds: 要删除的3个水果ID
- newLevel: 新水果等级(原等级+1)
- newX, newY: 新水果位置(质心)
- score: 得分 = 3^(level-1) × comboMultiplier
↓
返回 MergeEvent[] 数组
Combo 连击系统:
// 在 GameManager 中维护
private _comboCount: number = 0;
private _lastMergeTime: number = 0;
private readonly COMBO_WINDOW = 2.0; // 2秒窗口
handleMergeEvent(event: MergeEvent) {
const now = Date.now() / 1000;
if (now - this._lastMergeTime < this.COMBO_WINDOW) {
this._comboCount++;
} else {
this._comboCount = 1;
}
this._lastMergeTime = now;
// 根据连击数获取倍率
const multiplier = getComboConfig(this._comboCount)?.multiplier || 1.0;
event.score *= multiplier;
}
连击倍率表:
| 连击数 | 倍率 | 音效 |
|---|---|---|
| 1 | 1.0x | sfx_merge_success |
| 2 | 1.2x | sfx_combo_2 |
| 3 | 1.5x | sfx_combo_3 |
| 4 | 2.0x | sfx_combo_4 |
| 5 | 3.0x | sfx_combo_5 |
| 6+ | 5.0x | sfx_combo_legend |
5.2 GameManager 主控制器
位置:assets/scripts/core/GameManager.ts
职责:
- 管理游戏状态(MENU / PLAYING / PAUSED / GAMEOVER / CLEARED)
- 处理玩家点击掉落水果
- 监听 MergeCore 的合成事件
- 检测失败条件(水果超过警戒线)
- 协调各子系统(AudioManager、SaveManager、BossManager等)
关键方法:
// 开始关卡
startLevel(level: number): void {
this._currentLevel = level;
this._state = GameState.PLAYING;
// 初始化各系统
AudioManager.getInstance().playBGM(BGM.MAIN);
SaveManager.getInstance().load();
this.mergeCore = new MergeCore();
this.bossManager.initBoss(...);
// 记录开始时间
this._levelStartTime = Date.now();
}
// 处理玩家点击
onDropAreaClick(event: EventTouch): void {
if (this._state !== GameState.PLAYING) return;
// 获取点击X坐标
const touchPos = event.getUILocation();
const x = touchPos.x - this.gameContainer.position.x;
// 在顶部生成水果
this.spawnFruitAt(x, this.dropArea.position.y);
// 播放掉落音效
AudioManager.getInstance().playSFX(SFX.DROP);
}
// 处理合成事件
handleMergeEvent(event: MergeEvent): void {
// 播放合成音效
AudioManager.getInstance().playSFX(SFX.MERGE);
// 更新连击
this.updateCombo(event);
// 检查是否是金色传说(level 9)
if (event.newLevel === 9) {
this.triggerGoldenLegend();
}
// 尝试投喂Boss
this.bossManager.tryFeed(event.newLevel);
}
// 触发金色传说
triggerGoldenLegend(): void {
// 切换BGM
AudioManager.getInstance().stopBGM();
AudioManager.getInstance().playBGM(BGM.GOLDEN);
// 播放语音
AudioManager.getInstance().playVoice(VOICE.GOLDEN_A);
// 播放特效
this.goldenEffectNode.getComponent(GoldenLegendEffect).play(
this.fruitLayer.position,
() => {
// 特效结束后恢复BGM
AudioManager.getInstance().stopBGM();
AudioManager.getInstance().playBGM(BGM.MAIN);
}
);
}
// 关卡通关
handleLevelClear(): void {
this._state = GameState.CLEARED;
// 计算星星
const stars = this.calculateStars();
// 奖励金币
const rewardCoins = stars * 10;
SaveManager.getInstance().addCoins(rewardCoins);
// 更新最佳成绩
SaveManager.getInstance().updateLevelBestScore(this._currentLevel, this._score);
// 显示结算面板
this.showResultPanel(true, stars);
}
// 游戏失败
handleGameOver(): void {
this._state = GameState.GAMEOVER;
// 播放失败音效
AudioManager.getInstance().playSFX(SFX.GAME_OVER);
// 显示结算面板(带复活按钮)
this.showResultPanel(false);
}
5.3 AudioManager 音频管理
位置:assets/scripts/core/AudioManager.ts
单例模式:
const audioMgr = AudioManager.getInstance();
audioMgr.playBGM(BGM.MAIN);
audioMgr.playSFX(SFX.MERGE);
音频池设计:
为了避免同时播放多个音效时的冲突,使用对象池复用 AudioSource:
private _sfxSources: AudioSource[] = [];
init(rootNode: any): void {
// 预创建3个音效音频源
for (let i = 0; i < 3; i++) {
const source = rootNode.addComponent(AudioSource);
source.loop = false;
this._sfxSources.push(source);
}
}
playSFX(sfxName: string): void {
// 从池中获取空闲的音频源
const source = this._sfxSources.find(s => !s.isPlaying);
if (!source) {
console.warn('[AudioManager] 音效音频源池已满');
return;
}
source.clip = loadedClip;
source.volume = this._sfxVolume;
source.play();
}
淡入淡出效果:
playBGM(bgmName: string, fadeIn: boolean = true): void {
if (fadeIn) {
this._bgmSource.volume = 0;
this._bgmSource.clip = clip;
this._bgmSource.play();
// 线性淡入
let volume = 0;
const targetVolume = this._bgmVolume;
const duration = 1.0;
const startTime = Date.now();
const fadeUpdate = () => {
const elapsed = (Date.now() - startTime) / 1000;
const progress = Math.min(elapsed / duration, 1);
volume = targetVolume * progress;
if (this._bgmSource) {
this._bgmSource.volume = volume;
}
if (progress < 1) {
requestAnimationFrame(fadeUpdate);
}
};
fadeUpdate();
} else {
this._bgmSource.clip = clip;
this._bgmSource.volume = this._bgmVolume;
this._bgmSource.play();
}
}
5.4 SaveManager 存档系统
位置:assets/scripts/core/SaveManager.ts
数据存储结构:
interface GameSaveData {
currentLevel: number; // 当前关卡
coins: number; // 金币数量
itemCounts: number[]; // 道具数量 [后悔药, 缩小灯, 万能果, 炸弹]
unlockedFruits: number[]; // 已解锁水果等级
levelBestScores: { [level: number]: number }; // 各关最佳分数
totalClears: number; // 总通关次数
totalFails: number; // 总失败次数
totalMerges: number; // 总合成次数
maxComboRecord: number; // 历史最大连击
lastPlayTime: string; // 最后游玩时间
}
读写操作:
// 加载存档
const data = SaveManager.getInstance().load();
console.log(`当前关卡: ${data.currentLevel}`);
console.log(`金币: ${data.coins}`);
// 保存存档
SaveManager.getInstance().save();
// 更新特定字段
SaveManager.getInstance().addCoins(100);
SaveManager.getInstance().recordClear();
SaveManager.getInstance().updateLevelBestScore(5, 9999);
底层实现:
load(): GameSaveData {
try {
const json = localStorage.getItem('grateful_durian_save');
if (json) {
return JSON.parse(json);
}
} catch (e) {
console.error('[SaveManager] 加载存档失败', e);
}
// 返回默认数据
return this.getDefaultData();
}
save(): void {
try {
const json = JSON.stringify(this._data);
localStorage.setItem('grateful_durian_save', json);
} catch (e) {
console.error('[SaveManager] 保存存档失败', e);
}
}
注意事项:
- 微信小游戏中 localStorage 有 10MB 限制
- 建议每次关键操作后立即 save()(如通关、购买道具)
- 不要在高频操作中频繁 save()(如每帧更新分数)
5.5 AdManager 广告集成
位置:assets/scripts/core/AdManager.ts
支持的广告类型:
-
激励视频广告(Rewarded Video)
- 复活(game over 时)
- 移除一个水果(道具)
- 双倍分数(结算时)
-
插屏广告(Interstitial)
- 每3关显示一次
- 从图鉴返回时
使用方法:
// 初始化(游戏启动时调用一次)
AdManager.init();
// 显示复活广告
AdManager.showReviveAd((success) => {
if (success) {
// 玩家观看了广告,执行复活逻辑
GameManager.getInstance().revive();
}
});
// 显示插屏广告
AdManager.showInterstitial();
广告单元ID配置:
在 AdManager.ts 顶部修改为你的真实广告位ID:
private static readonly AD_UNIT_IDS = {
REVIVE: 'revive_ad_unit_id_123', // 替换为你的ID
REMOVE_FRUIT: 'remove_ad_unit_id_456', // 替换为你的ID
INTERSTITIAL: 'interstitial_ad_id_789' // 替换为你的ID
};
获取广告位ID:
- 登录微信公众平台 https://mp.weixin.qq.com/
- 进入小程序后台 → 推广 → 流量主
- 创建广告位,复制广告位ID
六、美术资源替换
6.1 水果精灵图
规格要求:
- 格式:PNG(带透明通道)
- 尺寸:见下方表格
- 命名:
fruit_1.png~fruit_9.png
| 等级 | 名称 | 推荐尺寸 | 颜色主题 |
|---|---|---|---|
| 1 | 果切丁 | 36x36 | 浅黄色 |
| 2 | 西瓜片 | 50x50 | 红绿色 |
| 3 | 紫葡萄 | 64x64 | 紫色 |
| 4 | 黄柠檬 | 80x80 | 黄色 |
| 5 | 红苹果 | 96x96 | 红色 |
| 6 | 甜橙 | 112x112 | 橙色 |
| 7 | 粉桃子 | 128x128 | 粉色 |
| 8 | 金菠萝 | 152x152 | 金黄色 |
| 9 | 报恩榴莲 | 180x180 | 金棕色带刺 |
替换步骤:
- 将9张图片放入
assets/textures/fruits/目录 - 在 Cocos Creator 中刷新资源面板
- 打开
Fruit.prefab预制体 - 在代码中通过
resources.load动态加载,或使用图集
使用图集(推荐):
- 选中9张水果图片
- 右键 → 创建 → Sprite Atlas
- 命名为
fruits_atlas - 在 Fruit.ts 中加载图集:
import { resources, SpriteAtlas } from 'cc';
resources.load('textures/fruits_atlas', SpriteAtlas, (err, atlas) => {
if (err) return;
// 根据等级获取精灵帧
const frameName = `fruit_${this.level}`;
const spriteFrame = atlas.getSpriteFrame(frameName);
this.fruitSprite.spriteFrame = spriteFrame;
});
6.2 Boss 图片
规格:
- 格式:PNG
- 尺寸:200x200 px
- 数量:1张(或20张对应20个关卡)
命名:
- 通用Boss:
boss_default.png - 分关卡:
boss_1.png~boss_20.png
替换步骤:
- 放入
assets/textures/boss/ - 在 BossManager 中加载:
const bossPath = `textures/boss/boss_${this.currentLevel}`;
resources.load(bossPath, SpriteFrame, (err, spriteFrame) => {
if (err) {
// 降级使用默认Boss
resources.load('textures/boss/boss_default', SpriteFrame, ...);
return;
}
this.bossSprite.spriteFrame = spriteFrame;
});
6.3 UI 元素
必需UI图片:
bg_main.png- 主菜单背景(750x1334)bg_game.png- 游戏背景(750x1334)button_start.png- 开始按钮(300x100)progress_bar_bg.png- 进度条背景(200x20)progress_bar_fill.png- 进度条填充(200x20,九宫格拉伸)star.png- 星星图标(50x50)danger_line.png- 警戒线(600x4,红色虚线)
可选UI图片:
- 图标:暂停、主页、下一关、双倍分数等
- 装饰:粒子、光效、边框
6.4 AI 绘图提示词
如需使用 Midjourney 或 DALL-E 3 生成素材,参考 04_报恩榴莲_UI界面设计描述.md 中的提示词。
示例(水果精灵):
Prompt: "Cute cartoon watermelon slice, round shape, vibrant red and green colors,
glossy surface, mobile game asset, flat design, transparent background, 512x512"
Negative prompt: "realistic, photorealistic, complex details, text, watermark"
七、音频资源配置
7.1 音频文件清单
BGM(背景音乐):
bgm_main.mp3- 主游戏BGM(轻快活泼,60秒循环)bgm_golden.mp3- 金色传说BGM(史诗感,30秒)bgm_result.mp3- 结算BGM(欢快胜利,20秒)
SFX(音效):
sfx_drop_fruit.mp3- 水果掉落sfx_land_soft.mp3- 轻柔落地sfx_land_hard.mp3- 重重落地sfx_merge_success.mp3- 合成成功sfx_merge_chain.mp3- 连锁合成sfx_combo_2.mp3~sfx_combo_5.mp3- 连击音效sfx_combo_legend.mp3- 传奇连击sfx_feed_boss.mp3- 投喂Bosssfx_satiety_full.mp3- 饱食度满sfx_game_over.mp3- 失败sfx_revive_success.mp3- 复活成功sfx_button_click.mp3- 按钮点击sfx_panel_open.mp3- 面板打开sfx_panel_close.mp3- 面板关闭
Voice(语音):
voice_golden_legend_a.mp3- 金色传说台词Avoice_golden_legend_b.mp3- 金色传说台词Bvoice_golden_legend_c.mp3- 金色传说台词C
7.2 音频格式建议
微信小游戏推荐格式:
- BGM:MP3,128kbps,立体声
- SFX:OGG,64kbps,单声道(体积更小)
- Voice:OGG,96kbps,单声道
转换工具:
- Audacity(免费开源)
- FFmpeg(命令行)
- Online Audio Converter(在线)
FFmpeg 转换示例:
# MP3 转 OGG
ffmpeg -i input.mp3 -codec:a libvorbis -qscale:a 4 output.ogg
# 降低比特率
ffmpeg -i input.wav -codec:a libvorbis -qscale:a 6 output_low.ogg
7.3 音频加载策略
预加载(BGM): 在 GameManager.start() 中预加载:
resources.load('audio/bgm_main', AudioClip, (err, clip) => {
if (err) return;
// 缓存到 AudioManager
AudioManager.getInstance().preloadClip('bgm_main', clip);
});
按需加载(SFX/Voice): AudioManager 已实现自动按需加载,首次播放时异步加载并缓存。
7.4 使用 Suno AI 生成 BGM
如果需要原创音乐,可使用 Suno AI:
Prompt 示例(主游戏BGM):
Style: Upbeat casual mobile game music
Instruments: Piano, light percussion, bass
Mood: Cheerful, relaxed, playful
Tempo: 120 BPM
Duration: 60 seconds (loopable)
No vocals
Prompt 示例(金色传说BGM):
Style: Epic orchestral hybrid
Instruments: Strings, brass, choir, electronic elements
Mood: Majestic, triumphant, awe-inspiring
Tempo: 100 BPM
Duration: 30 seconds
No lyrics
八、微信小游戏集成
8.1 构建微信小游戏包
步骤:
- 在 Cocos Creator 菜单栏 → 项目 → 构建发布
- 平台选择:WeChat Mini Game
- 填写 AppID(从微信公众平台获取)
- 构建模板:选择 Wechat Game
- 点击 "构建" 按钮
- 等待构建完成(约 1-3 分钟)
- 构建输出目录:
build/wechatgame/
8.2 上传到微信开发者工具
步骤:
- 打开微信开发者工具
- 点击 "+" → 导入项目
- 项目目录:选择
build/wechatgame/文件夹 - AppID:填写你的小程序 AppID
- 点击 "导入"
- 等待编译完成
- 点击 "上传" 按钮
- 填写版本号和备注
- 提交审核
8.3 广告 SDK 集成
重要:微信小游戏的广告需要在微信公众平台配置后才能使用。
配置步骤:
- 登录 https://mp.weixin.qq.com/
- 进入小程序后台
- 左侧菜单 → 推广 → 流量主
- 开通流量主(需满足条件:累计独立访客 ≥ 1000)
- 创建广告位:
- 激励视频广告 × 3(复活、去水果、双倍分数)
- 插屏广告 × 1
- 复制广告位ID
- 在
AdManager.ts中替换广告位ID
测试广告: 在开发阶段,可使用测试广告位ID(不会真正展示广告,但会触发回调):
private static readonly AD_UNIT_IDS = {
REVIVE: 'test-ad-unit-id',
REMOVE_FRUIT: 'test-ad-unit-id',
INTERSTITIAL: 'test-ad-unit-id'
};
8.4 分包加载(进阶)
如果包体超过 4MB,需要使用分包:
配置分包:
- 在 Cocos Creator → 项目 → 项目设置 → 功能裁剪
- 启用 "分包加载"
- 将大型资源(如音频、高清图片)放入子包
- 构建时会自动生成分包配置
代码中加载子包:
wx.loadSubpackage({
name: 'subpackage_audio',
success: () => {
console.log('子包加载成功');
},
fail: (err) => {
console.error('子包加载失败', err);
}
});
8.5 性能监控
微信小游戏提供性能监控接口:
// 上报自定义数据
wx.reportMonitorPerformance({
key: 'level_clear_time',
value: timeSeconds,
});
// 上报错误
wx.reportMonitorError({
message: error.message,
stack: error.stack,
});
九、调试与测试
9.1 Cocos Creator 内置调试
浏览器控制台:
- 点击编辑器 "预览" 按钮
- 浏览器自动打开
- 按 F12 打开开发者工具
- 查看 Console 标签页的日志
常用调试命令:
// 在控制台执行
// 跳过当前关卡
window.skipLevel = () => {
const gm = cc.find('GameManagerNode').getComponent('GameManager');
gm.handleLevelClear();
};
// 强制触发金色传说
window.triggerGolden = () => {
const gm = cc.find('GameManagerNode').getComponent('GameManager');
gm.triggerGoldenLegend();
};
// 打印当前分数
console.log(cc.find('GameManagerNode').getComponent('GameManager').score);
9.2 真机调试
步骤:
- 在微信开发者工具中点击 "预览"
- 扫描二维码(需用手机微信扫描)
- 在真机上测试
- 观察性能和表现
常见问题:
- 真机性能低于模拟器 → 减少同屏水果数量
- 音频不播放 → 检查文件格式和大小
- 触摸无响应 → 检查 DropArea 的 UITransform 尺寸
9.3 性能分析
使用 Cocos Profiler:
- 在编辑器中点击 "Profiler" 按钮
- 查看 FPS、DrawCall、内存占用
- 优化瓶颈:
- FPS < 30 → 减少物理模拟复杂度
- DrawCall > 100 → 使用图集合并
- 内存 > 100MB → 及时释放未用资源
微信性能面板:
- 微信开发者工具 → 调试器 → Performance
- 查看 CPU、GPU、内存曲线
- 识别性能热点
9.4 单元测试(可选)
对于 MergeCore 纯逻辑层,可以编写单元测试:
// tests/MergeCore.test.ts
import { MergeCore } from '../assets/scripts/core/MergeCore';
describe('MergeCore', () => {
it('should merge 3 same-level fruits', () => {
const core = new MergeCore();
// 添加3个等级1的水果
core.addFruit('f1', 1, 0, 0);
core.addFruit('f2', 1, 10, 0);
core.addFruit('f3', 1, 5, 10);
// 尝试合成
const events = core.tryMerge();
expect(events.length).toBe(1);
expect(events[0].newLevel).toBe(2);
expect(events[0].removeIds).toEqual(['f1', 'f2', 'f3']);
});
});
运行测试:
npm install --save-dev jest @types/jest
npx jest
十、性能优化
10.1 渲染优化
图集合并:
- 将所有 UI 元素打包成 1-2 个图集
- 将 9 个水果精灵打包成 1 个图集
- 目标:DrawCall < 50
节点层级简化:
- 减少不必要的嵌套节点
- 隐藏不可见节点(
node.active = false)
粒子优化:
- 金色传说特效中的粒子数量控制在 50 以内
- 使用简单的 Quad 粒子而非复杂模型
10.2 物理优化
限制同屏水果数量:
// GameConfig.ts
export const PerformanceConfig = {
maxFruitsOnScreen: 50, // 超过此数量不再掉落新水果
};
禁用不必要的物理模拟:
// 水果静止后禁用刚体
if (velocity.magnitude < 0.1) {
rigidBody.enabled = false;
}
调整物理步长:
// 在 project.settings 中
PhysicsSystem2D.instance.fixedTimeStep = 1/60; // 默认值,可根据需要调整
10.3 内存优化
及时释放资源:
// 关卡结束时释放未使用的音频
AudioManager.getInstance().clearCache();
// 释放未使用的纹理
resources.releaseRes('textures/fruits/fruit_1', SpriteFrame);
对象池复用: 对于频繁创建销毁的对象(如水果),使用对象池:
import { NodePool } from 'cc';
class FruitPool {
private static _pool: NodePool = new NodePool();
static get(prefab: Prefab): Node {
if (this._pool.size() > 0) {
return this._pool.get();
}
return instantiate(prefab);
}
static put(node: Node) {
node.setPosition(0, 0, 0);
node.setScale(1, 1, 1);
this._pool.put(node);
}
}
10.4 代码优化
避免每帧创建对象:
// ❌ 错误示例
update(dt) {
const pos = new Vec3(); // 每帧创建新对象
}
// ✅ 正确示例
private _tempPos = new Vec3();
update(dt) {
this._tempPos.set(0, 0, 0); // 复用对象
}
使用定时器代替每帧检查:
// ❌ 错误示例
update(dt) {
if (Date.now() - this.lastCheck > 1000) {
// 每秒执行的逻辑
}
}
// ✅ 正确示例
this.schedule(() => {
// 每秒执行的逻辑
}, 1.0);
十一、打包发布
11.1 构建前检查清单
- 移除所有
console.log调试代码 - 关闭物理调试绘制(
PhysicsSystem2D.instance.debugDrawFlags = 0) - 替换所有测试广告位ID为正式ID
- 压缩音频文件(OGG 格式)
- 检查所有图片尺寸是否符合规范
- 测试所有广告触发点
- 测试存档保存和读取
- 真机测试至少 3 台不同设备
11.2 构建步骤
-
Cocos Creator 构建:
- 菜单栏 → 项目 → 构建发布
- 平台:WeChat Mini Game
- 勾选 "加密脚本"(防止代码泄露)
- 点击 "构建"
-
微信开发者工具上传:
- 导入
build/wechatgame/目录 - 点击 "上传"
- 填写版本号(如 1.0.0)
- 填写版本说明
- 导入
-
提交审核:
- 登录微信公众平台
- 版本管理 → 提交审核
- 填写审核信息:
- 服务类目:游戏 → 休闲
- 特殊资质:无需(休闲游戏)
- 测试账号:提供测试用的微信账号
11.3 审核后运营
数据分析:
- 微信公众平台 → 统计 → 数据分析
- 关注指标:
- 日活跃用户(DAU)
- 留存率(次日、7日、30日)
- 广告曝光次数
- 广告点击率(CTR)
- eCPM(千次曝光收益)
版本迭代:
- 根据数据调整关卡难度
- 新增活动关卡(节日限定)
- 优化广告触发频率
- 修复玩家反馈的 Bug
十二、常见问题FAQ
Q1: 水果不掉下来?
可能原因:
- RigidBody2D.Type 不是 Dynamic
- Allow Sleep 被勾选
- 重力加速度设置为 0
解决方法:
- 检查 Fruit.prefab 的 RigidBody2D 组件
- Type 设为 Dynamic
- 取消勾选 Allow Sleep
- Gravity Scale 设为 1
Q2: 水果合成不触发?
可能原因:
- MergeCore 未正确初始化
- tryMerge() 未在合适时机调用
- 水果的 level 属性未设置
解决方法:
- 在 GameManager.startLevel() 中确认
this.mergeCore = new MergeCore() - 在水果落地静止后调用
this.mergeCore.tryMerge() - 检查 Fruit.ts 中
this.level是否正确赋值
Q3: 金色传说特效不播放?
可能原因:
- goldenEffectNode 未绑定
- GoldenLegendEffect 组件未挂载
- 特效节点被其他节点遮挡
解决方法:
- 在 GameManager 属性面板中绑定 goldenEffectNode
- 确认 GoldenEffectNode 上已挂载 GoldenLegendEffect.ts
- 检查 Z-order,确保特效节点在最上层
Q4: 音效不播放?
可能原因:
- AudioManager 未 init()
- 音频文件路径错误
- 音频文件未导入到项目中
- 音量为 0 或静音
解决方法:
- 在 GameManager.start() 中调用
AudioManager.getInstance().init(this.node) - 检查音频文件是否在
assets/audio/目录 - 检查控制台是否有 "音频加载失败" 错误
- 调用
AudioManager.getInstance().setMute(false)
Q5: 存档数据丢失?
可能原因:
- 在微信模拟器中运行(localStorage 不稳定)
- JSON 解析错误
- 存储空间已满
解决方法:
- 在真机上测试
- 检查 SaveManager.load() 中的 try-catch
- 清理微信缓存后重试
Q6: 广告不显示?
可能原因:
- 广告位ID错误
- 未开通流量主
- 广告未预加载
- 网络问题
解决方法:
- 确认广告位ID已从微信公众平台复制正确
- 确认已开通流量主(需 ≥ 1000 UV)
- 在游戏启动时调用
AdManager.preloadAds() - 检查网络连接
Q7: 真机性能卡顿?
可能原因:
- 同屏水果过多
- DrawCall 过高
- 音频文件过大
- 物理模拟复杂度高
解决方法:
- 降低
PerformanceConfig.maxFruitsOnScreen到 30 - 使用图集合并,减少 DrawCall
- 压缩音频为 OGG 格式
- 简化水果碰撞体(使用 CircleCollider 而非 PolygonCollider)
Q8: 如何调整关卡难度?
调整方法:
- 修改
GameConfig.ts中的LEVEL_CONFIGS - 调整以下参数:
targetLevel:Boss 需求的水果等级(越高越难)feedCount:需要投喂的次数(越多越难)timeLimit:时间限制(越短越难)
示例:
// 第5关 - 中等难度
{
level: 5,
bossName: '贪吃喵星人',
targetLevel: 6, // 需要合成甜橙
feedCount: 5, // 需要投喂5次
timeLimit: 120 // 限时2分钟
}
Q9: 如何添加新水果?
步骤:
- 在
GameConfig.ts的FRUIT_CHAIN数组中添加新条目 - 准备对应的水果精灵图
- 调整物理参数(半径、质量等)
- 测试合成链是否正常
示例:
{
level: 10,
name: '超级榴莲王',
radius: 100,
score: 19683, // 3^9
color: '#FFD700'
}
Q10: 如何实现多语言?
方案:
- 创建语言配置文件
Localization.ts - 使用键值对存储文本
- 根据系统语言动态切换
示例:
// Localization.ts
export const TEXTS = {
zh: {
start: '开始游戏',
pause: '暂停',
gameOver: '游戏结束',
},
en: {
start: 'Start Game',
pause: 'Pause',
gameOver: 'Game Over',
}
};
// 使用时
const lang = wx.getSystemInfoSync().language.startsWith('zh') ? 'zh' : 'en';
const text = TEXTS[lang]['start'];
附录 A:完整开发时间表
| 阶段 | 任务 | 预计工时 |
|---|---|---|
| Day 1-2 | 环境搭建、场景搭建、预制体制作 | 16小时 |
| Day 3-4 | 核心玩法实现(MergeCore + Fruit + 物理) | 16小时 |
| Day 5-6 | Boss系统、金色传说特效、UI界面 | 16小时 |
| Day 7-8 | 音频系统、存档系统、广告集成 | 16小时 |
| Day 9-10 | 美术资源替换、音频资源配置 | 16小时 |
| Day 11-12 | 调试测试、性能优化、Bug修复 | 16小时 |
| Day 13-14 | 微信小游戏打包、提交审核 | 8小时 |
| Day 15-17 | 审核后调整、上线运营准备 | 8小时 |
总计:约 112 小时(14 个工作日)
附录 B:参考资料
- Cocos Creator 官方文档:https://docs.cocos.com/creator/manual/zh/
- 微信小游戏文档:https://developers.weixin.qq.com/minigame/dev/guide/
- TypeScript 手册:https://www.typescriptlang.org/docs/
- 游戏设计模式:https://gameprogrammingpatterns.com/
附录 C:联系与支持
- 项目仓库:(待补充)
- 问题反馈:(待补充)
- 社区论坛:https://forum.qoder.com/
文档版本:v1.0
最后更新:2026-08-14
作者:QoderWork AI Assistant
许可证:MIT