mirror of
https://github.com/Gongxh0901/kunpolibrary
synced 2025-12-05 20:29:10 +00:00
添加项目规则;修复几处潜在问题
This commit is contained in:
@@ -1,55 +0,0 @@
|
|||||||
# Claude Code ignore rules for kunpolibrary
|
|
||||||
# 昆坡库项目过滤规则,减少上下文消耗
|
|
||||||
|
|
||||||
# Demo and example files - 示例和演示文件
|
|
||||||
demo/
|
|
||||||
**/demo/
|
|
||||||
|
|
||||||
# Dependencies - 依赖包
|
|
||||||
node_modules/
|
|
||||||
**/node_modules/
|
|
||||||
|
|
||||||
# Generated/build output - 构建产物
|
|
||||||
dist/
|
|
||||||
**/dist/
|
|
||||||
|
|
||||||
# Type definitions - 类型定义文件
|
|
||||||
libs/
|
|
||||||
**/libs/
|
|
||||||
|
|
||||||
# Images and assets - 图片和静态资源
|
|
||||||
image/
|
|
||||||
**/image/
|
|
||||||
*.jpg
|
|
||||||
*.jpeg
|
|
||||||
*.png
|
|
||||||
*.gif
|
|
||||||
*.ico
|
|
||||||
|
|
||||||
# Other common build artifacts - 其他常见构建产物
|
|
||||||
build/
|
|
||||||
**/build/
|
|
||||||
temp/
|
|
||||||
**/temp/
|
|
||||||
.temp/
|
|
||||||
**/.temp/
|
|
||||||
|
|
||||||
# Cache directories - 缓存目录
|
|
||||||
.cache/
|
|
||||||
**/.cache/
|
|
||||||
|
|
||||||
# Log files - 日志文件
|
|
||||||
*.log
|
|
||||||
logs/
|
|
||||||
**/logs/
|
|
||||||
|
|
||||||
# IDE and editor files - IDE和编辑器文件
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
|
|
||||||
# OS generated files - 操作系统生成的文件
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
133
.cursor/rules/README.md
Normal file
133
.cursor/rules/README.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# KunpoCC Cursor Rules
|
||||||
|
|
||||||
|
这个目录包含了 KunpoCC 项目的 Cursor AI 编程助手规则文件。这些规则帮助 Cursor 更好地理解项目架构和编码规范,提供更准确的代码建议和自动完成。
|
||||||
|
|
||||||
|
## 规则文件结构
|
||||||
|
|
||||||
|
### 📋 [project-overview.mdc](./project-overview.mdc)
|
||||||
|
**总体项目规范** - 始终应用
|
||||||
|
- 项目架构和目录结构
|
||||||
|
- 开发流程和质量保证
|
||||||
|
- 版本管理和部署规范
|
||||||
|
- 适用范围:所有文件
|
||||||
|
|
||||||
|
### 🔧 [typescript-general.mdc](./typescript-general.mdc)
|
||||||
|
**TypeScript 通用规范** - 始终应用
|
||||||
|
- 命名约定和代码风格
|
||||||
|
- 类型定义和泛型使用
|
||||||
|
- JSDoc 注释规范
|
||||||
|
- 错误处理模式
|
||||||
|
- 适用范围:`src/**/*.ts`
|
||||||
|
|
||||||
|
### 🎮 [cocos-creator.mdc](./cocos-creator.mdc)
|
||||||
|
**Cocos Creator 开发规范**
|
||||||
|
- Component 基类设计模式
|
||||||
|
- 生命周期管理
|
||||||
|
- 平台适配和节点管理
|
||||||
|
- 适配器模式应用
|
||||||
|
- 适用范围:`src/cocos/**/*.ts`, `src/global/**/*.ts`
|
||||||
|
|
||||||
|
### 🖼️ [fairygui.mdc](./fairygui.mdc)
|
||||||
|
**FairyGUI UI 系统规范**
|
||||||
|
- 窗口基类和生命周期
|
||||||
|
- 窗口管理器模式
|
||||||
|
- 装饰器系统使用
|
||||||
|
- 资源管理和屏幕适配
|
||||||
|
- 适用范围:`src/fgui/**/*.ts`, `src/ui/**/*.ts`
|
||||||
|
|
||||||
|
### 🎨 [decorator-patterns.mdc](./decorator-patterns.mdc)
|
||||||
|
**装饰器模式规范**
|
||||||
|
- namespace 封装模式
|
||||||
|
- 类装饰器和属性装饰器
|
||||||
|
- 方法装饰器和元数据管理
|
||||||
|
- 动态注册支持
|
||||||
|
- 适用范围:`**/*Decorator*.ts`, `**/*decorator*.ts`
|
||||||
|
|
||||||
|
### 📊 [data-binding.mdc](./data-binding.mdc)
|
||||||
|
**数据绑定系统规范**
|
||||||
|
- DataBase 基类设计
|
||||||
|
- 强类型绑定装饰器
|
||||||
|
- 绑定管理器和批量更新
|
||||||
|
- 路径解析和类型安全
|
||||||
|
- 适用范围:`src/data/**/*.ts`, `**/*Data*.ts`
|
||||||
|
|
||||||
|
### 🐛 [logging-debugging.mdc](./logging-debugging.mdc)
|
||||||
|
**日志系统和调试规范** - 始终应用
|
||||||
|
- 统一日志接口和级别
|
||||||
|
- 调试模式管理
|
||||||
|
- 全局调试工具
|
||||||
|
- 性能监控日志
|
||||||
|
- 适用范围:`src/tool/log.ts`, `**/*.ts`
|
||||||
|
|
||||||
|
### 🏗️ [architecture-patterns.mdc](./architecture-patterns.mdc)
|
||||||
|
**架构模式规范**
|
||||||
|
- 单例管理器模式
|
||||||
|
- 抽象基类设计
|
||||||
|
- 接口和契约定义
|
||||||
|
- 工厂模式和适配器模式
|
||||||
|
- 适用范围:`src/**/*.ts`
|
||||||
|
|
||||||
|
### 📱 [minigame-platform.mdc](./minigame-platform.mdc)
|
||||||
|
**小游戏平台规范**
|
||||||
|
- 平台检测和分类
|
||||||
|
- 平台适配器设计
|
||||||
|
- 统一接口封装
|
||||||
|
- 平台特定功能实现
|
||||||
|
- 适用范围:`src/minigame/**/*.ts`, `src/global/Platform.ts`
|
||||||
|
|
||||||
|
### 🔄 [hot-update.mdc](./hot-update.mdc)
|
||||||
|
**热更新系统规范**
|
||||||
|
- 管理器单例模式
|
||||||
|
- Promise 结果模式
|
||||||
|
- manifest 管理
|
||||||
|
- 进度监控和错误处理
|
||||||
|
- 适用范围:`src/hotupdate/**/*.ts`
|
||||||
|
|
||||||
|
## 规则应用方式
|
||||||
|
|
||||||
|
### 自动应用的规则
|
||||||
|
以下规则会自动应用到相关文件:
|
||||||
|
- `project-overview.mdc` - 所有文件
|
||||||
|
- `typescript-general.mdc` - 所有 TypeScript 文件
|
||||||
|
- `logging-debugging.mdc` - 所有文件
|
||||||
|
|
||||||
|
### 条件应用的规则
|
||||||
|
其他规则通过文件路径模式匹配自动应用到相关文件。
|
||||||
|
|
||||||
|
### 手动引用规则
|
||||||
|
你也可以在与 Cursor 的对话中手动引用特定的规则文件:
|
||||||
|
```
|
||||||
|
请参考 FairyGUI 规范来实现这个窗口类
|
||||||
|
请按照数据绑定规范来设计这个数据类
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用建议
|
||||||
|
|
||||||
|
### 开发新功能时
|
||||||
|
1. 先查看 `project-overview.mdc` 了解整体架构
|
||||||
|
2. 根据功能类型查看相应的专门规范
|
||||||
|
3. 遵循 `typescript-general.mdc` 的基本编码规范
|
||||||
|
|
||||||
|
### 重构代码时
|
||||||
|
1. 参考 `architecture-patterns.mdc` 的设计模式
|
||||||
|
2. 检查是否符合相关模块的特定规范
|
||||||
|
3. 确保日志和错误处理符合规范
|
||||||
|
|
||||||
|
### 添加新平台支持时
|
||||||
|
1. 参考 `minigame-platform.mdc` 的适配模式
|
||||||
|
2. 遵循统一接口设计原则
|
||||||
|
3. 添加相应的平台检测和错误处理
|
||||||
|
|
||||||
|
## 规则维护
|
||||||
|
|
||||||
|
### 更新规则
|
||||||
|
- 当项目架构发生重大变更时,及时更新相关规则
|
||||||
|
- 新增功能模块时,考虑是否需要新的规则文件
|
||||||
|
- 定期检查规则的准确性和完整性
|
||||||
|
|
||||||
|
### 规则版本控制
|
||||||
|
- 规则文件纳入版本控制
|
||||||
|
- 重大规则变更记录在项目 CHANGELOG 中
|
||||||
|
- 确保团队成员了解规则更新
|
||||||
|
|
||||||
|
这些规则文件帮助确保代码质量和一致性,同时让 Cursor AI 能够更好地理解项目上下文,提供更准确的编程辅助。
|
||||||
383
.cursor/rules/architecture-patterns.mdc
Normal file
383
.cursor/rules/architecture-patterns.mdc
Normal file
@@ -0,0 +1,383 @@
|
|||||||
|
---
|
||||||
|
description: "架构模式和设计原则规范"
|
||||||
|
globs: ["src/**/*.ts"]
|
||||||
|
alwaysApply: false
|
||||||
|
type: "architecture"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 架构模式和设计原则
|
||||||
|
|
||||||
|
## 单例管理器模式
|
||||||
|
|
||||||
|
### 管理器类设计规范
|
||||||
|
```typescript
|
||||||
|
export class WindowManager {
|
||||||
|
/** 使用私有静态属性存储状态 */
|
||||||
|
private static _groups: Map<string, WindowGroup> = new Map();
|
||||||
|
private static _windows: Map<string, IWindow> = new Map();
|
||||||
|
private static _resPool: WindowResPool;
|
||||||
|
|
||||||
|
/** 禁用构造函数 */
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/** 提供静态方法访问功能 */
|
||||||
|
public static showWindow(windowName: string): Promise<void> {
|
||||||
|
// 实现逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 内部方法使用下划线前缀和 @internal 注释 */
|
||||||
|
/**
|
||||||
|
* 添加窗口 (框架内部使用)
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static _addWindow(name: string, window: IWindow): void {
|
||||||
|
this._windows.set(name, window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 管理器初始化模式
|
||||||
|
```typescript
|
||||||
|
export class SomeManager {
|
||||||
|
private static _initialized: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化管理器 (框架内部调用)
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static _init(dependencies: Dependencies): void {
|
||||||
|
if (this._initialized) {
|
||||||
|
warn("管理器已经初始化");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._initialized = true;
|
||||||
|
// 初始化逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ensureInitialized(): void {
|
||||||
|
if (!this._initialized) {
|
||||||
|
throw new Error("管理器未初始化,请先调用 _init 方法");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 抽象基类模式
|
||||||
|
|
||||||
|
### 基类设计原则
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 抽象基类 - 定义共同接口和默认实现
|
||||||
|
*/
|
||||||
|
export abstract class WindowBase extends Component {
|
||||||
|
/** 共同属性 */
|
||||||
|
protected _gcom: GComponent;
|
||||||
|
protected _isShow: boolean = false;
|
||||||
|
|
||||||
|
/** 抽象方法 - 子类必须实现 */
|
||||||
|
protected abstract onInit(): void;
|
||||||
|
|
||||||
|
/** 虚方法 - 子类可选择重写 */
|
||||||
|
protected onShow(userdata?: any): void {
|
||||||
|
// 默认实现
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onClose(): void {
|
||||||
|
// 默认实现
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 最终方法 - 不允许重写 */
|
||||||
|
public final show(userdata?: any): void {
|
||||||
|
if (this._isShow) return;
|
||||||
|
|
||||||
|
this.onShow(userdata);
|
||||||
|
this._isShow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 具体实现类
|
||||||
|
*/
|
||||||
|
export abstract class Window extends WindowBase {
|
||||||
|
/** 进一步的抽象和默认实现 */
|
||||||
|
protected onAdapted(): void {
|
||||||
|
// 适配逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增的生命周期方法 */
|
||||||
|
protected onHide(): void { }
|
||||||
|
protected onShowFromHide(): void { }
|
||||||
|
protected onCover(): void { }
|
||||||
|
protected onRecover(): void { }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模块基类设计
|
||||||
|
```typescript
|
||||||
|
export abstract class ModuleBase extends Component implements IModule {
|
||||||
|
/** 模块标识 */
|
||||||
|
public moduleName: string;
|
||||||
|
|
||||||
|
/** 框架调用的初始化方法 */
|
||||||
|
public init(): void {
|
||||||
|
debug(`模块初始化: ${this.moduleName}`);
|
||||||
|
this.onInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 子类实现的初始化逻辑 */
|
||||||
|
protected abstract onInit(): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 接口和契约设计
|
||||||
|
|
||||||
|
### 接口定义规范
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 窗口接口 - 定义窗口必须实现的方法
|
||||||
|
*/
|
||||||
|
export interface IWindow {
|
||||||
|
/** 窗口显示 */
|
||||||
|
_show(userdata?: any): void;
|
||||||
|
|
||||||
|
/** 窗口隐藏 */
|
||||||
|
_hide(): void;
|
||||||
|
|
||||||
|
/** 窗口关闭 */
|
||||||
|
_close(): void;
|
||||||
|
|
||||||
|
/** 窗口恢复 */
|
||||||
|
_recover(): void;
|
||||||
|
|
||||||
|
/** 屏幕尺寸变化适配 */
|
||||||
|
screenResize(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模块接口
|
||||||
|
*/
|
||||||
|
export interface IModule {
|
||||||
|
/** 模块名称 */
|
||||||
|
moduleName: string;
|
||||||
|
|
||||||
|
/** 模块初始化 */
|
||||||
|
init(): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置接口设计
|
||||||
|
```typescript
|
||||||
|
export interface IPackageConfigRes {
|
||||||
|
[windowName: string]: {
|
||||||
|
group: string;
|
||||||
|
pkg: string;
|
||||||
|
bundle?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FrameConfig {
|
||||||
|
/** 开启debug 默认: false */
|
||||||
|
debug?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 工厂模式应用
|
||||||
|
|
||||||
|
### 动态注册工厂
|
||||||
|
```typescript
|
||||||
|
export class ComponentExtendHelper {
|
||||||
|
private static _componentMaps: Map<string, any> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册组件类型
|
||||||
|
*/
|
||||||
|
public static register(): void {
|
||||||
|
for (const { ctor, res } of _uidecorator.getComponentMaps().values()) {
|
||||||
|
// 使用 FairyGUI 的工厂方法注册
|
||||||
|
UIObjectFactory.setPackageItemExtension(
|
||||||
|
`ui://${res.pkg}/${res.name}`,
|
||||||
|
ctor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态注册单个组件
|
||||||
|
*/
|
||||||
|
public static dynamicRegister(ctor: any, pkg: string, name: string): void {
|
||||||
|
UIObjectFactory.setPackageItemExtension(`ui://${pkg}/${name}`, ctor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 资源池模式
|
||||||
|
|
||||||
|
### 资源管理设计
|
||||||
|
```typescript
|
||||||
|
export class WindowResPool {
|
||||||
|
/** 资源信息映射 */
|
||||||
|
private _windowInfos: Map<string, WindowHeaderInfo> = new Map();
|
||||||
|
private _headerInfos: Map<string, WindowHeaderInfo> = new Map();
|
||||||
|
|
||||||
|
/** 引用计数管理 */
|
||||||
|
private _refCounts: Map<string, number> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加资源引用
|
||||||
|
*/
|
||||||
|
public addResRef(windowName: string): void {
|
||||||
|
const count = this._refCounts.get(windowName) || 0;
|
||||||
|
this._refCounts.set(windowName, count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放资源引用
|
||||||
|
*/
|
||||||
|
public releaseWindowRes(windowName: string): void {
|
||||||
|
const count = this._refCounts.get(windowName) || 0;
|
||||||
|
if (count > 0) {
|
||||||
|
const newCount = count - 1;
|
||||||
|
this._refCounts.set(windowName, newCount);
|
||||||
|
|
||||||
|
// 引用为0时释放资源
|
||||||
|
if (newCount === 0) {
|
||||||
|
this.unloadResource(windowName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 事件驱动架构
|
||||||
|
|
||||||
|
### 事件系统集成
|
||||||
|
```typescript
|
||||||
|
// 使用外部事件系统
|
||||||
|
import { EventEmitter } from "kunpocc-event";
|
||||||
|
|
||||||
|
export class DataBase extends EventEmitter {
|
||||||
|
protected notify(path: string, value: any): void {
|
||||||
|
// 触发事件通知
|
||||||
|
this.emit(`change:${path}`, value);
|
||||||
|
|
||||||
|
// 同时支持绑定系统
|
||||||
|
BindManager.notifyChange(`${this.constructor.name}:${path}`, value, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 生命周期事件
|
||||||
|
```typescript
|
||||||
|
export class WindowManager {
|
||||||
|
/** 窗口生命周期事件 */
|
||||||
|
public static readonly events = {
|
||||||
|
WINDOW_SHOW: 'window:show',
|
||||||
|
WINDOW_HIDE: 'window:hide',
|
||||||
|
WINDOW_CLOSE: 'window:close'
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
public static showWindow(windowName: string): void {
|
||||||
|
// 业务逻辑...
|
||||||
|
|
||||||
|
// 触发事件
|
||||||
|
GlobalEvent.emit(this.events.WINDOW_SHOW, { windowName });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 适配器模式
|
||||||
|
|
||||||
|
### 平台适配设计
|
||||||
|
```typescript
|
||||||
|
export abstract class PlatformAdapter {
|
||||||
|
/** 平台特定的实现 */
|
||||||
|
public abstract showBanner(): void;
|
||||||
|
public abstract vibrate(): void;
|
||||||
|
public abstract getSystemInfo(): SystemInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WechatAdapter extends PlatformAdapter {
|
||||||
|
public showBanner(): void {
|
||||||
|
wx.createBannerAd(/* ... */);
|
||||||
|
}
|
||||||
|
|
||||||
|
public vibrate(): void {
|
||||||
|
wx.vibrateShort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AlipayAdapter extends PlatformAdapter {
|
||||||
|
public showBanner(): void {
|
||||||
|
my.createBannerAd(/* ... */);
|
||||||
|
}
|
||||||
|
|
||||||
|
public vibrate(): void {
|
||||||
|
my.vibrate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 引擎适配
|
||||||
|
```typescript
|
||||||
|
export class CocosAdapter {
|
||||||
|
public init(): void {
|
||||||
|
this.initUI();
|
||||||
|
this.initInput();
|
||||||
|
this.initAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initUI(): void {
|
||||||
|
// 初始化UI系统适配
|
||||||
|
const uiModule = new CocosUIModule();
|
||||||
|
uiModule.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 依赖注入模式
|
||||||
|
|
||||||
|
### 服务定位器模式
|
||||||
|
```typescript
|
||||||
|
export class ServiceLocator {
|
||||||
|
private static _services: Map<string, any> = new Map();
|
||||||
|
|
||||||
|
public static register<T>(name: string, service: T): void {
|
||||||
|
this._services.set(name, service);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static get<T>(name: string): T {
|
||||||
|
const service = this._services.get(name);
|
||||||
|
if (!service) {
|
||||||
|
throw new Error(`Service not found: ${name}`);
|
||||||
|
}
|
||||||
|
return service as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用示例
|
||||||
|
ServiceLocator.register('WindowManager', WindowManager);
|
||||||
|
const windowManager = ServiceLocator.get<typeof WindowManager>('WindowManager');
|
||||||
|
```
|
||||||
|
|
||||||
|
## 架构最佳实践
|
||||||
|
|
||||||
|
### 1. 职责分离
|
||||||
|
- 管理器负责全局状态和协调
|
||||||
|
- 基类提供共同功能和接口
|
||||||
|
- 具体实现类专注业务逻辑
|
||||||
|
|
||||||
|
### 2. 依赖管理
|
||||||
|
- 使用接口定义依赖契约
|
||||||
|
- 通过工厂和注入管理依赖关系
|
||||||
|
- 避免循环依赖
|
||||||
|
|
||||||
|
### 3. 扩展性设计
|
||||||
|
- 使用装饰器支持动态注册
|
||||||
|
- 提供插件化的扩展点
|
||||||
|
- 保持向后兼容性
|
||||||
|
|
||||||
|
### 4. 错误处理
|
||||||
|
- 在架构边界处理错误
|
||||||
|
- 提供降级和容错机制
|
||||||
|
- 记录详细的错误信息
|
||||||
171
.cursor/rules/cocos-creator.mdc
Normal file
171
.cursor/rules/cocos-creator.mdc
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
---
|
||||||
|
description: "Cocos Creator 3.x 开发规范和最佳实践"
|
||||||
|
globs: ["src/cocos/**/*.ts", "src/global/**/*.ts"]
|
||||||
|
alwaysApply: false
|
||||||
|
type: "framework-specific"
|
||||||
|
---
|
||||||
|
|
||||||
|
# Cocos Creator 3.x 开发规范
|
||||||
|
|
||||||
|
## 组件基类设计
|
||||||
|
|
||||||
|
### 继承 Component 的标准模式
|
||||||
|
```typescript
|
||||||
|
import { _decorator, Component } from "cc";
|
||||||
|
const { property } = _decorator;
|
||||||
|
|
||||||
|
export abstract class CocosEntry extends Component {
|
||||||
|
@property({ displayName: "uiConfig", type: JsonAsset, tooltip: "编辑器导出的UI配置" })
|
||||||
|
uiConfig: JsonAsset = null;
|
||||||
|
|
||||||
|
@property({ displayName: "游戏帧率" })
|
||||||
|
fps: number = 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 虚函数,子类需要实现
|
||||||
|
* kunpo库初始化完成后调用
|
||||||
|
*/
|
||||||
|
public abstract onInit(): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模块基类模式
|
||||||
|
```typescript
|
||||||
|
export abstract class ModuleBase extends Component implements IModule {
|
||||||
|
/** 模块名称 */
|
||||||
|
public moduleName: string;
|
||||||
|
|
||||||
|
/** 模块初始化 (内部使用) */
|
||||||
|
public init(): void { }
|
||||||
|
|
||||||
|
/** 模块初始化完成后调用的函数 */
|
||||||
|
protected abstract onInit(): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 生命周期管理
|
||||||
|
|
||||||
|
### 组件生命周期规范
|
||||||
|
- `start()`: 用于框架初始化,标记为 `@internal`
|
||||||
|
- `onInit()`: 用户自定义初始化,需要子类实现
|
||||||
|
- `onDestroy()`: 清理资源和取消事件监听
|
||||||
|
|
||||||
|
### 时间和更新管理
|
||||||
|
```typescript
|
||||||
|
// 使用统一的时间系统
|
||||||
|
import { GlobalTimer, InnerTimer } from "../global";
|
||||||
|
|
||||||
|
private initTime(): void {
|
||||||
|
Time._configBoot();
|
||||||
|
InnerTimer.initTimer();
|
||||||
|
GlobalTimer.initTimer();
|
||||||
|
this.schedule(this.tick.bind(this), 0, macro.REPEAT_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private tick(dt: number): void {
|
||||||
|
InnerTimer.update(dt);
|
||||||
|
GlobalTimer.update(dt);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 平台适配模式
|
||||||
|
|
||||||
|
### 平台检测和初始化
|
||||||
|
```typescript
|
||||||
|
private initPlatform(): void {
|
||||||
|
Platform.isNative = sys.isNative;
|
||||||
|
Platform.isMobile = sys.isMobile;
|
||||||
|
Platform.isNativeMobile = sys.isNative && sys.isMobile;
|
||||||
|
|
||||||
|
switch (sys.platform) {
|
||||||
|
case sys.Platform.WECHAT_GAME:
|
||||||
|
Platform.isWX = true;
|
||||||
|
Platform.platform = PlatformType.WX;
|
||||||
|
break;
|
||||||
|
case sys.Platform.ALIPAY_MINI_GAME:
|
||||||
|
Platform.isAlipay = true;
|
||||||
|
Platform.platform = PlatformType.Alipay;
|
||||||
|
break;
|
||||||
|
// ... 其他平台
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 平台特定功能
|
||||||
|
- 使用 `Platform` 类进行统一的平台判断
|
||||||
|
- 小游戏平台适配通过专门的适配类实现
|
||||||
|
- 避免在业务代码中直接使用 `sys` 对象
|
||||||
|
|
||||||
|
## 节点管理
|
||||||
|
|
||||||
|
### 持久化节点模式
|
||||||
|
```typescript
|
||||||
|
protected start(): void {
|
||||||
|
// 设置为持久化节点
|
||||||
|
director.addPersistRootNode(this.node);
|
||||||
|
this.node.setSiblingIndex(this.node.children.length - 1);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 组件查找模式
|
||||||
|
```typescript
|
||||||
|
private initModule(): void {
|
||||||
|
// 递归查找所有子节点中的模块组件
|
||||||
|
for (const module of this.getComponentsInChildren(ModuleBase)) {
|
||||||
|
debug(`module:${module.moduleName}`);
|
||||||
|
module.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 适配器模式
|
||||||
|
|
||||||
|
### 引擎适配器设计
|
||||||
|
- 通过 `CocosAdapter` 统一处理引擎特定功能
|
||||||
|
- UI系统通过 `CocosUIModule` 进行适配
|
||||||
|
- 窗口容器使用 `CocosWindowContainer`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
class CocosAdapter {
|
||||||
|
init(): void {
|
||||||
|
// 初始化引擎特定功能
|
||||||
|
this.initUI();
|
||||||
|
this.initScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 资源管理
|
||||||
|
|
||||||
|
### 配置文件处理
|
||||||
|
```typescript
|
||||||
|
// 使用 JsonAsset 处理配置
|
||||||
|
@property({ type: JsonAsset })
|
||||||
|
uiConfig: JsonAsset = null;
|
||||||
|
|
||||||
|
protected start(): void {
|
||||||
|
PropsHelper.setConfig(this.uiConfig?.json);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bundle 资源管理
|
||||||
|
- UI 资源通过 bundle 参数指定包名
|
||||||
|
- 支持动态资源加载和释放
|
||||||
|
- 使用资源池管理资源生命周期
|
||||||
|
|
||||||
|
## 调试和开发工具
|
||||||
|
|
||||||
|
### 全局调试接口
|
||||||
|
```typescript
|
||||||
|
// 暴露调试接口到全局对象
|
||||||
|
let _global = globalThis || window || global;
|
||||||
|
(_global as any)["getKunpoRegisterWindowMaps"] = function () {
|
||||||
|
return _uidecorator.getWindowMaps() as any;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 帧率控制
|
||||||
|
```typescript
|
||||||
|
// 统一的帧率设置
|
||||||
|
game.frameRate = this.fps;
|
||||||
|
```
|
||||||
347
.cursor/rules/data-binding.mdc
Normal file
347
.cursor/rules/data-binding.mdc
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
---
|
||||||
|
description: "强类型数据绑定系统开发规范"
|
||||||
|
globs: ["src/data/**/*.ts", "**/*Data*.ts"]
|
||||||
|
alwaysApply: false
|
||||||
|
type: "data-architecture"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 强类型数据绑定系统规范
|
||||||
|
|
||||||
|
## 数据基类设计
|
||||||
|
|
||||||
|
### DataBase 基类模式
|
||||||
|
```typescript
|
||||||
|
export class DataBase {
|
||||||
|
/** 数据变化监听器 */
|
||||||
|
private _watchers: Map<string, Function[]> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册属性监听器
|
||||||
|
* @param path 属性路径
|
||||||
|
* @param callback 回调函数
|
||||||
|
*/
|
||||||
|
public watch(path: string, callback: Function): void {
|
||||||
|
if (!this._watchers.has(path)) {
|
||||||
|
this._watchers.set(path, []);
|
||||||
|
}
|
||||||
|
this._watchers.get(path)!.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发属性变化通知
|
||||||
|
* @param path 属性路径
|
||||||
|
* @param value 新值
|
||||||
|
*/
|
||||||
|
protected notify(path: string, value: any): void {
|
||||||
|
if (this._watchers.has(path)) {
|
||||||
|
this._watchers.get(path)!.forEach(callback => {
|
||||||
|
callback(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据类定义规范
|
||||||
|
```typescript
|
||||||
|
class GameData extends DataBase {
|
||||||
|
private _level: number = 1;
|
||||||
|
private _coins: number = 0;
|
||||||
|
private _items: Item[] = [];
|
||||||
|
|
||||||
|
// 使用 getter/setter 实现响应式
|
||||||
|
get level(): number {
|
||||||
|
return this._level;
|
||||||
|
}
|
||||||
|
|
||||||
|
set level(value: number) {
|
||||||
|
if (this._level !== value) {
|
||||||
|
this._level = value;
|
||||||
|
this.notify('level', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get coins(): number {
|
||||||
|
return this._coins;
|
||||||
|
}
|
||||||
|
|
||||||
|
set coins(value: number) {
|
||||||
|
if (this._coins !== value) {
|
||||||
|
this._coins = value;
|
||||||
|
this.notify('coins', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复杂属性的变化通知
|
||||||
|
addItem(item: Item): void {
|
||||||
|
this._items.push(item);
|
||||||
|
this.notify('items', this._items);
|
||||||
|
this.notify('items.length', this._items.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 装饰器绑定系统
|
||||||
|
|
||||||
|
### 强类型属性绑定
|
||||||
|
```typescript
|
||||||
|
export namespace data {
|
||||||
|
/**
|
||||||
|
* 强类型属性绑定装饰器
|
||||||
|
* @param dataClass 数据类构造函数
|
||||||
|
* @param selector 类型安全的路径选择器
|
||||||
|
* @param callback 变化回调函数
|
||||||
|
* @param immediate 是否立即触发
|
||||||
|
*/
|
||||||
|
export function bindProp<T extends DataBase>(
|
||||||
|
dataClass: new () => T,
|
||||||
|
selector: (data: T) => any,
|
||||||
|
callback: (item: any, value?: any, data?: T) => void,
|
||||||
|
immediate: boolean = false
|
||||||
|
) {
|
||||||
|
return function (target: any, prop: string | symbol) {
|
||||||
|
const path = `${dataClass.name}:${extractPathFromSelector(selector)}`;
|
||||||
|
|
||||||
|
let ctor = target.constructor;
|
||||||
|
ctor[BIND_METADATA_KEY] = ctor[BIND_METADATA_KEY] || [];
|
||||||
|
ctor[BIND_METADATA_KEY].push({
|
||||||
|
prop, callback, path, immediate, isMethod: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方法绑定装饰器
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 强类型方法绑定装饰器
|
||||||
|
* @param dataClass 数据类构造函数
|
||||||
|
* @param selector 类型安全的路径选择器
|
||||||
|
* @param immediate 是否立即触发
|
||||||
|
*/
|
||||||
|
export function bindMethod<T extends DataBase>(
|
||||||
|
dataClass: new () => T,
|
||||||
|
selector: (data: T) => any,
|
||||||
|
immediate: boolean = false
|
||||||
|
) {
|
||||||
|
return function (target: any, method: string | symbol, descriptor?: PropertyDescriptor) {
|
||||||
|
const path = `${dataClass.name}:${extractPathFromSelector(selector)}`;
|
||||||
|
|
||||||
|
let ctor = target.constructor;
|
||||||
|
ctor[BIND_METADATA_KEY] = ctor[BIND_METADATA_KEY] || [];
|
||||||
|
ctor[BIND_METADATA_KEY].push({
|
||||||
|
prop: method,
|
||||||
|
callback: descriptor!.value,
|
||||||
|
path,
|
||||||
|
immediate,
|
||||||
|
isMethod: true
|
||||||
|
});
|
||||||
|
return descriptor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
### UI 数据绑定示例
|
||||||
|
```typescript
|
||||||
|
class PlayerData extends DataBase {
|
||||||
|
name: string = "";
|
||||||
|
level: number = 1;
|
||||||
|
exp: number = 0;
|
||||||
|
maxExp: number = 100;
|
||||||
|
|
||||||
|
// 计算属性
|
||||||
|
get expProgress(): number {
|
||||||
|
return this.exp / this.maxExp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@uiclass("main", "player", "PlayerPanel")
|
||||||
|
export class PlayerPanel extends Window {
|
||||||
|
@uiprop nameLabel: GLabel;
|
||||||
|
@uiprop levelLabel: GLabel;
|
||||||
|
@uiprop expBar: GProgressBar;
|
||||||
|
|
||||||
|
// 绑定玩家名称到标签
|
||||||
|
@data.bindProp(PlayerData, data => data.name, function(item, value) {
|
||||||
|
this.nameLabel.text = value;
|
||||||
|
})
|
||||||
|
private _nameBinding: any;
|
||||||
|
|
||||||
|
// 绑定等级显示
|
||||||
|
@data.bindMethod(PlayerData, data => data.level)
|
||||||
|
private onLevelChanged(value: number): void {
|
||||||
|
this.levelLabel.text = `Lv.${value}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绑定经验条
|
||||||
|
@data.bindMethod(PlayerData, data => data.expProgress)
|
||||||
|
private onExpChanged(progress: number): void {
|
||||||
|
this.expBar.value = progress * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onInit(): void {
|
||||||
|
// 初始化绑定
|
||||||
|
data.initializeBindings(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onClose(): void {
|
||||||
|
// 清理绑定
|
||||||
|
data.cleanupBindings(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 绑定管理器
|
||||||
|
|
||||||
|
### BindManager 设计
|
||||||
|
```typescript
|
||||||
|
export class BindManager {
|
||||||
|
private static _bindings: Map<string, BindInfo[]> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加绑定信息
|
||||||
|
*/
|
||||||
|
public static addBinding(bindInfo: BindInfo): void {
|
||||||
|
const key = bindInfo.path;
|
||||||
|
if (!this._bindings.has(key)) {
|
||||||
|
this._bindings.set(key, []);
|
||||||
|
}
|
||||||
|
this._bindings.get(key)!.push(bindInfo);
|
||||||
|
|
||||||
|
// 如果需要立即触发
|
||||||
|
if (bindInfo.immediate) {
|
||||||
|
this.triggerBinding(bindInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清理目标对象的绑定
|
||||||
|
*/
|
||||||
|
public static cleanup(target: any): void {
|
||||||
|
this._bindings.forEach((bindings, path) => {
|
||||||
|
const newBindings = bindings.filter(binding => binding.target !== target);
|
||||||
|
if (newBindings.length === 0) {
|
||||||
|
this._bindings.delete(path);
|
||||||
|
} else {
|
||||||
|
this._bindings.set(path, newBindings);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 触发路径对应的所有绑定
|
||||||
|
*/
|
||||||
|
public static notifyChange(path: string, value: any, data: any): void {
|
||||||
|
const bindings = this._bindings.get(path);
|
||||||
|
if (bindings) {
|
||||||
|
bindings.forEach(binding => {
|
||||||
|
try {
|
||||||
|
if (binding.isMethod) {
|
||||||
|
binding.callback.call(binding.target, value, data);
|
||||||
|
} else {
|
||||||
|
binding.callback.call(binding.target, binding.target[binding.prop], value, data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`绑定回调执行错误: ${path}`, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 路径解析器
|
||||||
|
|
||||||
|
### 路径提取函数
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 从选择器函数中提取路径字符串
|
||||||
|
* 支持编译期类型检查的运行时路径解析
|
||||||
|
*/
|
||||||
|
function extractPathFromSelector(selector: Function): string {
|
||||||
|
const fnString = selector.toString();
|
||||||
|
|
||||||
|
// 匹配箭头函数: data => data.property.path
|
||||||
|
let match = fnString.match(/\w+\s*=>\s*\w+\.(.+)/);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
// 匹配普通函数: function(data) { return data.property.path; }
|
||||||
|
match = fnString.match(/return\s+\w+\.(.+);?\s*}/);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw new Error('无效的路径选择器函数,请使用 data => data.property.path 格式');
|
||||||
|
}
|
||||||
|
|
||||||
|
return match[1].trim();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 批量更新优化
|
||||||
|
|
||||||
|
### BatchUpdater 设计
|
||||||
|
```typescript
|
||||||
|
export class BatchUpdater {
|
||||||
|
private static _pendingUpdates: Set<string> = new Set();
|
||||||
|
private static _updateTimer: number = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量更新通知
|
||||||
|
* @param path 变化路径
|
||||||
|
* @param value 新值
|
||||||
|
* @param data 数据对象
|
||||||
|
*/
|
||||||
|
public static scheduleUpdate(path: string, value: any, data: any): void {
|
||||||
|
this._pendingUpdates.add(path);
|
||||||
|
|
||||||
|
if (this._updateTimer === 0) {
|
||||||
|
this._updateTimer = requestAnimationFrame(() => {
|
||||||
|
this.flushUpdates();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行批量更新
|
||||||
|
*/
|
||||||
|
private static flushUpdates(): void {
|
||||||
|
this._pendingUpdates.forEach(path => {
|
||||||
|
// 获取最新值并触发更新
|
||||||
|
const [dataClassName, propertyPath] = path.split(':');
|
||||||
|
const dataInstance = DataRegistry.getInstance(dataClassName);
|
||||||
|
if (dataInstance) {
|
||||||
|
const value = this.getValueByPath(dataInstance, propertyPath);
|
||||||
|
BindManager.notifyChange(path, value, dataInstance);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._pendingUpdates.clear();
|
||||||
|
this._updateTimer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据绑定最佳实践
|
||||||
|
|
||||||
|
### 1. 类型安全
|
||||||
|
- 使用泛型约束确保绑定的类型安全
|
||||||
|
- 路径选择器函数提供编译期检查
|
||||||
|
- 避免字符串路径,优先使用选择器函数
|
||||||
|
|
||||||
|
### 2. 性能优化
|
||||||
|
- 使用批量更新减少频繁触发
|
||||||
|
- 及时清理不需要的绑定
|
||||||
|
- 避免在绑定回调中进行重度计算
|
||||||
|
|
||||||
|
### 3. 内存管理
|
||||||
|
- 在组件销毁时调用 `data.cleanupBindings()`
|
||||||
|
- 避免循环引用导致的内存泄漏
|
||||||
|
- 使用弱引用管理临时绑定
|
||||||
|
|
||||||
|
### 4. 调试支持
|
||||||
|
- 提供绑定信息的调试接口
|
||||||
|
- 记录绑定的创建和销毁日志
|
||||||
|
- 支持绑定状态的实时监控
|
||||||
276
.cursor/rules/decorator-patterns.mdc
Normal file
276
.cursor/rules/decorator-patterns.mdc
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
---
|
||||||
|
description: "装饰器模式和元数据管理规范"
|
||||||
|
globs: ["**/*Decorator*.ts", "**/*decorator*.ts"]
|
||||||
|
alwaysApply: false
|
||||||
|
type: "design-pattern"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 装饰器模式开发规范
|
||||||
|
|
||||||
|
## 装饰器设计原则
|
||||||
|
|
||||||
|
### namespace 封装模式
|
||||||
|
```typescript
|
||||||
|
export namespace _uidecorator {
|
||||||
|
/** 元数据存储键 */
|
||||||
|
const UIPropMeta = "__uipropmeta__";
|
||||||
|
const UICBMeta = "__uicbmeta__";
|
||||||
|
|
||||||
|
/** 注册映射 */
|
||||||
|
const uiclassMap: Map<any, UIWindowInfo> = new Map();
|
||||||
|
|
||||||
|
/** 对外接口 */
|
||||||
|
export function getWindowMaps(): Map<any, UIWindowInfo> {
|
||||||
|
return uiclassMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 装饰器工厂模式
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 类装饰器工厂
|
||||||
|
* @param groupName 窗口组名称
|
||||||
|
* @param pkgName fgui包名
|
||||||
|
* @param name 窗口名
|
||||||
|
* @param bundle 可选bundle名
|
||||||
|
*/
|
||||||
|
export function uiclass(groupName: string, pkgName: string, name: string, bundle?: string): Function {
|
||||||
|
return function (ctor: any): any {
|
||||||
|
// 元数据收集
|
||||||
|
uiclassMap.set(ctor, {
|
||||||
|
ctor: ctor,
|
||||||
|
props: ctor[UIPropMeta] || null,
|
||||||
|
callbacks: ctor[UICBMeta] || null,
|
||||||
|
res: {
|
||||||
|
group: groupName,
|
||||||
|
pkg: pkgName,
|
||||||
|
name: name,
|
||||||
|
bundle: bundle || ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 动态注册支持
|
||||||
|
_registerFinish && WindowManager.dynamicRegisterWindow(ctor, groupName, pkgName, name, bundle || "");
|
||||||
|
return ctor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 属性装饰器模式
|
||||||
|
|
||||||
|
### 属性标记装饰器
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* UI属性装饰器
|
||||||
|
* @param target 实例成员的类的原型
|
||||||
|
* @param name 属性名
|
||||||
|
*/
|
||||||
|
export function uiprop(target: Object, name: string): any {
|
||||||
|
ObjectHelper.getObjectProp(target.constructor, UIPropMeta)[name] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI控制器装饰器
|
||||||
|
*/
|
||||||
|
export function uicontrol(target: Object, name: string): any {
|
||||||
|
ObjectHelper.getObjectProp(target.constructor, UIControlMeta)[name] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI动画装饰器
|
||||||
|
*/
|
||||||
|
export function uitransition(target: Object, name: string): any {
|
||||||
|
ObjectHelper.getObjectProp(target.constructor, UITransitionMeta)[name] = 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
```typescript
|
||||||
|
@uiclass("popup", "common", "SettingsWindow")
|
||||||
|
export class SettingsWindow extends Window {
|
||||||
|
@uiprop
|
||||||
|
btnClose: GButton;
|
||||||
|
|
||||||
|
@uiprop
|
||||||
|
list: GList;
|
||||||
|
|
||||||
|
@uicontrol
|
||||||
|
tabController: GController;
|
||||||
|
|
||||||
|
@uitransition
|
||||||
|
showAnim: GTransition;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 方法装饰器模式
|
||||||
|
|
||||||
|
### 方法绑定装饰器
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 点击事件装饰器
|
||||||
|
* @param target 实例成员的类的原型
|
||||||
|
* @param name 方法名
|
||||||
|
* @param descriptor 属性描述符
|
||||||
|
*/
|
||||||
|
export function uiclick(target: Object, name: string, descriptor: PropertyDescriptor): void {
|
||||||
|
ObjectHelper.getObjectProp(target.constructor, UICBMeta)[name] = descriptor.value;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
```typescript
|
||||||
|
export class SettingsWindow extends Window {
|
||||||
|
@uiclick
|
||||||
|
private onBtnCloseClick(): void {
|
||||||
|
WindowManager.closeWindow("SettingsWindow");
|
||||||
|
}
|
||||||
|
|
||||||
|
@uiclick
|
||||||
|
private onBtnSaveClick(): void {
|
||||||
|
this.saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据绑定装饰器
|
||||||
|
|
||||||
|
### 强类型绑定装饰器
|
||||||
|
```typescript
|
||||||
|
export namespace data {
|
||||||
|
const BIND_METADATA_KEY = Symbol('__bind_metadata__');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性绑定装饰器
|
||||||
|
* @param dataClass 数据类构造函数
|
||||||
|
* @param selector 路径选择器函数
|
||||||
|
* @param callback 回调函数
|
||||||
|
* @param immediate 是否立即触发
|
||||||
|
*/
|
||||||
|
export function bindProp<T extends DataBase>(
|
||||||
|
dataClass: new () => T,
|
||||||
|
selector: (data: T) => any,
|
||||||
|
callback: (item: any, value?: any, data?: T) => void,
|
||||||
|
immediate: boolean = false
|
||||||
|
) {
|
||||||
|
return function (target: any, prop: string | symbol) {
|
||||||
|
const path = `${dataClass.name}:${extractPathFromSelector(selector)}`;
|
||||||
|
|
||||||
|
let ctor = target.constructor;
|
||||||
|
ctor[BIND_METADATA_KEY] = ctor[BIND_METADATA_KEY] || [];
|
||||||
|
ctor[BIND_METADATA_KEY].push({
|
||||||
|
prop, callback, path, immediate, isMethod: false
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方法绑定装饰器
|
||||||
|
*/
|
||||||
|
export function bindMethod<T extends DataBase>(
|
||||||
|
dataClass: new () => T,
|
||||||
|
selector: (data: T) => any,
|
||||||
|
immediate: boolean = false
|
||||||
|
) {
|
||||||
|
return function (target: any, method: string | symbol, descriptor?: PropertyDescriptor) {
|
||||||
|
const path = `${dataClass.name}:${extractPathFromSelector(selector)}`;
|
||||||
|
|
||||||
|
let ctor = target.constructor;
|
||||||
|
ctor[BIND_METADATA_KEY] = ctor[BIND_METADATA_KEY] || [];
|
||||||
|
ctor[BIND_METADATA_KEY].push({
|
||||||
|
prop: method,
|
||||||
|
callback: descriptor!.value,
|
||||||
|
path, immediate,
|
||||||
|
isMethod: true
|
||||||
|
});
|
||||||
|
return descriptor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据绑定使用示例
|
||||||
|
```typescript
|
||||||
|
class GameData extends DataBase {
|
||||||
|
level: number = 1;
|
||||||
|
coins: number = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GameUI extends Window {
|
||||||
|
@uiprop
|
||||||
|
levelLabel: GLabel;
|
||||||
|
|
||||||
|
@uiprop
|
||||||
|
coinsLabel: GLabel;
|
||||||
|
|
||||||
|
// 绑定属性到UI
|
||||||
|
@data.bindProp(GameData, data => data.level, function(item, value) {
|
||||||
|
this.levelLabel.text = `Level: ${value}`;
|
||||||
|
})
|
||||||
|
private _levelBinding: any;
|
||||||
|
|
||||||
|
// 绑定方法到数据变化
|
||||||
|
@data.bindMethod(GameData, data => data.coins)
|
||||||
|
private onCoinsChanged(value: number): void {
|
||||||
|
this.coinsLabel.text = `Coins: ${value}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 条件装饰器
|
||||||
|
|
||||||
|
### 条件注册装饰器
|
||||||
|
```typescript
|
||||||
|
export namespace _conditionDecorator {
|
||||||
|
const cdClassMap: Map<number, any> = new Map();
|
||||||
|
|
||||||
|
export function getConditionMaps(): Map<number, any> {
|
||||||
|
return cdClassMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件装饰器
|
||||||
|
* @param conditionType 条件类型ID
|
||||||
|
*/
|
||||||
|
export function conditionClass(conditionType: number): Function {
|
||||||
|
return function (ctor: any): void {
|
||||||
|
cdClassMap.set(conditionType, ctor);
|
||||||
|
return ctor;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 条件使用示例
|
||||||
|
```typescript
|
||||||
|
@conditionClass(1001)
|
||||||
|
export class LevelCondition extends ConditionBase {
|
||||||
|
protected check(): boolean {
|
||||||
|
return GameData.getInstance().level >= this.targetLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 装饰器最佳实践
|
||||||
|
|
||||||
|
### 元数据收集模式
|
||||||
|
1. 使用 Symbol 作为元数据键避免冲突
|
||||||
|
2. 在类原型上存储元数据信息
|
||||||
|
3. 提供统一的元数据访问接口
|
||||||
|
|
||||||
|
### 动态注册支持
|
||||||
|
```typescript
|
||||||
|
let _registerFinish: boolean = false;
|
||||||
|
|
||||||
|
export function setRegisterFinish(): void {
|
||||||
|
_registerFinish = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在装饰器中支持动态注册
|
||||||
|
_registerFinish && WindowManager.dynamicRegisterWindow(ctor, groupName, pkgName, name, bundle);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 类型安全
|
||||||
|
- 使用泛型约束确保装饰器类型安全
|
||||||
|
- 提供明确的参数类型定义
|
||||||
|
- 避免使用 any 类型,优先使用具体类型
|
||||||
256
.cursor/rules/fairygui.mdc
Normal file
256
.cursor/rules/fairygui.mdc
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
---
|
||||||
|
description: "FairyGUI UI 系统开发规范"
|
||||||
|
globs: ["src/fgui/**/*.ts", "src/ui/**/*.ts"]
|
||||||
|
alwaysApply: false
|
||||||
|
type: "ui-framework"
|
||||||
|
---
|
||||||
|
|
||||||
|
# FairyGUI UI 系统开发规范
|
||||||
|
|
||||||
|
## 窗口基类设计模式
|
||||||
|
|
||||||
|
### 窗口继承层次
|
||||||
|
```typescript
|
||||||
|
// 基础窗口类 - 提供核心功能
|
||||||
|
export abstract class WindowBase extends Component {
|
||||||
|
// 核心窗口管理逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抽象窗口类 - 定义生命周期
|
||||||
|
export abstract class Window extends WindowBase {
|
||||||
|
protected abstract onInit(): void;
|
||||||
|
protected onClose(): void { }
|
||||||
|
protected onShow(userdata?: any): void { }
|
||||||
|
protected onHide(): void { }
|
||||||
|
protected onShowFromHide(): void { }
|
||||||
|
protected onCover(): void { }
|
||||||
|
protected onRecover(): void { }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 窗口生命周期管理
|
||||||
|
- `onInit()`: 窗口初始化,必须实现
|
||||||
|
- `onShow()`: 窗口显示时调用
|
||||||
|
- `onHide()`: 窗口隐藏时调用
|
||||||
|
- `onClose()`: 窗口关闭时调用
|
||||||
|
- `onCover()`: 被其他窗口覆盖时调用
|
||||||
|
- `onRecover()`: 从覆盖状态恢复时调用
|
||||||
|
- `onShowFromHide()`: 从隐藏状态重新显示时调用
|
||||||
|
|
||||||
|
## 窗口管理器模式
|
||||||
|
|
||||||
|
### 静态管理器设计
|
||||||
|
```typescript
|
||||||
|
export class WindowManager {
|
||||||
|
/** 窗口组映射 */
|
||||||
|
private static _groups: Map<string, WindowGroup> = new Map();
|
||||||
|
/** 所有窗口映射 */
|
||||||
|
private static _windows: Map<string, IWindow> = new Map();
|
||||||
|
/** 资源池 */
|
||||||
|
private static _resPool: WindowResPool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步显示窗口(自动加载资源)
|
||||||
|
*/
|
||||||
|
public static showWindow(windowName: string, userdata?: any): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._resPool.loadWindowRes(windowName, {
|
||||||
|
complete: () => {
|
||||||
|
this.showWindowIm(windowName, userdata);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
fail: (pkgs: string[]) => reject(pkgs)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 立即显示窗口(资源已加载)
|
||||||
|
*/
|
||||||
|
public static showWindowIm(windowName: string, userdata?: any): void {
|
||||||
|
const info = this._resPool.get(windowName);
|
||||||
|
const windowGroup = this.getWindowGroup(info.group);
|
||||||
|
this._resPool.addResRef(windowName);
|
||||||
|
windowGroup.showWindow(info, userdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 窗口组管理
|
||||||
|
- 使用 `WindowGroup` 管理同类型窗口
|
||||||
|
- 支持窗口层级和遮挡关系
|
||||||
|
- 自动处理窗口恢复和覆盖
|
||||||
|
|
||||||
|
## 装饰器系统
|
||||||
|
|
||||||
|
### UI 装饰器使用规范
|
||||||
|
```typescript
|
||||||
|
// 窗口类装饰器
|
||||||
|
@uiclass("popup", "common", "SettingsWindow", "ui")
|
||||||
|
export class SettingsWindow extends Window {
|
||||||
|
|
||||||
|
// UI 属性装饰器
|
||||||
|
@uiprop
|
||||||
|
btnClose: GButton;
|
||||||
|
|
||||||
|
@uiprop
|
||||||
|
list: GList;
|
||||||
|
|
||||||
|
// UI 控制器装饰器
|
||||||
|
@uicontrol
|
||||||
|
controller: GController;
|
||||||
|
|
||||||
|
// UI 动画装饰器
|
||||||
|
@uitransition
|
||||||
|
showTransition: GTransition;
|
||||||
|
|
||||||
|
// 点击事件装饰器
|
||||||
|
@uiclick
|
||||||
|
private onBtnCloseClick(): void {
|
||||||
|
WindowManager.closeWindow("SettingsWindow");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件装饰器
|
||||||
|
@uicom("common", "CustomButton")
|
||||||
|
export class CustomButton extends GButton {
|
||||||
|
// 自定义组件逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header 装饰器
|
||||||
|
@uiheader("common", "CommonHeader", "ui")
|
||||||
|
export class CommonHeader extends WindowHeader {
|
||||||
|
// 通用头部逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 装饰器参数规范
|
||||||
|
- `@uiclass(group, pkg, name, bundle?)`: 窗口类注册
|
||||||
|
- `group`: 窗口组名
|
||||||
|
- `pkg`: FairyGUI 包名
|
||||||
|
- `name`: 组件名(与 FairyGUI 中一致)
|
||||||
|
- `bundle`: 可选的 bundle 名称
|
||||||
|
- `@uicom(pkg, name)`: 自定义组件注册
|
||||||
|
- `@uiheader(pkg, name, bundle?)`: 窗口头部注册
|
||||||
|
|
||||||
|
## 资源管理模式
|
||||||
|
|
||||||
|
### 资源池设计
|
||||||
|
```typescript
|
||||||
|
export class WindowResPool {
|
||||||
|
private _windowInfos: Map<string, WindowHeaderInfo> = new Map();
|
||||||
|
private _headerInfos: Map<string, WindowHeaderInfo> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载窗口资源
|
||||||
|
*/
|
||||||
|
loadWindowRes(windowName: string, callbacks: {
|
||||||
|
complete: () => void;
|
||||||
|
fail: (pkgs: string[]) => void;
|
||||||
|
}): void {
|
||||||
|
// 检查包资源是否已加载
|
||||||
|
// 自动加载依赖的UI包
|
||||||
|
// 调用相应的回调
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 释放窗口资源
|
||||||
|
*/
|
||||||
|
releaseWindowRes(windowName: string): void {
|
||||||
|
// 减少引用计数
|
||||||
|
// 必要时卸载资源
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 包配置管理
|
||||||
|
```typescript
|
||||||
|
interface IPackageConfigRes {
|
||||||
|
[windowName: string]: {
|
||||||
|
group: string;
|
||||||
|
pkg: string;
|
||||||
|
bundle?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化包配置
|
||||||
|
WindowManager.initPackageConfig(packageConfig);
|
||||||
|
```
|
||||||
|
|
||||||
|
## UI 组件扩展
|
||||||
|
|
||||||
|
### 组件扩展模式
|
||||||
|
```typescript
|
||||||
|
export class ComponentExtendHelper {
|
||||||
|
private static _componentMaps: Map<string, any> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册自定义组件
|
||||||
|
*/
|
||||||
|
public static register(): void {
|
||||||
|
for (const { ctor, res } of _uidecorator.getComponentMaps().values()) {
|
||||||
|
UIObjectFactory.setPackageItemExtension(
|
||||||
|
`ui://${res.pkg}/${res.name}`,
|
||||||
|
ctor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态注册组件
|
||||||
|
*/
|
||||||
|
public static dynamicRegister(ctor: any, pkg: string, name: string): void {
|
||||||
|
UIObjectFactory.setPackageItemExtension(
|
||||||
|
`ui://${pkg}/${name}`,
|
||||||
|
ctor
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 窗口头部系统
|
||||||
|
|
||||||
|
### WindowHeader 模式
|
||||||
|
```typescript
|
||||||
|
export class WindowHeader {
|
||||||
|
/** 头部组件实例 */
|
||||||
|
protected _header: GComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建头部
|
||||||
|
*/
|
||||||
|
public createHeader(pkg: string, name: string): GComponent {
|
||||||
|
this._header = UIPackage.createObject(pkg, name).asCom;
|
||||||
|
return this._header;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 头部适配
|
||||||
|
*/
|
||||||
|
public adapter(window: GComponent): void {
|
||||||
|
// 头部适配逻辑
|
||||||
|
// 处理安全区域
|
||||||
|
// 设置头部位置和尺寸
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 屏幕适配
|
||||||
|
|
||||||
|
### 屏幕尺寸变化处理
|
||||||
|
```typescript
|
||||||
|
// 在 WindowManager 中处理屏幕变化
|
||||||
|
public static _screenResize(): void {
|
||||||
|
this._windows.forEach((window: IWindow) => {
|
||||||
|
window.screenResize();
|
||||||
|
});
|
||||||
|
this._groups.forEach((group: WindowGroup) => {
|
||||||
|
group._screenResize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在窗口中实现屏幕适配
|
||||||
|
protected screenResize(): void {
|
||||||
|
// 处理窗口在屏幕尺寸变化时的适配逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
413
.cursor/rules/hot-update.mdc
Normal file
413
.cursor/rules/hot-update.mdc
Normal file
@@ -0,0 +1,413 @@
|
|||||||
|
---
|
||||||
|
description: "热更新系统开发规范"
|
||||||
|
globs: ["src/hotupdate/**/*.ts"]
|
||||||
|
alwaysApply: false
|
||||||
|
type: "system-module"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 热更新系统开发规范
|
||||||
|
|
||||||
|
## 热更新架构设计
|
||||||
|
|
||||||
|
### 管理器单例模式
|
||||||
|
```typescript
|
||||||
|
export class HotUpdateManager {
|
||||||
|
private static _instance: HotUpdateManager;
|
||||||
|
|
||||||
|
/** 获取单例实例 */
|
||||||
|
public static getInstance(): HotUpdateManager {
|
||||||
|
if (!this._instance) {
|
||||||
|
this._instance = new HotUpdateManager();
|
||||||
|
}
|
||||||
|
return this._instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 禁用直接构造 */
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/** 配置属性 */
|
||||||
|
public manifestUrl: string = "";
|
||||||
|
public versionUrl: string = "";
|
||||||
|
|
||||||
|
/** 初始化热更新 */
|
||||||
|
public init(manifestUrl: string, versionUrl: string): void {
|
||||||
|
this.manifestUrl = manifestUrl;
|
||||||
|
this.versionUrl = versionUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 热更新状态码定义
|
||||||
|
```typescript
|
||||||
|
export enum HotUpdateCode {
|
||||||
|
/** 成功 */
|
||||||
|
Succeed = 0,
|
||||||
|
/** 已是最新版本 */
|
||||||
|
LatestVersion = 1,
|
||||||
|
/** 检查更新失败 */
|
||||||
|
CheckFailed = 2,
|
||||||
|
/** 下载失败 */
|
||||||
|
DownloadFailed = 3,
|
||||||
|
/** 解压失败 */
|
||||||
|
UnzipFailed = 4,
|
||||||
|
/** 网络错误 */
|
||||||
|
NetworkError = 5,
|
||||||
|
/** 空间不足 */
|
||||||
|
NoSpace = 6,
|
||||||
|
/** 未知错误 */
|
||||||
|
UnknownError = 7
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Promise 结果模式
|
||||||
|
|
||||||
|
### 统一结果接口
|
||||||
|
```typescript
|
||||||
|
export interface IPromiseResult {
|
||||||
|
/** 状态码 */
|
||||||
|
code: HotUpdateCode;
|
||||||
|
|
||||||
|
/** 消息描述 */
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
/** 扩展数据 */
|
||||||
|
data?: any;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 热更新配置接口
|
||||||
|
```typescript
|
||||||
|
export interface IHotUpdateConfig {
|
||||||
|
/** 版本号 */
|
||||||
|
version: string;
|
||||||
|
|
||||||
|
/** 远程manifest文件URL */
|
||||||
|
remoteManifestUrl: string;
|
||||||
|
|
||||||
|
/** 远程version文件URL */
|
||||||
|
remoteVersionUrl: string;
|
||||||
|
|
||||||
|
/** 资源包URL */
|
||||||
|
packageUrl: string;
|
||||||
|
|
||||||
|
/** 资源文件列表 */
|
||||||
|
assets?: { [key: string]: any };
|
||||||
|
|
||||||
|
/** 搜索路径 */
|
||||||
|
searchPaths?: string[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 热更新核心实现
|
||||||
|
|
||||||
|
### HotUpdate 核心类
|
||||||
|
```typescript
|
||||||
|
export class HotUpdate {
|
||||||
|
private _am: jsb.AssetsManager;
|
||||||
|
private _updating: boolean = false;
|
||||||
|
|
||||||
|
constructor(manifestUrl: string) {
|
||||||
|
// 初始化 AssetsManager
|
||||||
|
this._am = new jsb.AssetsManager(manifestUrl, jsb.fileUtils.getWritablePath() + 'remote-assets');
|
||||||
|
this._am.setEventCallback(this.onUpdateEvent.bind(this));
|
||||||
|
this._am.setVerifyCallback(this.onVerifyCallback.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查更新
|
||||||
|
* @returns Promise<IPromiseResult>
|
||||||
|
*/
|
||||||
|
public checkUpdate(): Promise<IPromiseResult> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (this._updating) {
|
||||||
|
resolve({ code: HotUpdateCode.UnknownError, message: "正在更新中" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._am.getLocalManifest() || !this._am.getLocalManifest().isLoaded()) {
|
||||||
|
resolve({ code: HotUpdateCode.CheckFailed, message: "本地manifest加载失败" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._am.setEventCallback((event) => {
|
||||||
|
switch (event.getEventCode()) {
|
||||||
|
case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
|
||||||
|
resolve({ code: HotUpdateCode.CheckFailed, message: "本地manifest不存在" });
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
|
||||||
|
resolve({ code: HotUpdateCode.NetworkError, message: "下载manifest失败" });
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
|
||||||
|
resolve({ code: HotUpdateCode.LatestVersion, message: "已是最新版本" });
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.NEW_VERSION_FOUND:
|
||||||
|
resolve({ code: HotUpdateCode.Succeed, message: "发现新版本" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._am.checkUpdate();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行热更新
|
||||||
|
* @returns Promise<IPromiseResult>
|
||||||
|
*/
|
||||||
|
public hotUpdate(): Promise<IPromiseResult> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
if (this._updating) {
|
||||||
|
resolve({ code: HotUpdateCode.UnknownError, message: "正在更新中" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updating = true;
|
||||||
|
|
||||||
|
this._am.setEventCallback((event) => {
|
||||||
|
switch (event.getEventCode()) {
|
||||||
|
case jsb.EventAssetsManager.UPDATE_FINISHED:
|
||||||
|
this._updating = false;
|
||||||
|
resolve({ code: HotUpdateCode.Succeed, message: "更新完成" });
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.UPDATE_FAILED:
|
||||||
|
this._updating = false;
|
||||||
|
resolve({ code: HotUpdateCode.DownloadFailed, message: "更新失败" });
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ERROR_DECOMPRESS:
|
||||||
|
this._updating = false;
|
||||||
|
resolve({ code: HotUpdateCode.UnzipFailed, message: "解压失败" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._am.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件回调处理
|
||||||
|
*/
|
||||||
|
private onUpdateEvent(event: jsb.EventAssetsManager): void {
|
||||||
|
const code = event.getEventCode();
|
||||||
|
debug(`热更新事件: ${code}`);
|
||||||
|
|
||||||
|
switch (code) {
|
||||||
|
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
|
||||||
|
const progress = event.getPercent();
|
||||||
|
debug(`更新进度: ${progress}%`);
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.ASSET_UPDATED:
|
||||||
|
const assetId = event.getAssetId();
|
||||||
|
debug(`资源更新: ${assetId}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证回调
|
||||||
|
*/
|
||||||
|
private onVerifyCallback(path: string, asset: any): boolean {
|
||||||
|
// 资源验证逻辑
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## manifest 管理
|
||||||
|
|
||||||
|
### 本地 manifest 刷新
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* 替换 project.manifest 中的内容并刷新本地manifest
|
||||||
|
*/
|
||||||
|
private refreshLocalManifest(manifest: IHotUpdateConfig, versionManifest: IHotUpdateConfig): Promise<IPromiseResult> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// 版本比较
|
||||||
|
if (Utils.compareVersion(manifest.version, versionManifest.version) >= 0) {
|
||||||
|
resolve({ code: HotUpdateCode.LatestVersion, message: "已是最新版本" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 manifest 配置
|
||||||
|
manifest.remoteManifestUrl = Utils.addUrlParam(versionManifest.remoteManifestUrl, "timeStamp", `${Time.now()}`);
|
||||||
|
manifest.remoteVersionUrl = Utils.addUrlParam(versionManifest.remoteVersionUrl, "timeStamp", `${Time.now()}`);
|
||||||
|
manifest.packageUrl = versionManifest.packageUrl;
|
||||||
|
|
||||||
|
// 计算 manifest 根目录
|
||||||
|
let manifestRoot = "";
|
||||||
|
let manifestUrl = HotUpdateManager.getInstance().manifestUrl;
|
||||||
|
let found = manifestUrl.lastIndexOf("/");
|
||||||
|
if (found === -1) {
|
||||||
|
found = manifestUrl.lastIndexOf("\\");
|
||||||
|
}
|
||||||
|
if (found !== -1) {
|
||||||
|
manifestRoot = manifestUrl.substring(0, found + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析并设置本地 manifest
|
||||||
|
this._am.getLocalManifest().parseJSONString(JSON.stringify(manifest), manifestRoot);
|
||||||
|
|
||||||
|
resolve({ code: HotUpdateCode.Succeed, message: "更新热更新配置成功" });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 版本比较工具
|
||||||
|
|
||||||
|
### 版本号比较函数
|
||||||
|
```typescript
|
||||||
|
export class Utils {
|
||||||
|
/**
|
||||||
|
* 版本号比较
|
||||||
|
* @param versionA 版本A
|
||||||
|
* @param versionB 版本B
|
||||||
|
* @returns 0: 相等, 1: A > B, -1: A < B
|
||||||
|
*/
|
||||||
|
public static compareVersion(versionA: string, versionB: string): number {
|
||||||
|
const a = versionA.split('.');
|
||||||
|
const b = versionB.split('.');
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
||||||
|
const numA = parseInt(a[i] || '0', 10);
|
||||||
|
const numB = parseInt(b[i] || '0', 10);
|
||||||
|
|
||||||
|
if (numA > numB) return 1;
|
||||||
|
if (numA < numB) return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给URL添加参数
|
||||||
|
*/
|
||||||
|
public static addUrlParam(url: string, key: string, value: string): string {
|
||||||
|
const separator = url.indexOf('?') !== -1 ? '&' : '?';
|
||||||
|
return `${url}${separator}${key}=${encodeURIComponent(value)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 进度监控
|
||||||
|
|
||||||
|
### 更新进度回调
|
||||||
|
```typescript
|
||||||
|
export interface HotUpdateProgress {
|
||||||
|
/** 当前进度百分比 (0-100) */
|
||||||
|
percent: number;
|
||||||
|
|
||||||
|
/** 已下载字节数 */
|
||||||
|
downloadedBytes: number;
|
||||||
|
|
||||||
|
/** 总字节数 */
|
||||||
|
totalBytes: number;
|
||||||
|
|
||||||
|
/** 当前下载的文件 */
|
||||||
|
currentFile?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HotUpdateCallbacks {
|
||||||
|
/** 进度回调 */
|
||||||
|
onProgress?: (progress: HotUpdateProgress) => void;
|
||||||
|
|
||||||
|
/** 完成回调 */
|
||||||
|
onComplete?: (result: IPromiseResult) => void;
|
||||||
|
|
||||||
|
/** 错误回调 */
|
||||||
|
onError?: (error: IPromiseResult) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HotUpdate {
|
||||||
|
public updateWithCallbacks(callbacks: HotUpdateCallbacks): void {
|
||||||
|
this._am.setEventCallback((event) => {
|
||||||
|
switch (event.getEventCode()) {
|
||||||
|
case jsb.EventAssetsManager.UPDATE_PROGRESSION:
|
||||||
|
callbacks.onProgress?.({
|
||||||
|
percent: event.getPercent(),
|
||||||
|
downloadedBytes: event.getDownloadedBytes(),
|
||||||
|
totalBytes: event.getTotalBytes()
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.UPDATE_FINISHED:
|
||||||
|
callbacks.onComplete?.({ code: HotUpdateCode.Succeed, message: "更新完成" });
|
||||||
|
break;
|
||||||
|
case jsb.EventAssetsManager.UPDATE_FAILED:
|
||||||
|
callbacks.onError?.({ code: HotUpdateCode.DownloadFailed, message: "更新失败" });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._am.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理和重试
|
||||||
|
|
||||||
|
### 重试机制
|
||||||
|
```typescript
|
||||||
|
export class HotUpdate {
|
||||||
|
private _retryCount: number = 0;
|
||||||
|
private readonly _maxRetryCount: number = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带重试的热更新
|
||||||
|
*/
|
||||||
|
public async hotUpdateWithRetry(): Promise<IPromiseResult> {
|
||||||
|
for (let i = 0; i < this._maxRetryCount; i++) {
|
||||||
|
try {
|
||||||
|
const result = await this.hotUpdate();
|
||||||
|
|
||||||
|
if (result.code === HotUpdateCode.Succeed) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 网络错误可以重试
|
||||||
|
if (result.code === HotUpdateCode.NetworkError && i < this._maxRetryCount - 1) {
|
||||||
|
warn(`热更新失败,准备重试 (${i + 1}/${this._maxRetryCount})`);
|
||||||
|
await this.delay(1000 * (i + 1)); // 递增延迟
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
error('热更新异常', error);
|
||||||
|
if (i === this._maxRetryCount - 1) {
|
||||||
|
return { code: HotUpdateCode.UnknownError, message: `重试${this._maxRetryCount}次后仍然失败` };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private delay(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 热更新最佳实践
|
||||||
|
|
||||||
|
### 1. 版本管理
|
||||||
|
- 使用语义化版本号 (major.minor.patch)
|
||||||
|
- 提供版本比较和检查功能
|
||||||
|
- 记录版本更新历史
|
||||||
|
|
||||||
|
### 2. 网络处理
|
||||||
|
- 实现重试机制处理网络不稳定
|
||||||
|
- 添加超时控制
|
||||||
|
- 支持断点续传
|
||||||
|
|
||||||
|
### 3. 用户体验
|
||||||
|
- 提供详细的进度反馈
|
||||||
|
- 支持后台下载
|
||||||
|
- 提供更新取消选项
|
||||||
|
|
||||||
|
### 4. 错误恢复
|
||||||
|
- 验证下载文件完整性
|
||||||
|
- 支持回滚到上一版本
|
||||||
|
- 提供修复工具清理损坏文件
|
||||||
|
|
||||||
|
### 5. 安全性
|
||||||
|
- 验证manifest签名
|
||||||
|
- 检查文件哈希值
|
||||||
|
- 使用HTTPS传输
|
||||||
258
.cursor/rules/logging-debugging.mdc
Normal file
258
.cursor/rules/logging-debugging.mdc
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
---
|
||||||
|
description: "日志系统和调试规范"
|
||||||
|
globs: ["src/tool/log.ts", "**/*.ts"]
|
||||||
|
alwaysApply: true
|
||||||
|
type: "logging"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 日志系统和调试规范
|
||||||
|
|
||||||
|
## 统一日志接口
|
||||||
|
|
||||||
|
### 日志级别定义
|
||||||
|
```typescript
|
||||||
|
import { debug, info, log, warn, error } from "../tool/log";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志级别使用指南:
|
||||||
|
* - debug: 调试信息,仅在开发模式显示
|
||||||
|
* - log: 一般信息输出
|
||||||
|
* - info: 信息性消息,带有特殊图标
|
||||||
|
* - warn: 警告信息,黄色背景
|
||||||
|
* - error: 错误消息,红色背景
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 日志使用规范
|
||||||
|
```typescript
|
||||||
|
// ✅ 正确的日志使用
|
||||||
|
export class WindowManager {
|
||||||
|
public static showWindow(windowName: string): void {
|
||||||
|
debug(`显示窗口: ${windowName}`); // 调试信息
|
||||||
|
|
||||||
|
if (!this._windows.has(windowName)) {
|
||||||
|
warn(`窗口不存在 ${windowName}`); // 警告
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.doShowWindow(windowName);
|
||||||
|
log(`窗口 ${windowName} 显示成功`); // 普通信息
|
||||||
|
} catch (e) {
|
||||||
|
error(`显示窗口失败: ${windowName}`, e); // 错误信息
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 调试模式管理
|
||||||
|
|
||||||
|
### 调试开关设计
|
||||||
|
```typescript
|
||||||
|
// header.ts - 调试配置
|
||||||
|
export let KUNPO_DEBUG: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用或禁用调试模式
|
||||||
|
* @param enable 是否启用调试模式
|
||||||
|
*/
|
||||||
|
export function enableDebugMode(enable: boolean): void {
|
||||||
|
if (enable === true) {
|
||||||
|
KUNPO_DEBUG = true;
|
||||||
|
warn("调试模式已开启");
|
||||||
|
} else {
|
||||||
|
KUNPO_DEBUG = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// log.ts - 条件日志输出
|
||||||
|
function debug(...args: any[]): void {
|
||||||
|
KUNPO_DEBUG && console.log("kunpo:", ...args);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 框架配置集成
|
||||||
|
```typescript
|
||||||
|
interface FrameConfig {
|
||||||
|
/** 开启debug 默认: false */
|
||||||
|
debug?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CocosEntry extends Component {
|
||||||
|
public getConfig(): FrameConfig {
|
||||||
|
return {
|
||||||
|
debug: true // 在开发环境设置为 true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected start(): void {
|
||||||
|
const config = this.getConfig();
|
||||||
|
enableDebugMode(config.debug); // 根据配置启用调试
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 日志格式规范
|
||||||
|
|
||||||
|
### 统一前缀格式
|
||||||
|
```typescript
|
||||||
|
// 所有日志都带有 "kunpo:" 前缀
|
||||||
|
function log(...args: any[]) {
|
||||||
|
console.log("kunpo:", ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug(...args: any[]): void {
|
||||||
|
KUNPO_DEBUG && console.log("kunpo:", ...args);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 结构化日志信息
|
||||||
|
```typescript
|
||||||
|
// ✅ 推荐的日志格式
|
||||||
|
debug(`窗口注册 - 窗口名:${name} 包名:${pkg} 组名:${group}`);
|
||||||
|
log(`模块初始化完成: ${moduleName}`);
|
||||||
|
warn(`资源加载失败: ${resourcePath}`);
|
||||||
|
error(`网络请求异常: ${url}`, errorDetails);
|
||||||
|
|
||||||
|
// ❌ 避免的日志格式
|
||||||
|
debug("window registered"); // 信息不够具体
|
||||||
|
log(window); // 直接输出对象
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发调试工具
|
||||||
|
|
||||||
|
### 全局调试接口
|
||||||
|
```typescript
|
||||||
|
// 暴露调试接口到全局对象
|
||||||
|
let _global = globalThis || window || global;
|
||||||
|
|
||||||
|
(_global as any)["getKunpoRegisterWindowMaps"] = function () {
|
||||||
|
return _uidecorator.getWindowMaps() as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
(_global as any)["getKunpoRegisterComponentMaps"] = function () {
|
||||||
|
return _uidecorator.getComponentMaps() as any;
|
||||||
|
};
|
||||||
|
|
||||||
|
(_global as any)["getKunpoRegisterHeaderMaps"] = function () {
|
||||||
|
return _uidecorator.getHeaderMaps() as any;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 运行时信息输出
|
||||||
|
```typescript
|
||||||
|
// 系统信息调试
|
||||||
|
private initPlatform(): void {
|
||||||
|
debug(`系统类型: ${sys.os}`);
|
||||||
|
debug(`平台类型: ${PlatformType[Platform.platform]}`);
|
||||||
|
debug(`是否原生: ${Platform.isNative}`);
|
||||||
|
debug(`是否移动端: ${Platform.isMobile}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模块注册调试
|
||||||
|
public static registerUI(): void {
|
||||||
|
for (const { ctor, res } of _uidecorator.getWindowMaps().values()) {
|
||||||
|
debug(`窗口注册 - 窗口名:${res.name} 包名:${res.pkg} 组名:${res.group}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理和日志
|
||||||
|
|
||||||
|
### Promise 错误处理
|
||||||
|
```typescript
|
||||||
|
public static showWindow(windowName: string, userdata?: any): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
debug(`准备显示窗口: ${windowName}`);
|
||||||
|
|
||||||
|
this._resPool.loadWindowRes(windowName, {
|
||||||
|
complete: () => {
|
||||||
|
debug(`窗口资源加载完成: ${windowName}`);
|
||||||
|
this.showWindowIm(windowName, userdata);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
fail: (pkgs: string[]) => {
|
||||||
|
error(`窗口资源加载失败: ${windowName}`, pkgs);
|
||||||
|
reject(pkgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 异常捕获和记录
|
||||||
|
```typescript
|
||||||
|
try {
|
||||||
|
this.doSomethingRisky();
|
||||||
|
} catch (error) {
|
||||||
|
// 记录详细的错误信息
|
||||||
|
error(`操作执行失败: ${operation}`, {
|
||||||
|
error: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
context: this.getContext()
|
||||||
|
});
|
||||||
|
|
||||||
|
// 根据错误类型决定是否继续执行
|
||||||
|
if (error instanceof CriticalError) {
|
||||||
|
throw error; // 关键错误需要向上抛出
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 性能监控日志
|
||||||
|
|
||||||
|
### 时间统计
|
||||||
|
```typescript
|
||||||
|
export class WindowManager {
|
||||||
|
public static showWindow(windowName: string): Promise<void> {
|
||||||
|
const startTime = performance.now();
|
||||||
|
debug(`开始显示窗口: ${windowName}`);
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this._resPool.loadWindowRes(windowName, {
|
||||||
|
complete: () => {
|
||||||
|
const loadTime = performance.now() - startTime;
|
||||||
|
debug(`窗口显示完成: ${windowName}, 耗时: ${loadTime.toFixed(2)}ms`);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 资源使用监控
|
||||||
|
```typescript
|
||||||
|
export class WindowResPool {
|
||||||
|
private logResourceUsage(): void {
|
||||||
|
if (KUNPO_DEBUG) {
|
||||||
|
const windowCount = this._windowInfos.size;
|
||||||
|
const headerCount = this._headerInfos.size;
|
||||||
|
debug(`资源池状态 - 窗口: ${windowCount}, 头部: ${headerCount}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 调试最佳实践
|
||||||
|
|
||||||
|
### 1. 日志分级使用
|
||||||
|
- `debug`: 仅开发时需要的详细信息
|
||||||
|
- `log`: 系统运行的关键节点信息
|
||||||
|
- `warn`: 可能的问题但不影响运行
|
||||||
|
- `error`: 必须处理的错误情况
|
||||||
|
|
||||||
|
### 2. 信息完整性
|
||||||
|
- 包含足够的上下文信息
|
||||||
|
- 记录输入参数和状态
|
||||||
|
- 包含时间戳和调用路径
|
||||||
|
|
||||||
|
### 3. 性能考虑
|
||||||
|
- 避免在日志中进行重度计算
|
||||||
|
- 使用条件编译减少生产环境开销
|
||||||
|
- 延迟计算日志参数
|
||||||
|
|
||||||
|
### 4. 安全性
|
||||||
|
- 避免输出敏感信息到日志
|
||||||
|
- 在生产环境关闭详细日志
|
||||||
|
- 注意日志文件的权限设置
|
||||||
449
.cursor/rules/minigame-platform.mdc
Normal file
449
.cursor/rules/minigame-platform.mdc
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
---
|
||||||
|
description: "小游戏平台开发规范"
|
||||||
|
globs: ["src/minigame/**/*.ts", "src/global/Platform.ts"]
|
||||||
|
alwaysApply: false
|
||||||
|
type: "platform-specific"
|
||||||
|
---
|
||||||
|
|
||||||
|
# 小游戏平台开发规范
|
||||||
|
|
||||||
|
## 平台检测和分类
|
||||||
|
|
||||||
|
### 平台类型定义
|
||||||
|
```typescript
|
||||||
|
export enum PlatformType {
|
||||||
|
Unknown = 0,
|
||||||
|
Browser = 1,
|
||||||
|
WX = 2, // 微信小游戏
|
||||||
|
Alipay = 3, // 支付宝小游戏
|
||||||
|
Bytedance = 4, // 字节跳动小游戏
|
||||||
|
HuaweiQuick = 5 // 华为快游戏
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Platform {
|
||||||
|
/** 平台类型 */
|
||||||
|
public static platform: PlatformType = PlatformType.Unknown;
|
||||||
|
|
||||||
|
/** 平台标识 */
|
||||||
|
public static isWX: boolean = false;
|
||||||
|
public static isAlipay: boolean = false;
|
||||||
|
public static isBytedance: boolean = false;
|
||||||
|
public static isHuaweiQuick: boolean = false;
|
||||||
|
public static isBrowser: boolean = false;
|
||||||
|
|
||||||
|
/** 设备类型 */
|
||||||
|
public static isNative: boolean = false;
|
||||||
|
public static isMobile: boolean = false;
|
||||||
|
public static isNativeMobile: boolean = false;
|
||||||
|
|
||||||
|
/** 系统类型 */
|
||||||
|
public static isAndroid: boolean = false;
|
||||||
|
public static isIOS: boolean = false;
|
||||||
|
public static isHarmonyOS: boolean = false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 平台初始化模式
|
||||||
|
```typescript
|
||||||
|
export class CocosEntry extends Component {
|
||||||
|
private initPlatform(): void {
|
||||||
|
// 设备类型检测
|
||||||
|
Platform.isNative = sys.isNative;
|
||||||
|
Platform.isMobile = sys.isMobile;
|
||||||
|
Platform.isNativeMobile = sys.isNative && sys.isMobile;
|
||||||
|
|
||||||
|
// 系统类型检测
|
||||||
|
switch (sys.os) {
|
||||||
|
case sys.OS.ANDROID:
|
||||||
|
Platform.isAndroid = true;
|
||||||
|
debug("系统类型 Android");
|
||||||
|
break;
|
||||||
|
case sys.OS.IOS:
|
||||||
|
Platform.isIOS = true;
|
||||||
|
debug("系统类型 IOS");
|
||||||
|
break;
|
||||||
|
case sys.OS.OPENHARMONY:
|
||||||
|
Platform.isHarmonyOS = true;
|
||||||
|
debug("系统类型 HarmonyOS");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 平台类型检测
|
||||||
|
switch (sys.platform) {
|
||||||
|
case sys.Platform.WECHAT_GAME:
|
||||||
|
Platform.isWX = true;
|
||||||
|
Platform.platform = PlatformType.WX;
|
||||||
|
break;
|
||||||
|
case sys.Platform.ALIPAY_MINI_GAME:
|
||||||
|
Platform.isAlipay = true;
|
||||||
|
Platform.platform = PlatformType.Alipay;
|
||||||
|
break;
|
||||||
|
case sys.Platform.BYTEDANCE_MINI_GAME:
|
||||||
|
Platform.isBytedance = true;
|
||||||
|
Platform.platform = PlatformType.Bytedance;
|
||||||
|
break;
|
||||||
|
case sys.Platform.HUAWEI_QUICK_GAME:
|
||||||
|
Platform.isHuaweiQuick = true;
|
||||||
|
Platform.platform = PlatformType.HuaweiQuick;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Platform.isBrowser = true;
|
||||||
|
Platform.platform = PlatformType.Browser;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug(`platform: ${PlatformType[Platform.platform]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 平台适配器设计
|
||||||
|
|
||||||
|
### 通用适配器接口
|
||||||
|
```typescript
|
||||||
|
export interface IMiniGameAdapter {
|
||||||
|
/** 显示分享菜单 */
|
||||||
|
showShareMenu(): void;
|
||||||
|
|
||||||
|
/** 分享应用 */
|
||||||
|
shareAppMessage(options: ShareOptions): void;
|
||||||
|
|
||||||
|
/** 显示 loading */
|
||||||
|
showLoading(options: LoadingOptions): void;
|
||||||
|
|
||||||
|
/** 隐藏 loading */
|
||||||
|
hideLoading(): void;
|
||||||
|
|
||||||
|
/** 显示 toast */
|
||||||
|
showToast(options: ToastOptions): void;
|
||||||
|
|
||||||
|
/** 获取系统信息 */
|
||||||
|
getSystemInfo(): Promise<SystemInfo>;
|
||||||
|
|
||||||
|
/** 震动反馈 */
|
||||||
|
vibrateShort(): void;
|
||||||
|
vibrateLong(): void;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 微信小游戏适配
|
||||||
|
```typescript
|
||||||
|
export class WechatCommon implements IMiniGameAdapter {
|
||||||
|
public showShareMenu(): void {
|
||||||
|
if (Platform.isWX && wx.showShareMenu) {
|
||||||
|
wx.showShareMenu({
|
||||||
|
withShareTicket: true,
|
||||||
|
menus: ['shareAppMessage', 'shareTimeline']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public shareAppMessage(options: ShareOptions): void {
|
||||||
|
if (Platform.isWX && wx.shareAppMessage) {
|
||||||
|
wx.shareAppMessage({
|
||||||
|
title: options.title,
|
||||||
|
imageUrl: options.imageUrl,
|
||||||
|
query: options.query,
|
||||||
|
success: options.success,
|
||||||
|
fail: options.fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public showLoading(options: LoadingOptions): void {
|
||||||
|
if (Platform.isWX && wx.showLoading) {
|
||||||
|
wx.showLoading({
|
||||||
|
title: options.title || '加载中...',
|
||||||
|
mask: options.mask !== false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSystemInfo(): Promise<SystemInfo> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (Platform.isWX && wx.getSystemInfo) {
|
||||||
|
wx.getSystemInfo({
|
||||||
|
success: resolve,
|
||||||
|
fail: reject
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject(new Error('不支持的平台'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 支付宝小游戏适配
|
||||||
|
```typescript
|
||||||
|
export class AlipayCommon implements IMiniGameAdapter {
|
||||||
|
public showShareMenu(): void {
|
||||||
|
// 支付宝特定实现
|
||||||
|
}
|
||||||
|
|
||||||
|
public shareAppMessage(options: ShareOptions): void {
|
||||||
|
if (Platform.isAlipay && my.shareAppMessage) {
|
||||||
|
my.shareAppMessage({
|
||||||
|
title: options.title,
|
||||||
|
desc: options.desc,
|
||||||
|
path: options.path,
|
||||||
|
success: options.success,
|
||||||
|
fail: options.fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public showLoading(options: LoadingOptions): void {
|
||||||
|
if (Platform.isAlipay && my.showLoading) {
|
||||||
|
my.showLoading({
|
||||||
|
content: options.title || '加载中...'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 字节跳动小游戏适配
|
||||||
|
```typescript
|
||||||
|
export class BytedanceCommon implements IMiniGameAdapter {
|
||||||
|
public shareAppMessage(options: ShareOptions): void {
|
||||||
|
if (Platform.isBytedance && tt.shareAppMessage) {
|
||||||
|
tt.shareAppMessage({
|
||||||
|
title: options.title,
|
||||||
|
imageUrl: options.imageUrl,
|
||||||
|
query: options.query,
|
||||||
|
success: options.success,
|
||||||
|
fail: options.fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public vibrateShort(): void {
|
||||||
|
if (Platform.isBytedance && tt.vibrateShort) {
|
||||||
|
tt.vibrateShort({
|
||||||
|
success: () => debug('震动成功'),
|
||||||
|
fail: (err) => warn('震动失败', err)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 统一接口封装
|
||||||
|
|
||||||
|
### MiniHelper 统一接口
|
||||||
|
```typescript
|
||||||
|
export class MiniHelper {
|
||||||
|
private static _adapter: IMiniGameAdapter | null = null;
|
||||||
|
|
||||||
|
/** 初始化适配器 */
|
||||||
|
public static init(): void {
|
||||||
|
switch (Platform.platform) {
|
||||||
|
case PlatformType.WX:
|
||||||
|
this._adapter = new WechatCommon();
|
||||||
|
break;
|
||||||
|
case PlatformType.Alipay:
|
||||||
|
this._adapter = new AlipayCommon();
|
||||||
|
break;
|
||||||
|
case PlatformType.Bytedance:
|
||||||
|
this._adapter = new BytedanceCommon();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
warn('当前平台不支持小游戏功能');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 统一的分享接口 */
|
||||||
|
public static share(options: ShareOptions): void {
|
||||||
|
if (this._adapter) {
|
||||||
|
this._adapter.shareAppMessage(options);
|
||||||
|
} else {
|
||||||
|
warn('未初始化小游戏适配器');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 统一的震动接口 */
|
||||||
|
public static vibrate(type: 'short' | 'long' = 'short'): void {
|
||||||
|
if (this._adapter) {
|
||||||
|
if (type === 'short') {
|
||||||
|
this._adapter.vibrateShort();
|
||||||
|
} else {
|
||||||
|
this._adapter.vibrateLong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 统一的系统信息获取 */
|
||||||
|
public static async getSystemInfo(): Promise<SystemInfo | null> {
|
||||||
|
if (this._adapter) {
|
||||||
|
try {
|
||||||
|
return await this._adapter.getSystemInfo();
|
||||||
|
} catch (error) {
|
||||||
|
error('获取系统信息失败', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 平台特定功能
|
||||||
|
|
||||||
|
### 广告系统封装
|
||||||
|
```typescript
|
||||||
|
interface AdOptions {
|
||||||
|
adUnitId: string;
|
||||||
|
success?: () => void;
|
||||||
|
fail?: (error: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AdManager {
|
||||||
|
/** 显示激励视频广告 */
|
||||||
|
public static showRewardedVideoAd(options: AdOptions): void {
|
||||||
|
switch (Platform.platform) {
|
||||||
|
case PlatformType.WX:
|
||||||
|
this.showWXRewardedAd(options);
|
||||||
|
break;
|
||||||
|
case PlatformType.Alipay:
|
||||||
|
this.showAlipayRewardedAd(options);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
options.fail?.('当前平台不支持广告');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static showWXRewardedAd(options: AdOptions): void {
|
||||||
|
if (wx.createRewardedVideoAd) {
|
||||||
|
const rewardedVideoAd = wx.createRewardedVideoAd({
|
||||||
|
adUnitId: options.adUnitId
|
||||||
|
});
|
||||||
|
|
||||||
|
rewardedVideoAd.onLoad(() => {
|
||||||
|
rewardedVideoAd.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
rewardedVideoAd.onClose((res) => {
|
||||||
|
if (res && res.isEnded) {
|
||||||
|
options.success?.();
|
||||||
|
} else {
|
||||||
|
options.fail?.('用户取消观看');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 支付系统封装
|
||||||
|
```typescript
|
||||||
|
interface PaymentOptions {
|
||||||
|
amount: number;
|
||||||
|
orderInfo: string;
|
||||||
|
success?: (result: any) => void;
|
||||||
|
fail?: (error: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PaymentManager {
|
||||||
|
public static pay(options: PaymentOptions): void {
|
||||||
|
switch (Platform.platform) {
|
||||||
|
case PlatformType.WX:
|
||||||
|
this.wxPay(options);
|
||||||
|
break;
|
||||||
|
case PlatformType.Alipay:
|
||||||
|
this.alipayPay(options);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
options.fail?.('当前平台不支持支付');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static wxPay(options: PaymentOptions): void {
|
||||||
|
// 微信支付实现
|
||||||
|
}
|
||||||
|
|
||||||
|
private static alipayPay(options: PaymentOptions): void {
|
||||||
|
// 支付宝支付实现
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据存储适配
|
||||||
|
|
||||||
|
### 本地存储封装
|
||||||
|
```typescript
|
||||||
|
export class StorageManager {
|
||||||
|
/** 设置数据 */
|
||||||
|
public static setItem(key: string, value: any): void {
|
||||||
|
try {
|
||||||
|
const jsonValue = JSON.stringify(value);
|
||||||
|
|
||||||
|
switch (Platform.platform) {
|
||||||
|
case PlatformType.WX:
|
||||||
|
wx.setStorageSync(key, jsonValue);
|
||||||
|
break;
|
||||||
|
case PlatformType.Alipay:
|
||||||
|
my.setStorageSync({ key, data: jsonValue });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
localStorage.setItem(key, jsonValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
error('存储数据失败', key, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取数据 */
|
||||||
|
public static getItem<T>(key: string, defaultValue?: T): T | null {
|
||||||
|
try {
|
||||||
|
let jsonValue: string | null = null;
|
||||||
|
|
||||||
|
switch (Platform.platform) {
|
||||||
|
case PlatformType.WX:
|
||||||
|
jsonValue = wx.getStorageSync(key);
|
||||||
|
break;
|
||||||
|
case PlatformType.Alipay:
|
||||||
|
jsonValue = my.getStorageSync({ key }).data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
jsonValue = localStorage.getItem(key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jsonValue) {
|
||||||
|
return JSON.parse(jsonValue) as T;
|
||||||
|
}
|
||||||
|
return defaultValue || null;
|
||||||
|
} catch (error) {
|
||||||
|
error('读取数据失败', key, error);
|
||||||
|
return defaultValue || null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 平台开发最佳实践
|
||||||
|
|
||||||
|
### 1. 统一接口设计
|
||||||
|
- 为所有平台提供一致的API接口
|
||||||
|
- 使用适配器模式处理平台差异
|
||||||
|
- 提供降级方案处理不支持的功能
|
||||||
|
|
||||||
|
### 2. 平台检测
|
||||||
|
- 在应用启动时进行平台检测
|
||||||
|
- 使用枚举定义平台类型
|
||||||
|
- 提供便捷的平台判断属性
|
||||||
|
|
||||||
|
### 3. 错误处理
|
||||||
|
- 对不支持的平台提供友好的错误信息
|
||||||
|
- 使用 try-catch 处理平台API调用
|
||||||
|
- 提供回调函数处理异步操作结果
|
||||||
|
|
||||||
|
### 4. 性能优化
|
||||||
|
- 延迟加载平台特定功能
|
||||||
|
- 避免在不支持的平台上创建无用对象
|
||||||
|
- 使用条件编译减少包体积
|
||||||
260
.cursor/rules/project-overview.mdc
Normal file
260
.cursor/rules/project-overview.mdc
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
---
|
||||||
|
description: "KunpoCC 项目总体开发规范"
|
||||||
|
globs: ["**/*.ts", "**/*.md", "**/*.json"]
|
||||||
|
alwaysApply: true
|
||||||
|
type: "project-guidelines"
|
||||||
|
---
|
||||||
|
|
||||||
|
# KunpoCC 项目总体开发规范
|
||||||
|
|
||||||
|
## 项目介绍
|
||||||
|
|
||||||
|
KunpoCC 是一个基于 Cocos Creator 3.x 的游戏开发框架库,提供了一套完整的游戏开发工具和模块:
|
||||||
|
|
||||||
|
- **UI 管理系统**: 基于 FairyGUI 的窗口管理和组件系统
|
||||||
|
- **数据绑定系统**: 强类型的响应式数据绑定框架
|
||||||
|
- **平台适配**: 微信、支付宝、字节跳动等小游戏平台支持
|
||||||
|
- **热更新系统**: 完整的资源热更新解决方案
|
||||||
|
- **工具模块**: 时间管理、日志系统、数学工具等实用工具
|
||||||
|
|
||||||
|
## 项目结构规范
|
||||||
|
|
||||||
|
### 目录组织
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
├── cocos/ # Cocos Creator 适配层
|
||||||
|
├── condition/ # 条件管理系统 (红点系统)
|
||||||
|
├── data/ # 数据绑定系统
|
||||||
|
├── fgui/ # FairyGUI 窗口基类
|
||||||
|
├── global/ # 全局工具和配置
|
||||||
|
├── hotupdate/ # 热更新系统
|
||||||
|
├── interface/ # 通用接口定义
|
||||||
|
├── minigame/ # 小游戏平台适配
|
||||||
|
├── module/ # 模块基类
|
||||||
|
├── tool/ # 工具函数集合
|
||||||
|
└── ui/ # UI 管理系统
|
||||||
|
```
|
||||||
|
|
||||||
|
### 文件命名规范
|
||||||
|
- **类文件**: PascalCase (如 `WindowManager.ts`)
|
||||||
|
- **接口文件**: 以 `I` 开头 (如 `IWindow.ts`)
|
||||||
|
- **工具文件**: 功能描述命名 (如 `log.ts`, `Math.ts`)
|
||||||
|
- **装饰器文件**: 以 `Decorator` 结尾 (如 `UIDecorator.ts`)
|
||||||
|
|
||||||
|
## 编码规范总览
|
||||||
|
|
||||||
|
### 类型系统
|
||||||
|
```typescript
|
||||||
|
// 严格的 TypeScript 配置
|
||||||
|
{
|
||||||
|
"strict": true,
|
||||||
|
"strictNullChecks": false, // 项目特殊需求
|
||||||
|
"experimentalDecorators": true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先使用接口和泛型
|
||||||
|
interface IWindow {
|
||||||
|
_show(userdata?: any): void;
|
||||||
|
_close(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用泛型约束确保类型安全
|
||||||
|
public static getWindow<T extends IWindow>(name: string): T | null {
|
||||||
|
return this._windows.get(name) as T;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 装饰器系统
|
||||||
|
```typescript
|
||||||
|
// UI 装饰器
|
||||||
|
@uiclass("popup", "common", "SettingsWindow")
|
||||||
|
export class SettingsWindow extends Window {
|
||||||
|
@uiprop btnClose: GButton;
|
||||||
|
@uiclick private onBtnCloseClick(): void { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据绑定装饰器
|
||||||
|
@data.bindProp(GameData, data => data.level, function(item, value) {
|
||||||
|
this.levelLabel.text = `Level: ${value}`;
|
||||||
|
})
|
||||||
|
private _levelBinding: any;
|
||||||
|
|
||||||
|
// 条件装饰器
|
||||||
|
@conditionClass(1001)
|
||||||
|
export class LevelCondition extends ConditionBase { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 架构模式
|
||||||
|
```typescript
|
||||||
|
// 单例管理器模式
|
||||||
|
export class WindowManager {
|
||||||
|
private static _instance: WindowManager;
|
||||||
|
public static getInstance(): WindowManager { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 抽象基类模式
|
||||||
|
export abstract class Window extends WindowBase {
|
||||||
|
protected abstract onInit(): void;
|
||||||
|
protected onShow(userdata?: any): void { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 适配器模式
|
||||||
|
export class CocosAdapter implements IAdapter {
|
||||||
|
public init(): void { }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开发流程
|
||||||
|
|
||||||
|
### 1. 新增功能模块
|
||||||
|
1. 在相应目录创建模块文件
|
||||||
|
2. 实现必要的接口和基类
|
||||||
|
3. 添加相应的装饰器支持
|
||||||
|
4. 编写单元测试
|
||||||
|
5. 更新文档和示例
|
||||||
|
|
||||||
|
### 2. 窗口开发
|
||||||
|
```typescript
|
||||||
|
@uiclass("main", "game", "GameWindow")
|
||||||
|
export class GameWindow extends Window {
|
||||||
|
@uiprop playerPanel: GComponent;
|
||||||
|
@uiprop settingsBtn: GButton;
|
||||||
|
|
||||||
|
@uiclick
|
||||||
|
private onSettingsBtnClick(): void {
|
||||||
|
WindowManager.showWindow("SettingsWindow");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected onInit(): void {
|
||||||
|
// 窗口初始化逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 数据系统集成
|
||||||
|
```typescript
|
||||||
|
class GameData extends DataBase {
|
||||||
|
private _score: number = 0;
|
||||||
|
|
||||||
|
get score(): number { return this._score; }
|
||||||
|
set score(value: number) {
|
||||||
|
if (this._score !== value) {
|
||||||
|
this._score = value;
|
||||||
|
this.notify('score', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 质量保证
|
||||||
|
|
||||||
|
### 日志和调试
|
||||||
|
```typescript
|
||||||
|
import { debug, warn, error, log } from "../tool/log";
|
||||||
|
|
||||||
|
// 统一的日志格式
|
||||||
|
debug(`窗口注册 - 窗口名:${name} 包名:${pkg}`);
|
||||||
|
warn(`资源加载失败: ${resourcePath}`);
|
||||||
|
error(`网络请求异常: ${url}`, errorDetails);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 错误处理
|
||||||
|
```typescript
|
||||||
|
// Promise 错误处理
|
||||||
|
public static showWindow(name: string): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
// 业务逻辑
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
error('窗口显示失败', error);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 性能优化
|
||||||
|
- 使用资源池管理UI资源生命周期
|
||||||
|
- 实现批量更新减少频繁触发
|
||||||
|
- 及时清理事件监听和数据绑定
|
||||||
|
- 使用对象池复用临时对象
|
||||||
|
|
||||||
|
## 文档和注释
|
||||||
|
|
||||||
|
### JSDoc 规范
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* @Author: Gongxh
|
||||||
|
* @Date: 2024-12-07
|
||||||
|
* @Description: 窗口管理类
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示指定名称的窗口
|
||||||
|
* @param windowName - 窗口的名称
|
||||||
|
* @param userdata - 可选的用户数据
|
||||||
|
* @returns Promise<void>
|
||||||
|
* @internal - 标记内部方法
|
||||||
|
*/
|
||||||
|
public static showWindow(windowName: string, userdata?: any): Promise<void> {
|
||||||
|
```
|
||||||
|
|
||||||
|
### 代码注释原则
|
||||||
|
- 公共API必须有完整的JSDoc注释
|
||||||
|
- 复杂逻辑添加行内注释说明
|
||||||
|
- 内部方法使用 `@internal` 标记
|
||||||
|
- 废弃功能使用 `@deprecated` 标记
|
||||||
|
|
||||||
|
## 版本管理
|
||||||
|
|
||||||
|
### 语义化版本
|
||||||
|
- **主版本号**: 不兼容的API修改
|
||||||
|
- **次版本号**: 向后兼容的功能性新增
|
||||||
|
- **修订号**: 向后兼容的问题修正
|
||||||
|
|
||||||
|
### 变更记录
|
||||||
|
- 维护 CHANGELOG.md 记录版本变更
|
||||||
|
- 每个版本包含新增功能、修复问题、破坏性变更
|
||||||
|
- 提供迁移指南帮助用户升级
|
||||||
|
|
||||||
|
## 测试规范
|
||||||
|
|
||||||
|
### 单元测试
|
||||||
|
```typescript
|
||||||
|
describe('WindowManager', () => {
|
||||||
|
test('should show window correctly', () => {
|
||||||
|
// 测试逻辑
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle window not found', () => {
|
||||||
|
// 错误处理测试
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 集成测试
|
||||||
|
- 测试模块间的交互
|
||||||
|
- 验证装饰器系统的完整性
|
||||||
|
- 测试平台适配功能
|
||||||
|
|
||||||
|
## 部署和发布
|
||||||
|
|
||||||
|
### 构建配置
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build": "rollup -c rollup.config.mjs",
|
||||||
|
"build:all": "npm run build && npm run copy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 发布检查清单
|
||||||
|
1. 代码通过所有测试
|
||||||
|
2. 文档更新完整
|
||||||
|
3. 版本号正确更新
|
||||||
|
4. CHANGELOG.md 记录变更
|
||||||
|
5. 类型定义文件正确生成
|
||||||
|
|
||||||
|
这个规范文档作为项目开发的总体指导,具体的技术细节请参考各个专门的规则文件。
|
||||||
111
.cursor/rules/typescript-general.mdc
Normal file
111
.cursor/rules/typescript-general.mdc
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
---
|
||||||
|
description: "KunpoCC TypeScript 通用开发规范"
|
||||||
|
globs: ["src/**/*.ts"]
|
||||||
|
alwaysApply: true
|
||||||
|
type: "code-style"
|
||||||
|
---
|
||||||
|
|
||||||
|
# KunpoCC TypeScript 通用开发规范
|
||||||
|
|
||||||
|
## 代码风格和命名规范
|
||||||
|
|
||||||
|
### 命名约定
|
||||||
|
- **类名**: 使用 PascalCase (如 `WindowManager`, `DataBase`)
|
||||||
|
- **接口**: 以 `I` 开头,使用 PascalCase (如 `IWindow`, `IModule`)
|
||||||
|
- **方法和属性**: 使用 camelCase (如 `showWindow`, `onInit`)
|
||||||
|
- **常量**: 全大写,下划线分隔 (如 `KUNPO_DEBUG`, `TAG`)
|
||||||
|
- **私有方法**: 以下划线开头 (如 `_init`, `_removeWindow`)
|
||||||
|
- **内部方法**: 添加 `@internal` JSDoc 注释
|
||||||
|
|
||||||
|
### 类型定义
|
||||||
|
```typescript
|
||||||
|
// 优先使用接口定义对象结构
|
||||||
|
interface size {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用泛型约束确保类型安全
|
||||||
|
public static getWindow<T extends IWindow>(name: string): T | null {
|
||||||
|
return this._windows.get(name) as T;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注释规范
|
||||||
|
|
||||||
|
### JSDoc 注释要求
|
||||||
|
- 所有公共方法必须有完整的 JSDoc 注释
|
||||||
|
- 包含参数说明、返回值说明和描述
|
||||||
|
- 内部方法使用 `@internal` 标记
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/**
|
||||||
|
* @Author: Gongxh
|
||||||
|
* @Date: 2024-12-07
|
||||||
|
* @Description: 窗口管理类
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示指定名称的窗口,并传递可选的用户数据
|
||||||
|
* @param windowName - 窗口的名称
|
||||||
|
* @param userdata - 可选参数,用于传递给窗口的用户数据
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public static showWindowIm(windowName: string, userdata?: any): void {
|
||||||
|
```
|
||||||
|
|
||||||
|
## 错误处理和日志
|
||||||
|
|
||||||
|
### 使用统一的日志系统
|
||||||
|
```typescript
|
||||||
|
import { debug, warn, error, log } from "../tool/log";
|
||||||
|
|
||||||
|
// 调试信息(仅在 debug 模式下显示)
|
||||||
|
debug("窗口注册完成", windowName);
|
||||||
|
|
||||||
|
// 警告信息
|
||||||
|
warn(`窗口不存在 ${windowName} 不需要关闭`);
|
||||||
|
|
||||||
|
// 错误信息
|
||||||
|
error("初始化失败", errorMessage);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 错误处理模式
|
||||||
|
```typescript
|
||||||
|
// 使用 Promise 进行异步错误处理
|
||||||
|
public static showWindow(windowName: string, userdata?: any): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this._resPool.loadWindowRes(windowName, {
|
||||||
|
complete: () => {
|
||||||
|
this.showWindowIm(windowName, userdata);
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
fail: (pkgs: string[]) => {
|
||||||
|
reject(pkgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TypeScript 最佳实践
|
||||||
|
|
||||||
|
### 严格类型检查
|
||||||
|
- 启用 `strict: true`
|
||||||
|
- 禁用 `strictNullChecks: false` (项目特殊要求)
|
||||||
|
- 使用明确的类型注解,避免 `any`
|
||||||
|
|
||||||
|
### 装饰器使用
|
||||||
|
- 启用 `experimentalDecorators: true`
|
||||||
|
- 遵循项目的装饰器模式 (详见装饰器规范)
|
||||||
|
|
||||||
|
### 模块导入
|
||||||
|
```typescript
|
||||||
|
// 使用相对路径导入
|
||||||
|
import { WindowBase } from "./WindowBase";
|
||||||
|
import { debug, warn } from "../tool/log";
|
||||||
|
|
||||||
|
// 导出时保持清晰的结构
|
||||||
|
export { WindowManager } from "./ui/WindowManager";
|
||||||
|
export { _uidecorator } from "./ui/UIDecorator";
|
||||||
|
```
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "kunpocc",
|
"name": "kunpocc",
|
||||||
"version": "1.1.9",
|
"version": "1.1.10",
|
||||||
"description": "基于creator3.0+的kunpocc库",
|
"description": "基于creator3.0+的kunpocc库",
|
||||||
"main": "./dist/kunpocc.cjs",
|
"main": "./dist/kunpocc.cjs",
|
||||||
"module": "./dist/kunpocc.mjs",
|
"module": "./dist/kunpocc.mjs",
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ export class ConditionManager {
|
|||||||
*/
|
*/
|
||||||
public static _removeConditionNode(conditionNode: ConditionNode): void {
|
public static _removeConditionNode(conditionNode: ConditionNode): void {
|
||||||
let types = this._nodeToConditionTypes.get(conditionNode);
|
let types = this._nodeToConditionTypes.get(conditionNode);
|
||||||
|
if (!types) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (const conditionType of types.values()) {
|
for (const conditionType of types.values()) {
|
||||||
let nodes = this._typeToNotifyNodes.get(conditionType);
|
let nodes = this._typeToNotifyNodes.get(conditionType);
|
||||||
nodes.delete(conditionNode);
|
nodes.delete(conditionNode);
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export abstract class ConditionBase {
|
|||||||
public _updateCondition(): boolean {
|
public _updateCondition(): boolean {
|
||||||
let canNotify = this.evaluate();
|
let canNotify = this.evaluate();
|
||||||
if (canNotify == this._canNotify) {
|
if (canNotify == this._canNotify) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
this._canNotify = canNotify;
|
this._canNotify = canNotify;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
|
|
||||||
import { GComponent } from "fairygui-cc";
|
import { GComponent } from "fairygui-cc";
|
||||||
import { Screen } from "../global/Screen";
|
import { Screen } from "../global/Screen";
|
||||||
import { AdapterType } from "../kunpocc";
|
|
||||||
import { IWindow } from "../ui/IWindow";
|
import { IWindow } from "../ui/IWindow";
|
||||||
import { IWindowHeader } from "../ui/IWindowHeader";
|
import { IWindowHeader } from "../ui/IWindowHeader";
|
||||||
|
import { AdapterType } from "../ui/header";
|
||||||
|
|
||||||
|
|
||||||
export abstract class WindowHeader extends GComponent implements IWindowHeader {
|
export abstract class WindowHeader extends GComponent implements IWindowHeader {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { native } from "cc";
|
import { native } from "cc";
|
||||||
|
import { Platform } from "../global/Platform";
|
||||||
import { ICheckUpdatePromiseResult } from "../interface/PromiseResult";
|
import { ICheckUpdatePromiseResult } from "../interface/PromiseResult";
|
||||||
import { Platform } from "../kunpocc";
|
|
||||||
import { log } from "../tool/log";
|
import { log } from "../tool/log";
|
||||||
import { HotUpdate, HotUpdateCode } from "./HotUpdate";
|
import { HotUpdate, HotUpdateCode } from "./HotUpdate";
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Platform } from "../global/Platform";
|
import { Platform } from "../global/Platform";
|
||||||
import { BytedanceCommon } from "../kunpocc";
|
|
||||||
import { AlipayAds } from "./alipay/AlipayAds";
|
import { AlipayAds } from "./alipay/AlipayAds";
|
||||||
import { AlipayCommon } from "./alipay/AlipayCommon";
|
import { AlipayCommon } from "./alipay/AlipayCommon";
|
||||||
import { AlipayPay } from "./alipay/AlipayPay";
|
import { AlipayPay } from "./alipay/AlipayPay";
|
||||||
import { BytedanceAds } from "./bytedance/BytedanceAds";
|
import { BytedanceAds } from "./bytedance/BytedanceAds";
|
||||||
|
import { BytedanceCommon } from "./bytedance/BytedanceCommon";
|
||||||
import { BytedancePay } from "./bytedance/BytedancePay";
|
import { BytedancePay } from "./bytedance/BytedancePay";
|
||||||
import { IMiniRewardAds } from "./interface/IMiniAds";
|
import { IMiniRewardAds } from "./interface/IMiniAds";
|
||||||
import { IMiniCommon } from "./interface/IMiniCommon";
|
import { IMiniCommon } from "./interface/IMiniCommon";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @Description: 微信广告
|
* @Description: 微信广告
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { warn } from "../../kunpocc";
|
import { warn } from "../../tool/log";
|
||||||
import { MiniErrorCode } from "../header";
|
import { MiniErrorCode } from "../header";
|
||||||
import { IMiniRewardAds } from "../interface/IMiniAds";
|
import { IMiniRewardAds } from "../interface/IMiniAds";
|
||||||
|
|
||||||
|
|||||||
@@ -106,11 +106,17 @@ export class BinaryHeap<T extends HeapNode> {
|
|||||||
|
|
||||||
const size = --this._size;
|
const size = --this._size;
|
||||||
const nodes = this._nodes;
|
const nodes = this._nodes;
|
||||||
const newNode = (nodes[node.index] = nodes[size]);
|
|
||||||
|
|
||||||
newNode.index = node.index;
|
|
||||||
nodes[size] = null;
|
// 如果删除的不是最后一个元素,需要调整堆
|
||||||
this.update(newNode);
|
if (node.index < size) {
|
||||||
|
const newNode = (nodes[node.index] = nodes[size]);
|
||||||
|
newNode.index = node.index;
|
||||||
|
nodes[size] = null;
|
||||||
|
this.update(newNode);
|
||||||
|
} else {
|
||||||
|
nodes[size] = null;
|
||||||
|
}
|
||||||
node.index = -1;
|
node.index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ export class Timer {
|
|||||||
const timerNode = this._pool.get(timerId);
|
const timerNode = this._pool.get(timerId);
|
||||||
|
|
||||||
if (timerNode) {
|
if (timerNode) {
|
||||||
|
timerNode.pause = true;
|
||||||
timerNode.pauseRemainTime = timerNode.expireTime - this._elapsedTime;
|
timerNode.pauseRemainTime = timerNode.expireTime - this._elapsedTime;
|
||||||
this._heap.remove(timerNode);
|
this._heap.remove(timerNode);
|
||||||
this._pausedTimers.set(timerId, timerNode);
|
this._pausedTimers.set(timerId, timerNode);
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ export class WindowGroup {
|
|||||||
let isMoved = false;
|
let isMoved = false;
|
||||||
if (this.size == 0) {
|
if (this.size == 0) {
|
||||||
warn(`WindowGroup.moveWindowToTop: window group 【${this._name}】 is empty`);
|
warn(`WindowGroup.moveWindowToTop: window group 【${this._name}】 is empty`);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (this._windowNames[this.size - 1] == name) {
|
if (this._windowNames[this.size - 1] == name) {
|
||||||
// 已经在最顶层了
|
// 已经在最顶层了
|
||||||
@@ -185,7 +185,7 @@ export class WindowGroup {
|
|||||||
const index = this._windowNames.indexOf(name);
|
const index = this._windowNames.indexOf(name);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
warn(`WindowGroup.moveWindowToTop: window 【${name}】 not found in window group 【${this._name}】`);
|
warn(`WindowGroup.moveWindowToTop: window 【${name}】 not found in window group 【${this._name}】`);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (index < this._windowNames.length - 1) {
|
if (index < this._windowNames.length - 1) {
|
||||||
this._windowNames.splice(index, 1);
|
this._windowNames.splice(index, 1);
|
||||||
@@ -202,6 +202,7 @@ export class WindowGroup {
|
|||||||
window._setDepth(this._root.numChildren - 1);
|
window._setDepth(this._root.numChildren - 1);
|
||||||
// 处理窗口显示和隐藏状态
|
// 处理窗口显示和隐藏状态
|
||||||
this._processWindowHideStatus(this.size - 1, isMoved);
|
this._processWindowHideStatus(this.size - 1, isMoved);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import { assetManager, resources } from "cc";
|
import { assetManager, resources } from "cc";
|
||||||
import { UIObjectFactory, UIPackage } from "fairygui-cc";
|
import { UIObjectFactory, UIPackage } from "fairygui-cc";
|
||||||
import { warn } from "../kunpocc";
|
import { warn } from "../tool/log";
|
||||||
import { IPackageConfigRes } from "./IPackageConfig";
|
import { IPackageConfigRes } from "./IPackageConfig";
|
||||||
|
|
||||||
export interface WindowInfo {
|
export interface WindowInfo {
|
||||||
|
|||||||
Reference in New Issue
Block a user