麒麟子惯用框架分享(建议收藏)
零
前言
麒麟子在开发中搞出来的框架,都是遵守“大道至简,实用至上”这两个基本原则。
接触一个引擎的第一件事,就是搞出一个实用的框架,方便在此基础上做开发。
由于目前的引擎已经是对象+组件模式,所以在场景对象管理上,不需要花太多功夫了。我们主要集中在界面管理这块。
1
常见的几种游戏类型
既然我们的框架想要满足日常开发,就不得不满足星辰大海般的需求。从客户端的角度,我们可以把游戏分为三类。
1.1、纯界面玩法
像一些SLG、卡牌、象棋等可以视作纯界面玩法。(也有某些大作要求3D表现效果的,我们不作讨论),比如下面的这类游戏。
动物餐厅
1.2、某些纯界面&战场场景
王者荣耀
1.3、从头到尾都是3D场景
魔兽世界
1.4、小结
仔细分析以后我们可以发现,假如我们的框架始终支持 2D/3D场景 + 界面 这样的能力。就可以了。
2
单场景 vs 多场景
单场景+Prefab 和 多场景从 Cocos 2d-x 提供了 replaceScene 函数开始,就一直有人在争论这个话题。只要引擎提供了“场景”这个定义的,都会遇上决策问题。
麒麟子看得很明白,所谓的引擎场景管理,就是给了你一次无脑销毁所有结点的机会。
如果你是在两个截然不同的逻辑之间切换,那其实是可以使用引擎的场景切换功能的。 如果逻辑相差不大,我建议依然采用单场景方式。
而麒麟子更推荐的是单场景+Prefab方式,这样会迫使你自己更注重场景节点和资源的管理。
3
框架内容
3.1、程序启动入口
程序启动入口包含两个部分。
3.1.1、最小场景
最小场景如果不出意外,我们只需要一个Canvas节点,一个MainCamera节点,一个MainLight节点就好了。
3.1.2、App.ts
麒麟子喜欢以 App.ts 作为程序入口,将这个App.ts挂在Canvas节点上即可。App.ts的内容并不多,如下所示
import { _decorator, Component, Node } from 'cc';
import { UIMgr, UILayer } from './UIMgr';
import { HUD } from './HUD';
const { ccclass, property } = _decorator;
@ccclass('AppTs')
export class AppTs extends Component {
start () {
UIMgr.inst.setup(UILayer.NUM);
UIMgr.inst.showUI(HUD);
}
}
我们可以清晰地看到,在这里,麒麟子只启动了 UIMgr,如果还有其他游戏管理器需要一开始就初始化,那么把它们放在这里就好了。
3.2、界面管理器
- 界面管理器至少要包含几个功能
- 动态加载和销毁界面
- 界面层级管理
- 界面事件自动管理机制
- 界面与游戏业务逻辑通信机制
上面说的这些,框架里都自带了。
4
分辨率自适应
很多小伙伴一定还在纠结是用 fitWidth 还是 fitHeight 吧。
如果使用 fitWidth 遇上更细长的设备,界面上面部分可能被裁剪。
如果使用 fitHeight 遇上更短的设备,界面左右部分可能被裁剪。
其实我们想要的只有一句话:任何时候,都不要裁剪。
基于这个目标,我们制定出的策略就是。
- 在比设计分辨率更细长的设备上,我们使用 fitHeight,这样一来,他长由他长,我们只需要保证背景左右能够填充就行。 在比设计分辨率更短的设备上,我们使用 fitWidth这样一来,他高由他高,我们只需要保证背景上下能够填充就行。上面的适配机制,已经被麒麟子写成了一个函数。 public resize() { //根据屏幕大小决定适配策略 let dr = view.getDesignResolutionSize(); var s = cc.view.getFrameSize(); var rw = s.width; var rh = s.height; var finalW = rw; var finalH = rh; if ((rw / rh) > (dr.width / dr.height)) { //!#zh: 是否优先将设计分辨率高度撑满视图高度。 //cvs.fitHeight = true; //如果更长,则用定高 finalH = dr.height; finalW = finalH * rw / rh; } else { /*!#zh: 是否优先将设计分辨率宽度撑满视图宽度。*/ //cvs.fitWidth = true; //如果更短,则用定宽 finalW = dr.width; finalH = rh / rw * finalW; } view.setDesignResolutionSize(finalW, finalH, ResolutionPolicy.UNKNOWN); let cvs = find('Canvas').getComponent(UITransformComponent); cvs.node.width = finalW; cvs.node.height = finalH; }
关于这个函数,有两个地方要注意:
- 在设置面板里面,fitWidth和fitHeight都得去掉,一个都不要勾,否则可能出现设置失效。
- 麒麟子在调用 view.setDesignResolutionSize 函数的时候,最后一个参数传的是ResolutionPolicy.UNKNOWN。
这样一来,这个函数是可以重复调用且生效的。当我们处于微信浏览器的时候,横竖屏旋转会导致宽高比不一致,这就需要再次调用这个函数来重新调整布局。
5
游戏逻辑与界面通信机制
麒麟子这里没有MVC,也没有MVVM,只有以下几个套路
- 数据、数据更新方法由逻辑管理器提供
- 数据变更,想让界面产生改变,则通过事件传递
- 界面可以直接调用逻辑管理器
- 两个界面之间,只能通过事件传递(需要两个界面联动的情况非常少,一般情况下界面之间的逻辑都是互不干扰的)
也就是说,逻辑管理器不知道界面的存在,但界面是知道逻辑管理器的存在的。
我举一个关于个人信息的例子。在这个例子中,我们会涉及到三个文件 MyInfoMgr.ts(用户信息逻辑管理器 M) 、UIMyInfoController.ts(用户信息界面控制器 C) 、MyInfo.prefab(用户信息界面布局 V)。
如果非要用MVC来对应的话,就按我后面标记的字母来对应吧。
当用户点击个人按钮时,UIMgr会实例化UIMyInfoController类,UIMyInfoController类会加载MyInfo.prefab。
当加载成功后,UIMyInfoController会有一个onCreated回调,我们可以在回调里初始化我们的显示信息。
UIMyInfoController需要监听用户信息改变相关的事件,当收到事件的时候,需要对应地做显示更新
MyInfoMgr持有用户数据并提供访问接口以及数据修改接口,当数据产生修改时,会抛出修改事件。它不关心这个事件是否有人要用。无脑抛出就行。
6
总结
与其说是框架,不如说是麒麟子的惯用套路。这个套路没有出色的地方,也不满足很多学术性的依赖解耦标准。但这个套路陪着我走过了大大小小很多项目。
如果要给它下一个定义的话,我觉得就是两个词:“简单、实用”。
哦对了,只能用Cocos Creator 3D 1.1.1打开。
源码地址:
https://gitee.com/qilinzi/creator3ddemos
- ASP.NET vNext 概述
- 丰富排版页面——为你的wordpress主题添加短代码形式美化框
- 开放式管理基础结构 OMI
- 人类设计了游戏和AI 2017年AI在游戏中打败了人类
- WordPress 代码实现相关文章(列表模式)功能
- 自动刷新页面
- Python语言被列入全国计算机等级考试科目中
- WordPress纯代码高仿 无觅相关文章 图文模式功能
- 各种序列化库的性能数据
- WordPress内置搜索结果只有一篇文章时自动跳转到该文章
- Flash/Flex学习笔记(23):运动学原理
- WordPress重定向作者归档链接到“关于”页面
- Flash/Flex学习笔记(25):摩擦力与屏幕环绕
- 搞事情!富士通和微软强强联手用AI加速工作方式转变
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法