新手学习记录 试着分享一下
新手小白主要还是贴贴代码 可能有点冗余
全局类和游戏启动
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更顺手
以上是我的一小段分享