一、项目架构
因为Oops Framework整个的流程是基本固定的,所以要做的就是手动填满所有的流程,做的所有东西实际上都要按部就班的按规则做。在动手编码前,先明确整体架构流程,确保各模块职责清晰。
┌─────────────────────────────────────────────────┐
│ Main.ts │
│ 游戏入口,继承框架Root,初始化ECS/UI/SDK │
└──────────────┬──────────────────────────────────┘
│
┌───────────▼────────────┐
│ Initialize (ECS) │
│ 首个ECS实体,启动资源加载流程 │
└───────────┬────────────┘
│
┌───────────▼────────────┐
│ InitResSystem │
│ 异步队列加载:Bundle→多语言→通用资源 │
└───────────┬────────────┘
│
┌───────────▼────────────┐
│ LoadingViewComp │
│ 加载界面+原生热更逻辑 │
└───────────┬────────────┘
│
┌───────────▼────────────┐
│ Login Component │
│ 多渠道登录(测试/SDK/微信)+ 账号鉴权 │
└───────────┬────────────┘
│
┌───────────▼────────────┐
│ Account / AccountData │
│ 账号数据管理+登录状态维护 │
└─────────────────────────┘
二、环境准备
开始前确保以下环境就绪:
- 安装 Cocos Creator 3.8+(兼容 Oops Framework);
- 安装 Oops Framework 插件(执行框架提供的
update-oops-plugin-framework.bat/.sh); - 确认项目目录结构规范,
assets/resources里面只保证config.json文件 assets/bundle为框架默认资源加载目录。
三、核心模块手动实现
步骤 1:基础配置文件
1.1 框架核心配置(config.json)
创建assets/resources/config.json,定义项目基础配置(版本、服务器、多语言、UI 层级等):
json
{
"type": "prod",
"config": {
"prod": {
"version": "1.0.0",
"package": "com.example.game",
"frameRate": 60,
"localDataKey": "your-encrypt-key", // AES加密密钥(16位)
"localDataIv": "your-encrypt-iv-", // AES加密向量(16位)
"httpServer": "",
"httpTimeout": 10000,
"mobileSafeArea": false,
"stats": 0
}
},
"language": {
"type": ["zh", "en"],
"default": "zh",
"path": {
"json": "language/json"
}
},
"bundle": {
"default": "bundle"
},
"gui": [
{ "type": "UI", "name": "LayerUI" },
{ "type": "PopUp", "name": "LayerPopUp" },
{ "type": "Dialog", "name": "LayerDialog" }
]
}
1.2 多语言配置
这里只是示例
创建多语言 JSON 文件,统一管理文本,避免硬编码:
-
assets/bundle/language/json/zh.json{
"loading_load_player": "加载玩家数据",
"loading_load_json": "加载配置表",
"update_tips_check_update": "检查更新中"
} -
assets/bundle/language/json/en.json:{
"loading_load_player": "Loading player data",
"loading_load_json": "Loading config tables",
"update_tips_check_update": "Checking for updates"
}
1.3 常量与枚举定义
存储键枚举(StorageKey.ts) - assets/script/game/common/config/StorageKey.ts:
export enum StorageKey {
Account = 'Account',
Server = 'Server',
LoginUsePolicy = 'LoginUsePolicy',
}
游戏常量(GameConst.ts) - assets/script/game/common/config/GameConst.ts:
// 协议ID枚举
export enum MsgID {
Login = 1001,
LoginAuth = 1002,
}
// 埋点类型枚举
export enum SubmitTypes {
EnterGame = 1,
ExitGame = 2,
}
游戏事件枚举(GameEvent.ts) - assets/script/game/common/config/GameEvent.ts:
export enum GameEvent {
GameServerConnected = "GameServerConnected",
LoginSuccess = "LoginSuccess",
LoginError = 'LoginError',
Tick1S = 'Tick1S', // 1秒心跳
}
资源路径工具(GameResPath.ts) - assets/script/game/common/config/GameResPath.ts:
export class GameResPath {
// 获取龙骨模型路径
static getModelPath(name: string): { skeleton: string; atlas: string } {
return {
skeleton: `model/${name}/${name}_ske`,
atlas: `model/${name}/${name}_tex`,
};
}
// 获取通用精灵帧路径
static getSpriteFrameCommon(name: string): string {
return `common/texture/${name}/spriteFrame`;
}
}
// 音频路径常量
export const AudioClipMusic = {
Main: 'audio/bgm_main',
Game: 'audio/bgm_game',
};
UI 配置(GameUIConfig.ts) - assets/script/game/common/config/GameUIConfig.ts:
import { LayerType } from "../../../../../extensions/oops-plugin-framework/assets/core/gui/layer/LayerEnum";
import { UIConfig } from "../../../../../extensions/oops-plugin-framework/assets/core/gui/layer/UIConfig";
// UI唯一标识
export enum UIID {
Loading = 1,
Login = 2,
Main = 3,
}
// UIID与层/预制体映射
export var UIConfigData: { [key: number]: UIConfig } = {
[UIID.Loading]: { layer: LayerType.UI, prefab: "gui/loading/loading" },
[UIID.Login]: { layer: LayerType.UI, prefab: "gui/login/login" },
[UIID.Main]: { layer: LayerType.UI, prefab: "gui/main/main" },
};
步骤 2:单例模块注册
创建assets/script/game/common/SingletonModuleComp.ts,统一管理全局单例模块,简化调用:
运行
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { account, Account } from "../account/Account";
import { Initialize } from "../initialize/Initialize";
@ecs.register('SingletonModule')
export class SingletonModuleComp extends ecs.Comp {
// 初始化模块
initialize: Initialize = null!;
// 账号模块(getter封装)
get account(): Account { return account; }
reset() { }
}
// 全局单例访问器
export var smc: SingletonModuleComp = ecs.getSingleton(SingletonModuleComp);
步骤 3:游戏入口(Main.ts)
创建assets/script/Main.ts,作为游戏根入口,继承框架Root类,初始化核心能力:
运行
import { director, game, macro, _decorator } from 'cc';
import { oops } from '../../extensions/oops-plugin-framework/assets/core/Oops';
import { Root } from '../../extensions/oops-plugin-framework/assets/core/Root';
import { ecs } from '../../extensions/oops-plugin-framework/assets/libs/ecs/ECS';
import { GameEvent } from './game/common/config/GameEvent';
import { UIConfigData, UIID } from './game/common/config/GameUIConfig';
import { smc } from './game/common/SingletonModuleComp';
import { EcsInitializeSystem, Initialize } from './game/initialize/Initialize';
import { PlatformSDK } from './game/common/sdk/PlatformSDK';
const { ccclass } = _decorator;
@ccclass('Main')
export class Main extends Root {
start() {
// 初始化平台SDK
PlatformSDK.init();
// 注册定时心跳事件
this.schedule(() => { oops.message.dispatchEvent(GameEvent.Tick1S) }, 1, macro.REPEAT_FOREVER, 0.1);
}
onDestroy() {
this.unscheduleAllCallbacks();
}
// 配置加载完成后,启动初始化ECS实体
protected run() {
smc.initialize = ecs.getEntity<Initialize>(Initialize);
}
// 注册UI配置
protected initGui() {
oops.gui.init(UIConfigData);
}
// 注册ECS系统
protected initEcsSystem() {
oops.ecs.add(new EcsInitializeSystem());
}
}
// 全局快捷访问
export var app = new Main();
心跳网络游戏开发中使用,单机可以注释掉
步骤 4:初始化模块(ECS)
4.1 初始化实体(Initialize.ts)
创建assets/script/game/initialize/Initialize.ts,作为首个 ECS 实体,启动资源加载:
运行
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCEntity } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCEntity";
import { InitResComp, InitResSystem } from "./bll/InitRes";
@ecs.register('Initialize')
export class Initialize extends CCEntity {
protected init() {
this.add(InitResComp); // 添加资源加载组件
}
}
// ECS初始化系统
export class EcsInitializeSystem extends ecs.System {
constructor() {
super();
this.add(new InitResSystem());
}
}
4.2 资源加载逻辑(InitRes.ts)
创建assets/script/game/initialize/bll/InitRes.ts,实现异步资源加载队列:
typescript
运行
import { oops } from "../../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { AsyncQueue, NextFunction } from "../../../../../extensions/oops-plugin-framework/assets/libs/collection/AsyncQueue";
import { ecs } from "../../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { UIID } from "../../common/config/GameUIConfig";
import { Initialize } from "../Initialize";
import { LoadingViewComp } from "../view/LoadingViewComp";
@ecs.register('InitRes')
export class InitResComp extends ecs.Comp {
reset() { }
}
// 资源加载系统:Bundle→多语言→通用资源→加载界面
export class InitResSystem extends ecs.ComblockSystem implements ecs.IEntityEnterSystem {
filter(): ecs.IMatcher {
return ecs.allOf(InitResComp);
}
entityEnter(e: Initialize): void {
var queue: AsyncQueue = new AsyncQueue();
// 1. 加载远程Bundle
this.loadBundle(queue);
// 2. 加载多语言包
this.loadLanguage(queue);
// 3. 加载通用资源
this.loadCommon(queue);
// 4. 加载完成后打开加载界面
this.onComplete(queue, e);
// 执行队列
queue.play();
}
private loadBundle(queue: AsyncQueue) {
queue.push(async (next: NextFunction) => {
const bundleName = oops.res.defaultBundleName;
if (bundleName && bundleName !== "resources") {
await oops.res.loadBundle(bundleName);
}
next();
});
}
private loadLanguage(queue: AsyncQueue) {
queue.push((next: NextFunction) => {
let lan = oops.storage.get("language");
if (!lan) {
lan = oops.config.game.languageDefault;
oops.storage.set("language", lan);
}
oops.language.setLanguage(lan, next);
});
}
private loadCommon(queue: AsyncQueue) {
queue.push((next: NextFunction) => {
oops.res.loadDir("common", next);
});
}
private onComplete(queue: AsyncQueue, e: Initialize) {
queue.complete = async () => {
var node = await oops.gui.open(UIID.Loading);
if (node) e.add(node.getComponent(LoadingViewComp)!);
e.remove(InitResComp);
};
}
}
步骤 5:加载界面与热更
5.1 加载界面组件(LoadingViewComp.ts)
创建assets/script/game/initialize/view/LoadingViewComp.ts,实现加载进度展示与资源加载:
import { Prefab, sys, _decorator } from "cc";
import { oops } from "../../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { ecs } from "../../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCViewVM } from "../../../../../extensions/oops-plugin-framework/assets/module/common/CCViewVM";
import { UIID } from "../../common/config/GameUIConfig";
import { HotUpdate } from "./HotUpdate";
import { PlatformSDK } from "../../common/sdk/PlatformSDK";
import { Initialize } from "../Initialize";
const { ccclass } = _decorator;
@ccclass('LoadingViewComp')
@ecs.register('LoadingView', false)
export class LoadingViewComp extends CCViewVM<Initialize> {
// 绑定UI的数据(进度、提示文本)
data: any = {
finished: 0,
total: 0,
progress: "0",
prompt: ""
};
private progress: number = 0;
reset(): void {
this.data.prompt = oops.language.getLangByID("loading_load_player");
oops.gui.open(UIID.Login); // 加载完成打开登录界面
oops.gui.remove(UIID.Loading); // 关闭加载界面
}
start() {
// 原生平台且不跳过热更时,启动热更
if (sys.isNative && !PlatformSDK.isSkipHotUpdate()) {
this.addComponent(HotUpdate);
} else {
this.enter();
}
}
enter() {
this.loadRes();
}
private async loadRes() {
this.data.progress = 0;
await this.loadCustom(); // 加载JSON配置表
this.loadGameRes(); // 加载游戏预制体/纹理
}
private loadCustom() {
this.data.prompt = oops.language.getLangByID("loading_load_json");
return new Promise(async (resolve) => {
// 此处扩展:加载自定义JSON配置表
resolve();
});
}
private loadGameRes() {
this.data.prompt = oops.language.getLangByID("loading_load_game");
oops.res.loadDir(
"gui/main", Prefab,
this.onProgressCallback.bind(this),
this.onCompleteCallback.bind(this)
);
}
// 加载进度回调
private onProgressCallback(finished: number, total: number) {
this.data.finished = finished;
this.data.total = total;
var progress = finished / total;
if (progress > this.progress) {
this.progress = progress;
this.data.progress = (progress * 100).toFixed(2);
}
}
// 加载完成回调
private onCompleteCallback() {
this.ent.remove(LoadingViewComp);
}
}
5.2 热更管理(HotUpdate.ts + Hot.ts)
HotUpdate.ts - assets/script/game/initialize/view/HotUpdate.ts:
运行
import { Component, game, _decorator } from "cc";
import { oops } from "../../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { Hot, HotOptions } from "./Hot";
import { LoadingViewComp } from "./LoadingViewComp";
const { ccclass } = _decorator;
@ccclass('HotUpdate')
export class HotUpdate extends Component {
private hot = new Hot();
private lv: LoadingViewComp = null!;
onLoad() {
this.lv = this.getComponent(LoadingViewComp)!;
this.lv.data.prompt = oops.language.getLangByID("update_tips_check_update");
this.startHotUpdate();
}
private startHotUpdate() {
let options = new HotOptions();
// 热更进度回调
options.onUpdateProgress = (event: jsb.EventAssetsManager) => {
let pc = event.getPercent();
if (!isNaN(pc)) {
this.lv.data.finished = event.getDownloadedFiles();
this.lv.data.total = event.getTotalFiles();
this.lv.data.progress = (pc * 100).toFixed(2);
}
};
// 无需热更
options.onNoNeedToUpdate = () => { this.lv.enter(); };
// 热更成功(重启游戏)
options.onUpdateSucceed = () => {
this.lv.data.progress = 100;
setTimeout(() => { game.restart(); }, 1000);
};
// 热更失败(重试)
options.onUpdateFailed = () => {
this.hot.checkUpdate();
};
this.hot.init(options);
}
}
Hot.ts - assets/script/game/initialize/view/Hot.ts(热更核心逻辑):
运行
export class HotOptions {
onUpdateProgress?: (event: any) => void;
onNoNeedToUpdate?: () => void;
onUpdateSucceed?: () => void;
onUpdateFailed?: (code: number, msg: string) => void;
}
export class Hot {
private options: HotOptions = null!;
init(options: HotOptions) {
this.options = options;
this.checkUpdate();
}
// 检查热更(需对接项目实际的manifest地址)
checkUpdate() {
if (!jsb) {
this.options.onNoNeedToUpdate?.();
return;
}
// 扩展点:实现jsb.AssetsManager的热更逻辑
this.options.onNoNeedToUpdate?.();
}
// 版本对比工具方法
versionCompareHandle(versionA: string, versionB: string): number {
const partsA = versionA.split('.').map(Number);
const partsB = versionB.split('.').map(Number);
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
const a = partsA[i] || 0;
const b = partsB[i] || 0;
if (a !== b) return a - b;
}
return 0;
}
}
步骤 6:账号与登录模块
6.1 账号管理(Account.ts + AccountData.ts)
Account.ts - assets/script/game/account/Account.ts(登录 / 登出 / 鉴权):
运行
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { GameEvent } from "../common/config/GameEvent";
import { MsgID } from "../common/config/GameConst";
import { httpChannel } from "../common/net/HttpChannelManager";
import { AccountData } from "./AccountData";
export class Account {
data: AccountData = null;
// 账号密码登录
login(req: LoginReq) {
httpChannel.req(MsgID.Login, req,
this.onLoginHttpResp.bind(this),
() => { oops.message.dispatchEvent(GameEvent.LoginError); }
);
}
// SDK token登录
loginAuth(req: LoginAuthReq) {
httpChannel.req(MsgID.LoginAuth, req,
this.onLoginHttpResp.bind(this),
() => { oops.message.dispatchEvent(GameEvent.LoginError); }
);
}
// 登出
logout() {
this.data = null;
}
// 检查是否登录
isLogin(): boolean {
return this.data != null;
}
// 登录成功回调
private onLoginHttpResp(httpResp: HttpSuccResp) {
if (httpResp.code !== 0) {
oops.message.dispatchEvent(GameEvent.LoginError, `登录失败(${httpResp.message})`);
return;
}
const resp = httpResp.data as LoginResp;
if (!resp?.ok) {
oops.message.dispatchEvent(GameEvent.LoginError, `登录失败(${resp.msg})`);
return;
}
this.data = new AccountData(httpResp.data);
oops.storage.setUser(this.data.id);
httpChannel.setAuthorization(this.data.token);
oops.message.dispatchEvent(GameEvent.LoginSuccess);
}
}
// 全局账号实例
export var account = new Account();
// 类型定义(可迁移到NetProtocol.ts)
export interface LoginReq {
username: string;
password: string;
serverid: number;
}
export interface LoginAuthReq {
userid: string;
gameLoginToken: string;
serverid: number;
channelType: number;
}
export interface HttpSuccResp {
code: number;
message: string;
data: any;
}
export interface LoginResp {
ok: boolean;
msg?: string;
gt: string;
role: { id: string; name: string };
info: { rolelevel: number };
}
AccountData.ts - assets/script/game/account/AccountData.ts(账号数据封装):
运行
import { LoginResp } from "./Account";
export class AccountData {
protected data: LoginResp = null;
constructor(data: LoginResp) {
this.data = data;
}
get info() { return this.data; }
get id(): string { return this.data.role.id; }
get name(): string { return this.data.role.name; }
get token(): string { return this.data.gt; }
get heroLevel(): number { return this.data.info.rolelevel; }
}
6.2 登录 UI 组件(Login.ts)
创建assets/script/game/login/Login.ts,实现登录界面交互:
运行
import { Component, EditBox, Toggle, _decorator } from 'cc';
import { oops } from '../../../../extensions/oops-plugin-framework/assets/core/Oops';
import { app } from '../../Main';
import { GameEvent } from '../common/config/GameEvent';
import { UIID } from "../common/config/GameUIConfig";
import { StorageKey } from "../common/config/StorageKey";
import { SubmitTypes } from "../common/config/GameConst";
import { smc } from '../common/SingletonModuleComp';
import { PlatformSDK } from "../common/sdk/PlatformSDK";
import { LoginReq, LoginAuthReq } from "../account/Account";
const { ccclass, property } = _decorator;
@ccclass('Login')
export class Login extends Component {
@property(EditBox) private edtAccount: EditBox;
@property(Toggle) private tglUsePolicy: Toggle;
@property(Node) private nodeUsePolicy: Node;
private _inLoading = false;
get loading() { return this._inLoading; }
set loading(v: boolean) { this._inLoading = v; }
async onLoad() {
// 监听登录事件
oops.message.on(GameEvent.LoginSuccess, this._eventHandler, this);
oops.message.on(GameEvent.LoginError, this._eventHandler, this);
}
async start() {
// 测试渠道预填账号
if (PlatformSDK.isTestChannel()) {
var user = oops.storage.get(StorageKey.Account);
if (user) { this.edtAccount.string = user; }
} else {
this.edtAccount.node.active = false;
// 已同意协议则自动登录
if (oops.storage.getNumber(StorageKey.LoginUsePolicy, 0)) {
PlatformSDK.doLogin();
}
}
this.initView();
}
onDestroy() {
oops.message.off(GameEvent.LoginSuccess, this._eventHandler, this);
oops.message.off(GameEvent.LoginError, this._eventHandler, this);
}
// 初始化协议勾选状态
initView() {
var storage = oops.storage.get(StorageKey.LoginUsePolicy);
this.nodeUsePolicy.active = (storage == "" || Number(storage) != 1);
}
// 事件处理
private async _eventHandler(event: string, msg: string = null) {
if (event == GameEvent.LoginSuccess) {
PlatformSDK.doSubmit(SubmitTypes.EnterGame);
await oops.gui.open(UIID.Main);
oops.gui.remove(UIID.Login, true);
} else if (event == GameEvent.LoginError) {
this.loading = false;
oops.gui.toast(msg || "登录失败");
}
}
// 登录按钮点击
onLoginClick() {
if (this.loading) return;
if (PlatformSDK.isTestChannel()) {
this.testLogin();
} else {
this.sdkLogin();
}
}
// 测试渠道登录
private testLogin() {
const account = this.edtAccount.string.trim();
if (!account) { oops.gui.toast("请输入账号"); return; }
oops.storage.set(StorageKey.Account, account);
const req: LoginReq = { username: account, password: "123123", serverid: 1 };
smc.account.login(req);
this.loading = true;
}
// SDK登录
private async sdkLogin() {
const data = await PlatformSDK.doLogin();
if (!data) return;
await this.serverLogin(data);
}
// 对接游戏服务器登录
private async serverLogin(data: RespLogin) {
this.loading = true;
// 扩展点:对接平台鉴权服务器、获取游戏服务器信息、最终登录
const reqLogin: LoginAuthReq = {
userid: data.uid,
gameLoginToken: "", // 从平台鉴权服务器获取
serverid: 1,
channelType: PlatformSDK.cfg.channel_type,
};
smc.account.loginAuth(reqLogin);
}
// 同意协议
onClickSure() {
if (!this.tglUsePolicy.isChecked) {
oops.gui.toast('请同意隐私政策');
return;
}
oops.storage.set(StorageKey.LoginUsePolicy, 1);
PlatformSDK.doLogin();
this.nodeUsePolicy.active = false;
}
// 退出游戏
onClickCancel() { app.exit(); }
}
// 类型定义
export interface RespLogin {
uid: string;
token: string;
}
步骤 7:网络与 SDK 封装
7.1 HTTP 请求管理(HttpChannelManager.ts)
创建assets/script/game/common/net/HttpChannelManager.ts,封装游戏服务器 HTTP 请求:
typescript
运行
import { oops } from "../../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { GameServerConfig } from "../../login/Login";
class HttpChannelManager {
private authorization: string = "";
private gameServer: GameServerConfig = null;
// 设置鉴权Token
setAuthorization(token: string) { this.authorization = token; }
// 设置游戏服务器配置
setGameServer(s: GameServerConfig) { this.gameServer = s; }
// 发送HTTP请求
req(msgId: number, req: any, onOk: Function, onErr?: Function) {
if (!this.gameServer?.gameUrl) {
onErr?.("游戏服务器未配置");
return;
}
const url = `${this.gameServer.gameUrl}/api/${msgId}`;
const headers: Record<string, string> = {
"Content-Type": "application/json",
};
if (this.authorization) {
headers["Authorization"] = `Bearer ${this.authorization}`;
}
// 扩展点:实现oops.http.post请求
// oops.http.post(url, req, headers, onOk, onErr);
}
}
export var httpChannel = new HttpChannelManager();
7.2 平台 SDK 抽象(PlatformSDK.ts)
创建assets/script/game/common/sdk/PlatformSDK.ts,统一多平台 SDK 接入:
运行
import { game } from "cc";
import { SubmitTypes } from "../config/GameConst";
import { RespLogin } from "../../login/Login";
class PlatformSDK {
cfg = {
channel_type: 0,
skip_hot_update: false,
};
// 初始化SDK
init() { /* 扩展点:对接微信/字节/QQ等SDK */ }
// 销毁SDK
destroy() { /* 扩展点:清理SDK资源 */ }
// 是否跳过热更
static isSkipHotUpdate(): boolean { return platformSDK.cfg.skip_hot_update; }
// SDK登录
static async doLogin(): Promise<RespLogin | null> { /* 扩展点:实现平台登录 */ return null; }
// 埋点上报
static doSubmit(type: SubmitTypes) { /* 扩展点:实现埋点逻辑 */ }
// 是否测试渠道
static isTestChannel(): boolean { return true; }
// 退出游戏
static doExit() { game.end(); }
}
export var platformSDK = new PlatformSDK();
export { PlatformSDK };
四、项目验证与扩展
4.1 验证要点
- 确认
Main组件挂载到场景根节点,且根节点包含game和gui子节点; - 检查
config.json的localDataKey/localDataIv为 16 位,JSON 格式合法;这步也可以忽略 - 确保 UI 预制体路径与
UIConfigData中的配置一致; - 多语言文件包含所有使用的
labId,无缺失。
五、核心 API 速查
| API | 用途 |
|---|---|
oops.gui.open(UIID.X) |
打开指定 UI |
oops.gui.toast(msg) |
显示提示弹窗 |
oops.message.dispatchEvent(ev, data) |
派发全局事件 |
oops.res.loadDir(path, type, progCb, doneCb) |
加载目录资源 |
oops.storage.get/set(key, val) |
本地存储读写 |
oops.language.getLangByID(id) |
获取多语言文本 |
ecs.getEntity<T>(Class) |
获取 ECS 实体 |
最终测试界面如下

这个基本算是游戏开发的最简流程,可基于此骨架,补充业务逻辑,快速落地各类小游戏 / 手游项目。