Oops Framework-8-由空项目创建第一个登录界面

一、项目架构

因为Oops Framework整个的流程是基本固定的,所以要做的就是手动填满所有的流程,做的所有东西实际上都要按部就班的按规则做。在动手编码前,先明确整体架构流程,确保各模块职责清晰。

复制代码
┌─────────────────────────────────────────────────┐
│                    Main.ts                       │
│  游戏入口,继承框架Root,初始化ECS/UI/SDK        │
└──────────────┬──────────────────────────────────┘
               │
   ┌───────────▼────────────┐
   │   Initialize (ECS)      │
   │  首个ECS实体,启动资源加载流程                │
   └───────────┬────────────┘
               │
   ┌───────────▼────────────┐
   │  InitResSystem          │
   │  异步队列加载:Bundle→多语言→通用资源        │
   └───────────┬────────────┘
               │
   ┌───────────▼────────────┐
   │  LoadingViewComp        │
   │  加载界面+原生热更逻辑                        │
   └───────────┬────────────┘
               │
   ┌───────────▼────────────┐
   │  Login Component        │
   │  多渠道登录(测试/SDK/微信)+ 账号鉴权        │
   └───────────┬────────────┘
               │
   ┌───────────▼────────────┐
   │  Account / AccountData  │
   │  账号数据管理+登录状态维护                    │
   └─────────────────────────┘

二、环境准备

开始前确保以下环境就绪:

  1. 安装 Cocos Creator 3.8+(兼容 Oops Framework);
  2. 安装 Oops Framework 插件(执行框架提供的update-oops-plugin-framework.bat/.sh);
  3. 确认项目目录结构规范,assets/resources里面只保证config.json文件
  4. 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 验证要点

  1. 确认Main组件挂载到场景根节点,且根节点包含gamegui子节点;
  2. 检查config.jsonlocalDataKey/localDataIv为 16 位,JSON 格式合法;这步也可以忽略
  3. 确保 UI 预制体路径与UIConfigData中的配置一致;
  4. 多语言文件包含所有使用的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 实体

最终测试界面如下

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

相关推荐
速易达网络1 小时前
躲避巨石游戏 · Python版
游戏
wgc2k1 小时前
Oops Framework-7-由空项目创建Oops Framework项目
游戏·cocos2d
wgc2k1 天前
Oops Framework-6-项目中如何使用AI的思路
人工智能·游戏·cocos2d
小熊Coding1 天前
从零打造一款回合制 RPG 游戏:基于 Pygame 的《塔影守卫》全解析
python·游戏·计算机专业·pygame·rpg·2d游戏
小雨下雨的雨1 天前
iOS风格计算器 - 鸿蒙PC Electron框架上的技术实现详解
游戏·ios·华为·electron·harmonyos·鸿蒙
小雨下雨的雨1 天前
五子棋AI在鸿蒙PC Electron上的实现的原理与实践
人工智能·游戏·华为·electron·harmonyos·鸿蒙
小雨下雨的雨1 天前
通过鸿蒙PC Electron框架技术完成-井字棋游戏 - 实现详解
前端·javascript·游戏·华为·electron·鸿蒙
小雨下雨的雨1 天前
基于鸿蒙PC Electron框架技术完成的五子棋游戏 - 技术实现详解
前端·javascript·游戏·华为·electron·鸿蒙
提子拌饭1332 天前
逛三园游戏——基于鸿蒙PC Electron框架实现
前端·javascript·游戏·华为·electron·鸿蒙