gratitude.durian.xpcool.com/cocos-prototype/PROJECT_GUIDE.md
2026-08-14 18:01:45 +08:00

36 KiB
Raw Permalink Blame History

报恩榴莲 - 完整项目开发指南

从零基础到微信小游戏上架的完整开发流程文档


📋 目录

  1. 项目概述
  2. 开发环境准备
  3. 项目结构说明
  4. 快速开始5分钟运行
  5. 核心系统详解
  6. 美术资源替换
  7. 音频资源配置
  8. 微信小游戏集成
  9. 调试与测试
  10. 性能优化
  11. 打包发布
  12. 常见问题FAQ

一、项目概述

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 安装

  1. 下载 Cocos Creator 3.8.x 安装包
  2. 运行安装程序,选择安装路径
  3. 首次启动需登录 Cocos 账号(可使用 GitHub 快捷登录)
  4. 在 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导入项目

  1. 打开 Cocos Creator 3.8
  2. 点击 "打开其他项目"
  3. 选择 cocos-prototype 文件夹
  4. 等待资源导入完成(首次约 1-2 分钟)

步骤 2创建场景

  1. 在资源管理器中右键 assets/scenes/ → 新建 → Scene
  2. 命名为 MainGame
  3. 双击打开场景编辑

步骤 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 预制体

  1. 右键 assets/prefabs/ → 新建 → Prefab → 命名 Fruit
  2. 双击进入预制体编辑
  3. 创建节点结构:
    FruitRoot
    ├── FruitSprite (Sprite)
    ├── CircleCollider2D
    ├── RigidBody2D (Type=Dynamic, Fixed Rotation=true)
    └── Fruit.ts (脚本组件)
    
  4. 为 FruitSprite 指定一个临时图片(可用 Cocos 默认 spriteFrame
  5. 保存预制体

步骤 5绑定 GameManager 属性

  1. 在场景中选中 GameManagerNode
  2. 在属性检查器中点击 "添加组件" → 搜索 GameManager
  3. 将以下节点拖拽到对应属性槽:
    • gameContainer → GameContainer 节点
    • dropArea → DropArea 节点
    • fruitLayer → FruitLayer 节点
    • fruitPrefab → Fruit.prefab 资源
  4. 暂时留空其他属性Boss、UI等后续补充

步骤 6运行测试

  1. 点击编辑器顶部的 "预览" 按钮(或按 Ctrl+P
  2. 浏览器自动打开游戏页面
  3. 点击 DropArea 区域(场景顶部)
  4. 观察水果掉落和物理碰撞

如果看到水果掉落并堆积在底部,恭喜!核心玩法已跑通!


五、核心系统详解

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

职责

  1. 管理游戏状态MENU / PLAYING / PAUSED / GAMEOVER / CLEARED
  2. 处理玩家点击掉落水果
  3. 监听 MergeCore 的合成事件
  4. 检测失败条件(水果超过警戒线)
  5. 协调各子系统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

支持的广告类型

  1. 激励视频广告Rewarded Video

    • 复活game over 时)
    • 移除一个水果(道具)
    • 双倍分数(结算时)
  2. 插屏广告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

  1. 登录微信公众平台 https://mp.weixin.qq.com/
  2. 进入小程序后台 → 推广 → 流量主
  3. 创建广告位复制广告位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 金棕色带刺

替换步骤

  1. 将9张图片放入 assets/textures/fruits/ 目录
  2. 在 Cocos Creator 中刷新资源面板
  3. 打开 Fruit.prefab 预制体
  4. 在代码中通过 resources.load 动态加载,或使用图集

使用图集(推荐)

  1. 选中9张水果图片
  2. 右键 → 创建 → Sprite Atlas
  3. 命名为 fruits_atlas
  4. 在 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个关卡

命名

  • 通用Bossboss_default.png
  • 分关卡:boss_1.png ~ boss_20.png

替换步骤

  1. 放入 assets/textures/boss/
  2. 在 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 - 投喂Boss
  • sfx_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 - 金色传说台词A
  • voice_golden_legend_b.mp3 - 金色传说台词B
  • voice_golden_legend_c.mp3 - 金色传说台词C

7.2 音频格式建议

微信小游戏推荐格式

  • BGMMP3128kbps立体声
  • SFXOGG64kbps单声道体积更小
  • VoiceOGG96kbps单声道

转换工具

  • 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 构建微信小游戏包

步骤

  1. 在 Cocos Creator 菜单栏 → 项目 → 构建发布
  2. 平台选择:WeChat Mini Game
  3. 填写 AppID从微信公众平台获取
  4. 构建模板:选择 Wechat Game
  5. 点击 "构建" 按钮
  6. 等待构建完成(约 1-3 分钟)
  7. 构建输出目录:build/wechatgame/

8.2 上传到微信开发者工具

步骤

  1. 打开微信开发者工具
  2. 点击 "+" → 导入项目
  3. 项目目录:选择 build/wechatgame/ 文件夹
  4. AppID填写你的小程序 AppID
  5. 点击 "导入"
  6. 等待编译完成
  7. 点击 "上传" 按钮
  8. 填写版本号和备注
  9. 提交审核

8.3 广告 SDK 集成

重要:微信小游戏的广告需要在微信公众平台配置后才能使用。

配置步骤

  1. 登录 https://mp.weixin.qq.com/
  2. 进入小程序后台
  3. 左侧菜单 → 推广 → 流量主
  4. 开通流量主(需满足条件:累计独立访客 ≥ 1000
  5. 创建广告位:
    • 激励视频广告 × 3复活、去水果、双倍分数
    • 插屏广告 × 1
  6. 复制广告位ID
  7. 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需要使用分包

配置分包

  1. 在 Cocos Creator → 项目 → 项目设置 → 功能裁剪
  2. 启用 "分包加载"
  3. 将大型资源(如音频、高清图片)放入子包
  4. 构建时会自动生成分包配置

代码中加载子包

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 内置调试

浏览器控制台

  1. 点击编辑器 "预览" 按钮
  2. 浏览器自动打开
  3. 按 F12 打开开发者工具
  4. 查看 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 真机调试

步骤

  1. 在微信开发者工具中点击 "预览"
  2. 扫描二维码(需用手机微信扫描)
  3. 在真机上测试
  4. 观察性能和表现

常见问题

  • 真机性能低于模拟器 → 减少同屏水果数量
  • 音频不播放 → 检查文件格式和大小
  • 触摸无响应 → 检查 DropArea 的 UITransform 尺寸

9.3 性能分析

使用 Cocos Profiler

  1. 在编辑器中点击 "Profiler" 按钮
  2. 查看 FPS、DrawCall、内存占用
  3. 优化瓶颈:
    • FPS < 30 → 减少物理模拟复杂度
    • DrawCall > 100 → 使用图集合并
    • 内存 > 100MB → 及时释放未用资源

微信性能面板

  1. 微信开发者工具 → 调试器 → Performance
  2. 查看 CPU、GPU、内存曲线
  3. 识别性能热点

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 构建步骤

  1. Cocos Creator 构建

    • 菜单栏 → 项目 → 构建发布
    • 平台WeChat Mini Game
    • 勾选 "加密脚本"(防止代码泄露)
    • 点击 "构建"
  2. 微信开发者工具上传

    • 导入 build/wechatgame/ 目录
    • 点击 "上传"
    • 填写版本号(如 1.0.0
    • 填写版本说明
  3. 提交审核

    • 登录微信公众平台
    • 版本管理 → 提交审核
    • 填写审核信息:
      • 服务类目:游戏 → 休闲
      • 特殊资质:无需(休闲游戏)
      • 测试账号:提供测试用的微信账号

11.3 审核后运营

数据分析

  • 微信公众平台 → 统计 → 数据分析
  • 关注指标:
    • 日活跃用户DAU
    • 留存率次日、7日、30日
    • 广告曝光次数
    • 广告点击率CTR
    • eCPM千次曝光收益

版本迭代

  • 根据数据调整关卡难度
  • 新增活动关卡(节日限定)
  • 优化广告触发频率
  • 修复玩家反馈的 Bug

十二、常见问题FAQ

Q1: 水果不掉下来?

可能原因

  1. RigidBody2D.Type 不是 Dynamic
  2. Allow Sleep 被勾选
  3. 重力加速度设置为 0

解决方法

  • 检查 Fruit.prefab 的 RigidBody2D 组件
  • Type 设为 Dynamic
  • 取消勾选 Allow Sleep
  • Gravity Scale 设为 1

Q2: 水果合成不触发?

可能原因

  1. MergeCore 未正确初始化
  2. tryMerge() 未在合适时机调用
  3. 水果的 level 属性未设置

解决方法

  • 在 GameManager.startLevel() 中确认 this.mergeCore = new MergeCore()
  • 在水果落地静止后调用 this.mergeCore.tryMerge()
  • 检查 Fruit.ts 中 this.level 是否正确赋值

Q3: 金色传说特效不播放?

可能原因

  1. goldenEffectNode 未绑定
  2. GoldenLegendEffect 组件未挂载
  3. 特效节点被其他节点遮挡

解决方法

  • 在 GameManager 属性面板中绑定 goldenEffectNode
  • 确认 GoldenEffectNode 上已挂载 GoldenLegendEffect.ts
  • 检查 Z-order确保特效节点在最上层

Q4: 音效不播放?

可能原因

  1. AudioManager 未 init()
  2. 音频文件路径错误
  3. 音频文件未导入到项目中
  4. 音量为 0 或静音

解决方法

  • 在 GameManager.start() 中调用 AudioManager.getInstance().init(this.node)
  • 检查音频文件是否在 assets/audio/ 目录
  • 检查控制台是否有 "音频加载失败" 错误
  • 调用 AudioManager.getInstance().setMute(false)

Q5: 存档数据丢失?

可能原因

  1. 在微信模拟器中运行localStorage 不稳定)
  2. JSON 解析错误
  3. 存储空间已满

解决方法

  • 在真机上测试
  • 检查 SaveManager.load() 中的 try-catch
  • 清理微信缓存后重试

Q6: 广告不显示?

可能原因

  1. 广告位ID错误
  2. 未开通流量主
  3. 广告未预加载
  4. 网络问题

解决方法

  • 确认广告位ID已从微信公众平台复制正确
  • 确认已开通流量主(需 ≥ 1000 UV
  • 在游戏启动时调用 AdManager.preloadAds()
  • 检查网络连接

Q7: 真机性能卡顿?

可能原因

  1. 同屏水果过多
  2. DrawCall 过高
  3. 音频文件过大
  4. 物理模拟复杂度高

解决方法

  • 降低 PerformanceConfig.maxFruitsOnScreen 到 30
  • 使用图集合并,减少 DrawCall
  • 压缩音频为 OGG 格式
  • 简化水果碰撞体(使用 CircleCollider 而非 PolygonCollider

Q8: 如何调整关卡难度?

调整方法

  1. 修改 GameConfig.ts 中的 LEVEL_CONFIGS
  2. 调整以下参数:
    • targetLevelBoss 需求的水果等级(越高越难)
    • feedCount:需要投喂的次数(越多越难)
    • timeLimit:时间限制(越短越难)

示例

// 第5关 - 中等难度
{
    level: 5,
    bossName: '贪吃喵星人',
    targetLevel: 6,  // 需要合成甜橙
    feedCount: 5,    // 需要投喂5次
    timeLimit: 120   // 限时2分钟
}

Q9: 如何添加新水果?

步骤

  1. GameConfig.tsFRUIT_CHAIN 数组中添加新条目
  2. 准备对应的水果精灵图
  3. 调整物理参数(半径、质量等)
  4. 测试合成链是否正常

示例

{
    level: 10,
    name: '超级榴莲王',
    radius: 100,
    score: 19683, // 3^9
    color: '#FFD700'
}

Q10: 如何实现多语言?

方案

  1. 创建语言配置文件 Localization.ts
  2. 使用键值对存储文本
  3. 根据系统语言动态切换

示例

// 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参考资料


附录 C联系与支持


文档版本v1.0
最后更新2026-08-14
作者QoderWork AI Assistant
许可证MIT