在 Cocos Creator 多平台开发中,平台抽象层不仅是架构设计问题,更是工程落地能力的体现。如果仅停留在概念层面,很容易流于形式。因此,本文在系统总结的基础上,结合实际代码示例,说明如何构建一个可落地的多平台抽象层。
一、问题背景:为什么需要平台抽象层
在未做抽象的情况下,业务代码通常会直接依赖平台 API,例如:
tsx
if (cc.sys.platform === cc.sys.WECHAT_GAME) {
wx.login({
success(res) {
console.log(res.code);
}
});
} else if (cc.sys.isNative) {
jsb.reflection.callStaticMethod(...);
}
这种写法在项目初期似乎简单,但随着功能增加,会带来以下问题:
- 平台判断逻辑散落在各个模块
- 新增平台需要修改大量业务代码
- 测试困难,无法模拟平台环境
平台抽象层的目标,就是将这些差异集中管理。
二、基础设计:定义统一接口
抽象层的第一步是定义接口,描述"能力"而非"实现"。
tsx
export interface IPlatform {
login(): Promise<LoginResult>;
share(data: ShareData): Promise<boolean>;
showRewardAd(): Promise<boolean>;
vibrateShort(): void;
}
配套的数据结构:
tsx
export interface LoginResult {
uid: string;
token?: string;
}
export interface ShareData {
title: string;
imageUrl?: string;
}
此时,业务层只关心"登录返回用户 ID",而不关心具体平台返回什么字段。
三、平台实现:差异封装
1. Web 平台实现
tsx
export class WebPlatform implements IPlatform {
async login(): Promise<LoginResult> {
return { uid: "web_user" };
}
async share(data: ShareData): Promise<boolean> {
console.log("share:", data.title);
return true;
}
async showRewardAd(): Promise<boolean> {
console.log("web no ad");
return false;
}
vibrateShort(): void {}
}
2. 微信小游戏实现
tsx
export class WechatPlatform implements IPlatform {
async login(): Promise<LoginResult> {
return new Promise((resolve, reject) => {
wx.login({
success: (res) => {
resolve({ uid: res.code });
},
fail: reject
});
});
}
async share(data: ShareData): Promise<boolean> {
return new Promise((resolve) => {
wx.shareAppMessage({
title: data.title,
imageUrl: data.imageUrl,
success: () => resolve(true),
fail: () => resolve(false)
});
});
}
async showRewardAd(): Promise<boolean> {
// 示例简化
return true;
}
vibrateShort(): void {
wx.vibrateShort();
}
}
可以看到,平台差异被完全封装在实现层中。
3. 原生平台实现
tsx
export class NativePlatform implements IPlatform {
async login(): Promise<LoginResult> {
const token = jsb.reflection.callStaticMethod(
"org/cocos2dx/javascript/AppActivity",
"login",
"()Ljava/lang/String;"
);
return { uid: token };
}
async share(): Promise<boolean> {
return false;
}
async showRewardAd(): Promise<boolean> {
return false;
}
vibrateShort(): void {}
}
四、统一入口:平台管理器
为了避免业务层直接创建实例,需要一个统一入口:
tsx
export class PlatformManager {
private static _instance: IPlatform;
static init(platform: IPlatform) {
this._instance = platform;
}
static get instance(): IPlatform {
return this._instance;
}
}
初始化阶段根据平台注入实现:
tsx
if (cc.sys.platform === cc.sys.WECHAT_GAME) {
PlatformManager.init(new WechatPlatform());
} else if (cc.sys.isNative) {
PlatformManager.init(new NativePlatform());
} else {
PlatformManager.init(new WebPlatform());
}
五、业务层使用方式
业务层代码不再关心平台差异:
tsx
async function startGame() {
const user = await PlatformManager.instance.login();
console.log("当前用户:", user.uid);
const success = await PlatformManager.instance.showRewardAd();
if (success) {
console.log("发放奖励");
}
}
这种方式的核心价值在于:
- 无平台判断
- 无平台 API
- 可直接复用
六、能力降级处理
不同平台能力不一致,需要在抽象层统一处理。
例如广告在 Web 不支持:
tsx
async showRewardAd(): Promise<boolean> {
console.warn("当前平台不支持广告");
return false;
}
业务层只需要判断返回值:
tsx
if (await platform.showRewardAd()) {
// 发奖励
}
无需额外兼容逻辑。
七、模块化拆分(进阶优化)
当项目规模扩大,可以按功能拆分接口:
tsx
export interface IUser {
login(): Promise<LoginResult>;
}
export interface IAd {
showRewardAd(): Promise<boolean>;
}
export interface IShare {
share(data: ShareData): Promise<boolean>;
}
组合为总接口:
tsx
export interface IPlatform extends IUser, IAd, IShare {}
这种方式可以降低耦合,提高扩展性。
八、调试与扩展能力
抽象层的另一个优势是支持"替换实现"。
例如开发阶段使用 Mock:
tsx
export class DebugPlatform implements IPlatform {
async login(): Promise<LoginResult> {
return { uid: "debug_user" };
}
async showRewardAd(): Promise<boolean> {
return true;
}
async share(): Promise<boolean> {
return true;
}
vibrateShort(): void {}
}
切换只需一行:
tsx
PlatformManager.init(new DebugPlatform());
无需修改任何业务代码。
九、总结
平台抽象层的核心价值在于将"平台差异"转化为"统一能力接口",并通过实现层进行隔离。其关键设计点包括:
- 面向能力的接口定义
- 统一的数据结构
- Promise 化异步处理
- 平台实现隔离
- 集中管理入口
结合合理的工程结构与模块划分,可以在不增加业务复杂度的前提下,实现高质量的多平台支持。这种设计不仅适用于游戏项目,也适用于任何需要跨端运行的应用系统。