新手学习记录 试着分享一下

新手学习记录 试着分享一下

新手小白主要还是贴贴代码 可能有点冗余

全局类和游戏启动

Global.ts

//Global.ts

/**

 * 框架核心目录

 * @author yhh

 * @alias hh

 * @example 引用值时使用hh.xx, 引用类型时使用core.xx

 */

namespace core {

    //#region game

    /**游戏入口 */

    export const game = Game.Launcher;

    /**配置选项 */

    export const options = Options;

    //#endregion

    //#region manager

    /**管理器基类 */

    export const manager = Manager;

    /**事件管理器 */

    export const eventMgr = EventMgr.GetInstance<EventMgr>();

    /**层级管理器 */

    export const layerMgr = LayerMgr.GetInstance<LayerMgr>();

    /**配置管理器 */

    export const configMgr = ConfigMgr.GetInstance<ConfigMgr>();

    ... ...

    //#endregion

    //#region tool

    /**实用工具 */

    export const utils = Utils;

    /**椰子头工具 */

    export const cocos = Cocos;

    /**装饰器 */

    export const decorator = Decorator;

    /**工具合集 */

    export const tools = Object.assign(utils, cocos, decorator);

    ... ...

    //#endregion

    //#region enum

    /**事件枚举 */

    export const enum_event = Enum_Event;

    ... ...

    //#endregion

    //#region type

    /**

     * 指定类型属性字面量类型

     * @example type event = PickValue<cc.EditBox, cc.Component.EventHandler[]>;

     */

    export type PickValue<T extends Object, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];

    ... ...

    //#endregion

}

/**

 * 异步引用脚本方法

 * @deprecated 一般使用require()同步加载

 * @param importer import()

 * @returns 动态导入的模块

 */

function requireAsync<T extends Promise<any>>(importer: T): Awaited<T> {

    //获取动态导入的模块

    let modules: Awaited<T> = Object.create(null);

    importer.then(exports => modules = exports);

    //处理异步返回

    return new Proxy(modules, {

        get(target: Awaited<T>, module: string) {

            //获取模块

            if (modules[module]) return modules[module];

            //调用模块

            return new Proxy(modules, {

                get(target: Awaited<T>, property: string) {

                    if (modules[module]) return modules[module][property];

                    console.log(`${modules[module]}模块未完载, 调用${property}方法失败!`);

                    return null;

                },

            });

        },

    });

}

/** 全局声明 */

declare global {

    interface Window {

        hh: typeof import('./Global').default;

    }

    /**框架核心目录 */

    const hh: typeof import('./Global').default;

    /**

     * 同步引用脚本方法

     * @param module 模块加载路径

     * @example require<typeof import('./Global')>('./Global').default;

     * @returns 返回同步加载的模块

     */

    function require<T = { default: any }>(module: string): T;

    /**

     * 异步引用脚本方法

     * @deprecated 请直接使用require()同步加载

     * @param importer import()

     * @example requireAsync(import('./Global')).default.xxx;

     * @returns 动态导入的模块代理

     */

    function requireAsync<T extends Promise<any>>(importer: T): Awaited<T>;

}

//挂载框架核心目录到全局

window.hh = core;

window.requireAsync = requireAsync;

//对外开放框架核心目录模块

export { core as default };

里面主要放各种管理类工具类枚举类以及一些ts的工具类型

我的想法是做成目录的形式 在Global.ts文件内集中导入框架的各个模块 目录的形式一目了然 方便跳转模块掌握框架

'全局’的做法就是把一个namespace或class或变量进行全局声明然后挂载到全局上(说了好像没说)

Game.ts

//Game.ts

//自动运行

cc.director.once(cc.Director.EVENT_AFTER_SCENE_LAUNCH, (scene: cc.Scene) => {

    if (CC_EDITOR) return;

    //添加画布

    if (!scene.children.length) scene.addChild(new cc.Node('game'));

    //适配屏幕

    hh.cocos.adaptResolution();

    //设置帧率

    cc.game.setFrameRate(hh.options.frameRate.high);

    //启动游戏

    hh.game.boot();

});

/**游戏入口 */

class Game {

    private static _Launcher: Game = null;

    /**启动器 */

    public static get Launcher(): Game {

        if (this._Launcher === null) this._Launcher = new Game();

        return this._Launcher;

    }

    private constructor() { }

    /**

     * 启动游戏方法

     */

    public async boot() {

        //加载管理器

        hh.manager.LoadManagers();

        //发送加载公共存档消息

        await hh.eventMgr.requestMessage(hh.enum_event.LoadEvent.LOAD_ARCHIVE, 'Public');

        //初始化游戏音量

        hh.audioMgr.saveVolumeSettings(0, 1);

        //初始化加载页

        await this.initLoadScene();

    }

    /**

     * 初始化加载页方法

     */

    public async initLoadScene() {

        //创建加载页面并初始化

        let LoadingPage = await hh.entityMgr.getPageEntity(hh.enum_entity.LoadPage);

        let loadingPage = await LoadingPage.init();

    }

    /**

     * 初始化主页方法

     */

    public async initMainScene() {

        //创建顶部栏页面并初始化

        let TopmBar = await hh.entityMgr.getPageEntity(hh.enum_entity.TopBar);

        let topmBar = await TopmBar.init();

        //创建底部栏页面并初始化

        let BottomBar = await hh.entityMgr.getPageEntity(hh.enum_entity.BottomBar);

        let bottomBar = await BottomBar.init();

        //创建主城页面并初始化

        let HomePage = await hh.entityMgr.getPageEntity(hh.enum_entity.HomePage);

        let homePage = await HomePage.init();

        //创建战斗页面并初始化

        let FightPage = await hh.entityMgr.getPageEntity(hh.enum_entity.FightPage);

        let fightPage = await FightPage.init();

    }

}

/**游戏配置选项 */

namespace Options {

    /**设计分辨率 */

    export const designSize = {

        /**竖屏 */

        vertical: new cc.Size(750, 1334),

        /**横屏 */

        horizontal: new cc.Size(1280, 720)

    };

    /**游戏帧率 */

    export const frameRate = {

        /**低帧率 */

        low: 30.1,

        /**中帧率 */

        middle: 60.1,

        /**高帧率 */

        high: 90.1,

        /**超高帧率 */

        higher: 120.1,

        /**最高帧率 */

        highest: 144.1

    };

    ... ...

}

export { Options, Game as default };

我看大家都喜欢用一个App.ts然后挂到场景的Canvas节点上当作游戏入口

我是场景和预制体上除了cocos内置的组件外基本不挂脚本(至于如何做到连App.ts都不挂, 下面会说到)

开发流派,到底哪种才是正途算是我在论坛上的启蒙文章 让我从枯燥的语言基础过渡到了用cocos继续学习

我还是更喜欢面向对象new来new去的 最早cocos让我只有组件脚本生命周期@property的概念 淡化了自定义类定义属性实例化调用方法的概念 对新手说不上来是不是一件好事(和组合优于继承不是一码事)

毕竟应该也有很多像我一样0基础从语言开始学然后到组件的使用(啊b上看的飞羽老师的视频) 可能刚学会组件脚本就又忘了普通的类长啥样了…

以上只是我在新手角度站在门口的看法 另外也希望后来者学习cocos不要从入门到放弃

好了回归正题 我是如何做到连App.ts都不挂脚本的呢

答案是我把场景里的Canvas节点直接删掉了哈哈哈 然后把添加Canvas和Camera以及Widget组件封装到了适配分辨率的方法里 这样就得到了一个empty场景当启动场景 然后再重新创建一个editor场景用于平时测试

在我看来2.x版本Scene的概念应该是基于编辑器而不是游戏的 所以我写的是单场景加多预制体框架 一个layer预制体其实就相当于是游戏场景的概念了


//自动运行

cc.director.once(cc.Director.EVENT_AFTER_SCENE_LAUNCH, (scene: cc.Scene) => {

    if (CC_EDITOR) return;

    //添加画布

    if (!scene.children.length) scene.addChild(new cc.Node('game'));

    //适配屏幕

    hh.cocos.adaptResolution();

    //设置帧率

    cc.game.setFrameRate(hh.options.frameRate.high);

    //启动游戏

    hh.game.boot();

});

顺便分享一下其它东东

单例继承

论坛里也是有的


//单例基类

class Singleton {

    private static _Instance: Singleton = null;

    protected constructor() { }

    public static GetInstance<T extends Singleton>(): T {

        if (this._Instance === null) this._Instance = new this();

        return this._Instance as T;

    }

}

//继承单例基类

class Test extends Singleton { }

//单例对象

const test = Test.GetInstance<Test>(); //Test.GetInstance()是没有准确类型提示的, 需要加<Test>

工具类分类

大家通常都会用xxxUtils或xxxHelp来命名自己的工具类 甚至全部塞到一个类里面


class Utils {

    static Method() { }

    ... ...

}

其实归类到一个个namespace里会更加合理


namespace ArrayUtils {

    export function arrayMethod() { }

    ... ...

}

namespace MathUtils {

    export function mathMethod() { }

    ... ...

}

如果分类模糊或者忘了在哪个分类 可以利用Object.assign()方法创建一个工具类合集

let tools = Object.assign(Utils, ArrayUtils, MathUtils);

我个人觉得键盘敲tools比utils和help更顺手

以上是我的一小段分享

1赞