【VVFramework】单场景懒人极简框架

【懒人极简框架 VVFramework】

为什么取名叫VVFramework?全称 very vegetable framework 是一个很菜的框架[手动doge]

一、开门见山先丢链接

这个框架分两部分,插件和View管理模块,适用单场景开发的同学,理论上多场景也能用,没实验过[doge]

插件适用版本未知,.prefab文件结构一样应该都能用

View管理适用有bundle的版本cocos creator 2.4.x + TS

https://gitee.com/Chou_nan/SimpleFramework.git

二、一点哔哔叨

从刚工作时的2.0.9,到现在的2.4.4,creator的成长可以说是非常的大了。在如此多的版本跨度里,其实我也就用过这俩[再次手动doge],中间跑去搞2dx-lua去了,最近才回到creator的怀抱。

又因为种种原因,一个小小菜鸡要担负起写框架的重任,属实害怕。在网上搜罗一圈大佬们写的真·框架,拿过来一看属实脑阔疼,只能感叹自己太菜了。直到看到 麒麟子大大 在介绍自己框架的文章中的一句话 遵守“大道至简,实用至上”这两个基本原则。 顿然醒悟,网上大佬的框架功能极其丰富,涉及到MVC,MVVM,一连串继承关系,还有跨引擎的公用框架,对我的需求来说就是在用航空母舰打蚊子,于是便决定了自己造个小苍蝇拍。

回顾自己不多的写BUG经验,确定主要解决以下痛点:【主要是懒,想少些点代码,“懒是科技发展的动力”真是至理名言】

  1. 能对脚本需要的节点自动导出,不用每次都手敲一堆声明(用过FGUI的自动导出脚本都说香)
  2. 脚本挂载暴露的参数很多时,对开发时拖节点和往后冷不丁的维护都是噩梦,维护的痛懂的都懂!
  3. 在onEnable和onDisable的里面成篇的on和off事件
 onEnable() {
    this.node.on('Event1', this.func1, this);    
    // ...省去n行
    this.node.on('Eventn', this.funcn, this);
 }
 onDisable() {
    this.node.off('Event1', this.func1, this);
    // ...省去n行
    this.node.off('Eventn', this.funcn, this);
 }

三、插件(针对痛点1)

对于痛点1网上有不少的插件,但为了结合我对于View管理的一些想法,所以对已有插件进行了亿点改造。

1、引入

拉下工程,将packages的prefab-ts-export导入到自己工程相同位置,不会的同学参照官方文档

2、配置

1620837617353

core:功能主要代码

template:自动导出文件的模板文件,导出的声明会放在红框两行注释之间,修改预制体再次导出时会根据这两行注释内的内容进行更新, !!所以其他代码不要放在注释中!!这也是正则检索的标识不要修改!!

1620838412115

export-conf.js:一些必要的配置,有需要导出的组件可自行在compMap修改

3、使用

因为针对的是单场景开发,所以导出功能只做了对prefab文件的,有需要可以自行拓展。

首先选中一个预制体,扩展->prefab-ts-export->导出(快捷键alt+s)

四、View管理

1、导入

a、引入assets/Framework文件夹(-js的不完整无法使用);b、引入SimpleFramework.d.ts代码提示

2、使用

这里的设计使用了bundle这一新特性,类似于FGUI的包,以功能模板为一个bundle包,里面放用到的图片和界面预制体等资源。

1620840682015

a、打开界面

onLoad() {
vv.viewMgr.open('MenuView', '参数1', '参数2');
vv.viewMgr.open('MenuView', (tsComp: typeof vv.BaseView) => {
// tsComp是界面的脚本
},'参数1', '参数2');
let tsComp = await vv.viewMgr.openSync('MenuView', '参数1', '参数2')
let tsComp = vv.viewMgr.get('MenuView');
vv.viewMgr.close('MenuView');
vv.viewMgr.hide('MenuView');
}

这里打开的界面需要配置在脚本 VVViewConfig.ts

/** 界面配置 */
const ViewConfig = { // BundlePath: bundle包的名字,PrefabPath: 预制体在包内的路径,preventTouch在界面打开前是否阻断触摸
'MenuView': {BundlePath: 'TestPanel', PrefabPath: 'MenuView', preventTouch: false},
'ViewA': {BundlePath: 'TestPanel', PrefabPath: 'ViewA', preventTouch: false},
}

ts脚本需为预制体同名,可挂载可不挂载,挂载则必须挂在预制体根节点上,不挂载则会尝试挂载同名脚本。

b、vv.BaseView

要使用vv.viewMgr打开的View必须继承该类,并且额外增加了一些生命函数子类继承

/** vv.view.open时 界面初始化 在 onLoad 和 onEnable执行之后 start执行之前 */
protected init(arg1, arg2, ...) {}
/** vv.view.close时 界面被关闭 */
protected onClose() {}
/** 可做一些UI初始化 init之后 */
protected _initUI() {}
/** 可做初始化事件 _initUI之后 */
protected _initEvent() {}
/** 打开其他界面被隐藏到后台时触发 */
public onToBack() {}
/** 关闭其他界面后自动显示时触发 */
public onToFront() {}

每个名字的View仅能打开一个 ,再次打开时会将其放置在顶层

c、view的层级管理

目前大致分为(可在ViewZOrder.ts进行修改),将View的LayerOrder属性设置为相应的值改变层级,同一层级的View仅展示栈顶的(被隐藏的会调用 onToBack ),当上面的View关闭时,下面的自动显示(会调用 onToFront

/** view 层级 */
export const ViewZOrder = {
    MainView: 0,
    SubView: 200,
    PopView: 500,
    TopView: 900,
    TipsView: 999,
}

五、针对痛点2

当导出插件的配置 useAutoBind为true时,大家会发现导出声明的中装饰器变了,这里是仿造(官方貌似没支持,也可能我姿势不对)TS的 reflect-metadata 实现。

再配合View的 _autoBind 属性开启,在init()方法中可以直接使用导出的节点或组件。

// @property(cc.Node)
// public node1: cc.Node = null;
// 变成了
@metadata("cc.Node")
private node1: cc.Node = null;
_autoBind = true; // 开启自动绑定
init() {
    consoel.log(this.node1) // 已经绑定对应节点
}

六、针对痛点3

继承自BaseView的脚本,在open的时候会自动为形如 func_test 这种中间带下划线的函数注册事件,考虑到不挂载脚本的使用,也对Button进行了点击事件的注册

@metadata("cc.Button")
private _btn_test: cc.Button = null;
​
onLoad() {
    // 自动注册是用vv.evtMgr.on,所以只有vv.evtMgr发送的才能触发
    vv.evtMgr.emit('func_test', '参数1', '参数2');
}
// 自动注册为事件,事件名跟函数名相同
func_test(arg1, arg2) {
    console.log('接受到事件', arg1, arg2); // 接受到事件 参数1 参数2
}
// 这种不会注册为事件 funcTest2_ 这种也不会
// _funcTest2() {}
​
// 按钮点击事件对应 click + 按钮名(带下划线的会转大写)
clickBtnTest() {
    console.log('_btn_test 被点击了');
}

七、其他

vv.resLoader 对资源加载进行了简单的封装

vv.evtMgr 对自定义事件的简单实现

八、结束

本菜也是第一写框架,应该说只是一些View管理,考虑不周和实现方式不优雅的地方希望大佬能指导指导,也希望本文能帮助到一些新同学,也算为社区贡献一丝绵薄之力。有想法或者问题的同学也可以找我一起讨论,感谢大家浏览。

1620843575394

6赞

大道至简,实用至上

我有个小问题,如果单场景中,大厅和游戏两个节点,进入游戏节点时,大厅节点需要隐藏吗?还有直接在大厅节点上挂载游戏节点,那么从游戏切换到大厅的时候,游戏节点需要隐藏吗?我也是单场景,不知道大佬们是怎么处理的,求教

虽然没做过大厅+子游戏模式,但感觉这里的大厅跟游戏应该是属于两个同级的View,我的想法是游戏跟大厅的父节点保持一致,一个显示的时候另一个隐藏,如果没用到的必要可以移除,需要的时候再加载显示