# 慧玩 Cocos 游戏开发 API 文档

## 目录

- [简介](#简介)
- [核心工具库 pg](#核心工具库-pg)
  - [事件系统 Emitter](#事件系统-emitter)
  - [时间控制 TimeCtrl](#时间控制-timectrl)
  - [日志系统 logger](#日志系统-logger)
  - [视图管理 view](#视图管理-view)
  - [资源加载 load](#资源加载-load)
  - [本地存储 localStorage](#本地存储-localstorage)
  - [网络请求 http](#网络请求-http)
  - [音频管理 audio](#音频管理-audio)
  - [时间管理 time](#时间管理-time)
  - [硬件交互 hw](#硬件交互-hw)
- [游戏管理控制器 Game](#游戏管理控制器-game)
  - [游戏状态 GAME_STATE](#游戏状态-game_state)
  - [选项类 Option](#选项类-option)
  - [角色类 Role](#角色类-role)
  - [玩家类 Player](#玩家类-player)
  - [游戏类 Game](#游戏类-game)
- [游戏开发示例](#游戏开发示例)
  - [场景初始化](#场景初始化)
  - [游戏流程控制](#游戏流程控制)
  - [UI交互处理](#ui交互处理)

## 简介

本文档提供了慧玩 Cocos 游戏开发框架的 API 参考，包括核心工具库 `pg` 和游戏管理控制器 `Game`。这些工具和类库为 Cocos Creator 游戏开发提供了便捷的功能封装，帮助开发者快速构建高质量的教育游戏。

## 核心工具库 pg

`pg` 是 Cocos Creator 常用功能的二次封装，提供了事件管理、视图操作、资源加载等实用工具。

### 事件系统 Emitter

`Emitter` 是一个基于单例模式的事件发射器，用于管理游戏中的事件监听和触发。

#### 属性

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| `instance` | `Emitter` | 单例实例 |
| `_name` | `string` | 事件发射器名称 |
| `_callbacks` | `Object` | 回调函数映射表 |

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `getInstance()` | 无 | `Emitter` | 获取单例实例 |
| `constructor(name)` | `name: string` | 无 | 构造函数，初始化事件发射器 |
| `clear()` | 无 | 无 | 清除所有事件监听 |
| `on(event, callback, target)` | `event: string`<br>`callback: Function`<br>`target: any` | 无 | 添加事件监听 |
| `once(event, callback, target)` | `event: string`<br>`callback: Function`<br>`target: any` | 无 | 添加一次性事件监听 |
| `off(event, callback, target)` | `event: string`<br>`callback: Function`<br>`target: any` | 无 | 移除事件监听 |
| `emit(event, ...args)` | `event: string`<br>`...args: any[]` | 无 | 触发事件 |
| `listeners(event)` | `event: string` | `Array<any>` | 获取指定事件的所有监听器 |
| `hasListeners(event)` | `event: string` | `boolean` | 检查是否有指定事件的监听器 |

### 时间控制 TimeCtrl

`TimeCtrl` 是一个时间控制器，用于管理游戏中的时间相关操作。

#### 属性

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| `current` | `number` | 当前时间值 |

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `constructor()` | 无 | 无 | 构造函数，初始化时间控制器 |
| `fillTime(time)` | `time: number` | 无 | 填充时间值 |
| `setTime(time)` | `time: number` | 无 | 设置时间值 |
| `wait(time)` | `time: number` | `Promise<void>` | 等待指定时间 |
| `reset()` | 无 | 无 | 重置时间控制器 |

### 日志系统 logger

`pg.logger` 模块提供了日志输出功能，用于调试和信息记录。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `d(tag, ...args)` | `tag: string`<br>`...args: any[]` | 无 | 输出调试日志 |
| `i(tag, ...args)` | `tag: string`<br>`...args: any[]` | 无 | 输出信息日志 |
| `w(tag, ...args)` | `tag: string`<br>`...args: any[]` | 无 | 输出警告日志 |

### 视图管理 view

`pg.view` 模块提供了节点管理、触控事件和渲染相关的功能。

#### 节点管理方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `createNode(name)` | `name: string` | `cc.Node` | 创建一个新节点 |
| `addChild(parent, child)` | `parent: cc.Node`<br>`child: cc.Node` | 无 | 添加子节点 |
| `removeChild(parent, child)` | `parent: cc.Node`<br>`child: cc.Node` | 无 | 移除子节点 |
| `removeSelf(node)` | `node: cc.Node` | 无 | 移除节点自身 |
| `removChildren(node)` | `node: cc.Node` | 无 | 移除所有子节点 |
| `find(node, path)` | `node: cc.Node`<br>`path: string` | `cc.Node` | 查找节点 |

#### 触控事件方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `touchOn(node, callback, target)` | `node: cc.Node`<br>`callback: Function`<br>`target: any` | 无 | 添加触控事件监听 |
| `touchOff(node, callback, target)` | `node: cc.Node`<br>`callback: Function`<br>`target: any` | 无 | 移除触控事件监听 |
| `touchEnable(node, enable)` | `node: cc.Node`<br>`enable: boolean` | 无 | 启用或禁用触控事件 |

#### 渲染相关方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `visible(node, visible)` | `node: cc.Node`<br>`visible: boolean` | 无 | 设置节点可见性 |
| `setImg(node, url)` | `node: cc.Node`<br>`url: string` | 无 | 设置节点图片 |
| `setNetImg(node, url, callback)` | `node: cc.Node`<br>`url: string`<br>`callback?: Function` | 无 | 设置网络图片 |
| `setNodeImg(node, sprite)` | `node: cc.Node`<br>`sprite: cc.SpriteFrame` | 无 | 设置节点精灵帧 |
| `switchMaterial(node, material)` | `node: cc.Node`<br>`material: cc.Material` | 无 | 切换节点材质 |
| `setString(node, str)` | `node: cc.Node`<br>`str: string` | 无 | 设置文本内容 |
| `setColor(node, color, stroke)` | `node: cc.Node`<br>`color: cc.Color`<br>`stroke?: cc.Color` | 无 | 设置颜色和描边 |
| `colorRgb(color)` | `color: string` | `cc.Color` | 十六进制颜色转RGB |

#### 动画相关方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `playSpineAnimation(node, name, loop)` | `node: cc.Node`<br>`name: string`<br>`loop: boolean` | 无 | 播放Spine动画 |
| `playDBAnimation(node, name, times)` | `node: cc.Node`<br>`name: string`<br>`times: number` | 无 | 播放DragonBones动画 |
| `clone(node)` | `node: cc.Node` | `cc.Node` | 克隆节点 |

### 资源加载 load

`pg.load` 模块提供了资源加载相关的功能。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `loadRes(url, type, callback)` | `url: string`<br>`type: typeof cc.Asset`<br>`callback?: Function` | `Promise<any>` | 加载资源 |
| `loadImg(url, callback)` | `url: string`<br>`callback?: Function` | `Promise<cc.SpriteFrame>` | 加载图片 |
| `loadPrefab(url, callback)` | `url: string`<br>`callback?: Function` | `Promise<cc.Prefab>` | 加载预制体 |
| `loadNetImg(url, callback)` | `url: string`<br>`callback?: Function` | `Promise<cc.SpriteFrame>` | 加载网络图片 |
| `loadDB(url, callback)` | `url: string`<br>`callback?: Function` | `Promise<dragonBones.DragonBonesAsset>` | 加载DragonBones动画资源 |
| `loadNetRes(url, callback)` | `url: string`<br>`callback?: Function` | `Promise<any>` | 加载网络资源 |

### 本地存储 localStorage

`pg.localStorage` 模块提供了本地存储功能。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `setItem(key, value)` | `key: string`<br>`value: any` | 无 | 存储数据 |
| `getItem(key)` | `key: string` | `any` | 获取数据 |

### 网络请求 http

`pg.http` 模块提供了网络请求功能。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `send(url, data, callback, method)` | `url: string`<br>`data?: any`<br>`callback?: Function`<br>`method?: string` | `Promise<any>` | 发送HTTP请求 |

### 音频管理 audio

`pg.audio` 模块提供了音频播放和管理功能。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `playAudioByUrl(url, callback, onStop)` | `url: string`<br>`callback?: Function`<br>`onStop?: Function` | `Promise<number>` | 通过URL播放音频 |
| `playBgAudioByUrl(url, callback)` | `url: string`<br>`callback?: Function` | `Promise<number>` | 通过URL播放背景音乐 |
| `stopAudio(audioId)` | `audioId: number` | 无 | 停止音频播放 |
| `playAudio(clip, callback)` | `clip: cc.AudioClip`<br>`callback?: Function` | `number` | 播放音频剪辑 |
| `playLocalAudio(node, callback)` | `node: cc.Node`<br>`callback?: Function` | 无 | 播放节点上的音频 |
| `getAudioClip(node)` | `node: cc.Node` | `cc.AudioClip` | 获取节点上的音频剪辑 |

### 时间管理 time

`pg.time` 模块提供了时间管理和延时功能。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `delay(time)` | `time: number` | `Promise<void>` | 延时指定时间 |
| `delayCtrl()` | 无 | `TimeCtrl` | 创建时间控制器 |

### 硬件交互 hw

`pg.hw` 模块提供了硬件交互功能。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `playLocalAudio(node, callback)` | `node: cc.Node`<br>`callback?: Function` | 无 | 播放本地音频 |

## 游戏管理控制器 Game

`Game` 是游戏的主控制器，负责管理游戏状态、问题选项和玩家信息。

### 游戏状态 GAME_STATE

`GAME_STATE` 是一个枚举类型，定义了游戏的不同状态。

| 状态 | 描述 |
| --- | --- |
| `WAIT` | 等待状态 |
| `RUNNING` | 运行状态 |
| `OVER` | 结束状态 |

### 选项类 Option

`Option` 类表示游戏中的一个问题选项。

#### 属性

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| `type` | `any` | 选项类型 |
| `txt` | `string` | 选项文本 |
| `picUrl` | `string` | 图片URL |
| `audioUrl` | `string` | 音频URL |
| `right` | `boolean` | 是否为正确选项 |
| `data` | `any` | 选项数据 |
| `id` | `number` | 选项ID |
| `count` | `number` | 计数器 |
| `time` | `number` | 持续时间 |
| `touchRight` | `boolean` | 是否触摸正确 |
| `parent` | `any` | 父对象引用 |

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `constructor(data, id)` | `data: any`<br>`id: number` | 无 | 构造函数，初始化选项属性 |

### 角色类 Role

`Role` 是一个基类，定义了角色的基本属性和方法。

#### 属性

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| `score` | `number` | 得分 |
| `right` | `number` | 正确次数 |
| `error` | `number` | 错误次数 |
| `voices` | `Array<any>` | 语音记录 |

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `constructor()` | 无 | 无 | 构造函数，初始化角色属性 |
| `addScore(score)` | `score?: number` | 无 | 增加分数 |
| `addError(score)` | `score?: number` | 无 | 增加错误次数 |
| `addRight()` | 无 | 无 | 增加正确次数 |
| `addVoice(obj)` | `obj: any` | 无 | 添加语音记录 |

### 玩家类 Player

`Player` 类继承自 `Role` 类，表示游戏中的玩家。

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `constructor()` | 无 | 无 | 构造函数 |
| `reset()` | 无 | 无 | 重置玩家数据 |

### 游戏类 Game

`Game` 是游戏的主控制类，采用单例模式设计。

#### 属性

| 属性 | 类型 | 描述 |
| --- | --- | --- |
| `ins` | `Game` | 单例实例 |
| `data` | `any` | 游戏数据 |
| `lists` | `Array<Option>` | 选项列表 |
| `player` | `Player` | 玩家实例 |
| `state` | `GAME_STATE` | 游戏状态 |
| `total` | `number` | 问题总数 |
| `question` | `{text, audio}` | 问题信息 |
| `title` | `string` | 游戏标题 |
| `bgAudio` | `string` | 背景音乐URL |
| `questionText` | `string` | 问题文本 |
| `start` | `boolean` | 游戏是否已开始 |
| `page` | `number` | 当前页码/进度 |

#### 方法

| 方法 | 参数 | 返回值 | 描述 |
| --- | --- | --- | --- |
| `getIns()` | 无 | `Game` | 获取游戏实例 |
| `constructor()` | 无 | 无 | 构造函数，初始化游戏基本属性 |
| `get len()` | 无 | `number` | 获取当前选项列表长度 |
| `init(data)` | `data: any` | 无 | 初始化游戏数据 |
| `reset()` | 无 | 无 | 重置游戏状态 |
| `getCardInfo()` | 无 | `Option` | 获取随机卡片信息 |
| `getTotalPageNum()` | 无 | `number` | 获取总页数/剩余问题数 |
| `getCurrentPageNum()` | 无 | `number` | 获取当前页码 |
| `addPage()` | 无 | 无 | 页码加1 |
| `get getTotla()` | 无 | `number` | 获取剩余总数 |
| `get isOver()` | 无 | `boolean` | 判断游戏是否结束 |

## 游戏开发示例

以下是使用 `pg` 工具库和 `Game` 控制器开发游戏的示例代码片段。

### 场景初始化

```typescript
// 导入工具函数和游戏管理类
import { asyncDelay, onHomeworkFinish, RandomInt } from "../script/util";
import { MyCocosSceneComponent } from "../script/MyCocosSceneComponent";
import Game, { GAME_STATE, Option } from "./tool/Game";
import pg from "./tool/pg";

@ccclass
export default class SceneComponent extends MyCocosSceneComponent {
    // 资源预加载
    addPreloadImage() {
        this._imageResList.push(...this.data.questions.map(question => {
            return { url: question.image };
        }));
    }
    
    addPreloadAudio() {
        this._audioResList.push({ url: this.data.questionTextAudio });
        this._audioResList.push({ url: this.data.bgAudio });
        this._audioResList.push(...this.data.questions.map(question => {
            return { url: question.audio };
        }));
    }
    
    // 资源加载完成后的回调
    onLoadEnd() {
        this.initData();  // 初始化数据
        this.initView();  // 初始化视图
        this.initEvent(); // 初始化事件
    }
    
    // 组件销毁时清理事件
    protected onDestroy(): void {
        pg.event.clear(); // 清理所有事件监听
    }
}
```

### 游戏流程控制

```typescript
// 游戏开始
gameStart() {
    // 设置游戏状态为运行中
    Game.getIns().state = GAME_STATE.RUNNING;
    
    // 隐藏开始界面
    this.layout_start.active = false;
    
    // 停止问题音频
    if (this.audioId > -1 && Game.getIns().state != GAME_STATE.WAIT) {
        cc.audioEngine.stopAllEffects();
        pg.audio.stopAudio(this.audioId);
    }
    
    // 播放背景音乐
    pg.audio.playBgAudioByUrl(Game.getIns().bgAudio);
    
    // 显示问题
    this.showQuestions();
}

// 游戏结束
private gameOver() {
    // 设置游戏状态为结束
    Game.getIns().state = GAME_STATE.OVER;
    
    // 准备提交的数据
    let data: any = {};
    data.total = Game.getIns().total;           // 总题目数
    data.right = Game.getIns().player.right;    // 正确题目数
    data.scores = Game.getIns().player.voices;  // 语音记录
    data.stone = Math.floor(10 * data.right / data.total);  // 计算得分
    
    // 提交作业完成数据
    onHomeworkFinish(data)
}
```

### UI交互处理

```typescript
// 初始化事件监听
initEvent() {
    // 开始按钮点击事件
    pg.view.touchOn(pg.view.find(this.layout_start, 'btn_start'), this.onTouchStart, this);
    // 音频控制按钮事件
    this.btn_audio_off.on(cc.Node.EventType.TOUCH_END, this.onAudioOff, this);
    this.btn_audio_on.on(cc.Node.EventType.TOUCH_END, this.onAudioOn, this);

    // 监听游戏开始事件
    pg.event.on("game_start", () => {
        this.gameStart();
    });
}

// 开始按钮点击处理
onTouchStart() {
    pg.audio.playLocalAudio(cc.find(`Canvas/res/audio/btn`));
    pg.event.emit("game_start");
}

// 显示问题选项
private showQuestions() {
    // 获取问题数量
    let len = Game.getIns().len;
    // 最多显示4个选项
    let count = len >= 4 ? 4 : len;
    
    // 创建每个蛋壳
    for (let i = 0; i < count; i++) {
        // 获取问题数据
        let eggData = Game.getIns().getCardInfo();
        // 获取蛋壳节点
        let egg = cc.find("layout_egg/egg" + i, this.layout_game);
        // 设置数据和显示状态
        egg.data = eggData;
        egg.active = true;
        
        // 添加点击事件
        egg.on(cc.Node.EventType.TOUCH_END, () => {
            // 处理点击逻辑
        });
    }
}
```

以上示例展示了如何使用 `pg` 工具库和 `Game` 控制器来开发一个完整的游戏。开发者可以根据自己的需求，灵活运用这些API来构建各种类型的教育游戏。