【转】发布一个基于NGUI编写的UI框架
发布一个基于NGUI编写的UI框架
1.加载,显示,隐藏,关闭页面,根据标示获得相应界面实例
2.提供界面显示隐藏动画接口
3.单独界面层级,Collider,背景管理
4.根据存储的导航信息完成界面导航
5.界面通用对话框管理(多类型Message Box)
6.便于进行需求和功能扩展(比如,在跳出页面之前添加逻辑处理等)
目标:编写一个简单通用UI框架用于管理页面和完成导航跳转
最终的实现效果和Demo请拉到最下方回复查看
框架具体实现的功能和需求
- 加载,显示,隐藏,关闭页面,根据标示获得相应界面实例
- 提供界面显示隐藏动画接口
- 单独界面层级,Collider,背景管理
- 根据存储的导航信息完成界面导航
- 界面通用对话框管理(多类型Message Box)
- 便于进行需求和功能扩展(比如,在跳出页面之前添加逻辑处理等)
编写UI框架意义
- 打开,关闭,层级,页面跳转等管理问题集中化,将外部切换等逻辑交给UIManager处理
- 功能逻辑分散化,每个页面维护自身逻辑,依托于框架便于多人协同开发,不用关心跳转和显示关闭细节
- 通用性框架能够做到简单的代码复用和"项目经验"沉淀
步入正题,如何实现
- 窗口类设计:基本窗口对象,维护自身逻辑维护
- 窗口管理类:控制被管理窗口的打开和关闭等逻辑(具体设计请看下文)
- 动画接口:提供打开和关闭动画接口,提供动画完成回调函数等
- 层级,Collider背景管理
窗口基类设计
框架中设计的窗口类型和框架所需定义如下
public enum UIWindowType
{
Normal, // 可推出界面(UIMainMenu,UIRank等)
Fixed, // 固定窗口(UITopBar等)
PopUp, // 模式窗口
} public enum UIWindowShowMode
{
DoNothing,
HideOther, // 闭其他界面
NeedBack, // 点击返回按钮关闭当前,不关闭其他界面(需要调整好层级关系)
NoNeedBack, // 关闭TopBar,关闭其他界面,不加入backSequence队列
} public enum UIWindowColliderMode
{
None, // 显示该界面不包含碰撞背景
Normal, // 碰撞透明背景
WithBg, // 碰撞非透明背景
}
using UnityEngine;
using System.Collections;
using System; namespace CoolGame
{
/// <summary>
/// 窗口基类
/// </summary>
public class UIBaseWindow : MonoBehaviour
{
protected UIPanel originPanel; // 如果需要可以添加一个BoxCollider屏蔽事件
private bool isLock = false;
protected bool isShown = false; // 当前界面ID
protected WindowID windowID = WindowID.WindowID_Invaild; // 指向上一级界面ID(BackSequence无内容,返回上一级)
protected WindowID preWindowID = WindowID.WindowID_Invaild;
public WindowData windowData = new WindowData(); // Return处理逻辑
private event BoolDelegate returnPreLogic = null; protected Transform mTrs;
protected virtual void Awake()
{
this.gameObject.SetActive(true);
mTrs = this.gameObject.transform;
InitWindowOnAwake();
} private int minDepth = 1;
public int MinDepth
{
get { return minDepth; }
set { minDepth = value; }
} /// <summary>
/// 能否添加到导航数据中
/// </summary>
public bool CanAddedToBackSeq
{
get
{
if (this.windowData.windowType == UIWindowType.PopUp)
return false;
if (this.windowData.windowType == UIWindowType.Fixed)
return false;
if (this.windowData.showMode == UIWindowShowMode.NoNeedBack)
return false;
return true;
}
} /// <summary>
/// 界面是否要刷新BackSequence数据
/// 1.显示NoNeedBack或者从NoNeedBack显示新界面 不更新BackSequenceData(隐藏自身即可)
/// 2.HideOther
/// 3.NeedBack
/// </summary>
public bool RefreshBackSeqData
{
get
{
if (this.windowData.showMode == UIWindowShowMode.HideOther
|| this.windowData.showMode == UIWindowShowMode.NeedBack)
return true;
return false;
}
} /// <summary>
/// 在Awake中调用,初始化界面(给界面元素赋值操作)
/// </summary>
public virtual void InitWindowOnAwake()
{
} /// <summary>
/// 获得该窗口管理类
/// </summary>
public UIManagerBase GetWindowManager
{
get
{
UIManagerBase baseManager = this.gameObject.GetComponent<UIManagerBase>();
return baseManager;
}
private set { }
} /// <summary>
/// 重置窗口
/// </summary>
public virtual void ResetWindow()
{
} /// <summary>
/// 初始化窗口数据
/// </summary>
public virtual void InitWindowData()
{
if (windowData == null)
windowData = new WindowData();
} public virtual void ShowWindow()
{
isShown = true;
NGUITools.SetActive(this.gameObject, true);
} public virtual void HideWindow(Action action = null)
{
IsLock = true;
isShown = false;
NGUITools.SetActive(this.gameObject, false);
if (action != null)
action();
} public void HideWindowDirectly()
{
IsLock = true;
isShown = false;
NGUITools.SetActive(this.gameObject, false);
} public virtual void DestroyWindow()
{
BeforeDestroyWindow();
GameObject.Destroy(this.gameObject);
} protected virtual void BeforeDestroyWindow()
{
} /// <summary>
/// 界面在退出或者用户点击返回之前都可以注册执行逻辑
/// </summary>
protected void RegisterReturnLogic(BoolDelegate newLogic)
{
returnPreLogic = newLogic;
} public bool ExecuteReturnLogic()
{
if (returnPreLogic == null)
return false;
else
return returnPreLogic();
}
}
}
动画接口设计
界面可以继承该接口进行实现打开和关闭动画
/// <summary>
/// 窗口动画
/// </summary>
interface IWindowAnimation
{
/// <summary>
/// 显示动画
/// </summary>
void EnterAnimation(EventDelegate.Callback onComplete); /// <summary>
/// 隐藏动画
/// </summary>
void QuitAnimation(EventDelegate.Callback onComplete); /// <summary>
/// 重置动画
/// </summary>
void ResetAnimation();
}
public void EnterAnimation(EventDelegate.Callback onComplete)
{
if (twAlpha != null)
{
twAlpha.PlayForward();
EventDelegate.Set(twAlpha.onFinished, onComplete);
}
} public void QuitAnimation(EventDelegate.Callback onComplete)
{
if (twAlpha != null)
{
twAlpha.PlayReverse();
EventDelegate.Set(twAlpha.onFinished, onComplete);
}
} public override void ResetWindow()
{
base.ResetWindow();
ResetAnimation();
}
窗口管理和导航设计实现
导航功能实现通过一个显示窗口堆栈实现,每次打开和关闭窗口通过判断窗口属性和类型更新处理BackSequence数据
- 打开界面:将当前界面状态压入堆栈中更新BackSequence数据
- 返回操作(主动关闭当前界面或者点击返回按钮):从堆栈中Pop出一个界面状态,将相应的界面重新打开
- 怎么衔接:比如从一个界面没有回到上一个状态而是直接的跳转到其他的界面,这个时候需要将BackSequence清空因为当前的导航链已经被破坏,当BackSequence为空需要根据当前窗口指定的PreWindowId告知系统当从该界面返回,需要到达的指定页面,这样就能解决怎么衔接的问题,如果没断,继续执行导航,否则清空数据,根据PreWindowId进行导航
导航系统中关键性设计:
游戏中可以存在多个的Manager进行管理(一般在很少需求下才会使用),每个管理对象需要维护自己的导航信息BackSequence,每次退出一个界面需要检测当前退出的界面是否存在相应的Manager管理,如果存在则需要先执行Manager退出操作(退出过程分步进行)保证界面一层接着一层正确退出
窗口层级,Collider,统一背景添加如何实现?
有很多方式进行层级管理,该框架选择的方法如下
- 设置三个常用层级Root,根据窗口类型在加载到游戏中时添加到对应的层级Root下面即可,每次添加重新计算设置层级(通过UIPanel的depth实现)保证每次打开一个新窗口层级显示正确,每次窗口内通过depth的大小区分层级关系
- 根据窗口Collider和背景类型,在窗口的最小Panel上面添加Collider或者带有碰撞体的BackGround即可
具体实现如下:
private void AdjustBaseWindowDepth(UIBaseWindow baseWindow)
{
UIWindowType windowType = baseWindow.windowData.windowType;
int needDepth = 1;
if (windowType == UIWindowType.Normal)
{
needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UINormalWindowRoot.gameObject, false) + 1, normalWindowDepth, int.MaxValue);
Debug.Log("[UIWindowType.Normal] maxDepth is " + needDepth + baseWindow.GetID);
}
else if (windowType == UIWindowType.PopUp)
{
needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UIPopUpWindowRoot.gameObject) + 1, popUpWindowDepth, int.MaxValue);
Debug.Log("[UIWindowType.PopUp] maxDepth is " + needDepth);
}
else if (windowType == UIWindowType.Fixed)
{
needDepth = Mathf.Clamp(GameUtility.GetMaxTargetDepth(UIFixedWidowRoot.gameObject) + 1, fixedWindowDepth, int.MaxValue);
Debug.Log("[UIWindowType.Fixed] max depth is " + needDepth);
}
if(baseWindow.MinDepth != needDepth)
GameUtility.SetTargetMinPanel(baseWindow.gameObject, needDepth);
baseWindow.MinDepth = needDepth;
} /// <summary>
/// 窗口背景碰撞体处理
/// </summary>
private void AddColliderBgForWindow(UIBaseWindow baseWindow)
{
UIWindowColliderMode colliderMode = baseWindow.windowData.colliderMode;
if (colliderMode == UIWindowColliderMode.None)
return; if (colliderMode == UIWindowColliderMode.Normal)
GameUtility.AddColliderBgToTarget(baseWindow.gameObject, "Mask02", maskAtlas, true);
if (colliderMode == UIWindowColliderMode.WithBg)
GameUtility.AddColliderBgToTarget(baseWindow.gameObject, "Mask02", maskAtlas, false);
}
多形态MessageBox实现
这个应该是项目中一定会用到的功能,说下该框架简单的实现
- 三个按钮三种回调逻辑:左中右三个按钮,提供设置内容,设置回调函数的接口即可
- 提供接口设置核心Content
- 不同作用下不同的按钮不会隐藏和显示
public void SetCenterBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
{
lbCenter.text = msg;
NGUITools.SetActive(btnCenter, true);
UIEventListener.Get(btnCenter).onClick = callBack;
} public void SetLeftBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
{
lbLeft.text = msg;
NGUITools.SetActive(btnLeft, true);
UIEventListener.Get(btnLeft).onClick = callBack;
} public void SetRightBtnCallBack(string msg, UIEventListener.VoidDelegate callBack)
{
lbRight.text = msg;
NGUITools.SetActive(btnRight, true);
UIEventListener.Get(btnRight).onClick = callBack;
}
后续需要改进和增强计划
- 图集管理,针对大中型游戏对游戏内存要求苛刻的项目,一般都会对UI图集贴图资源进行动态管理,加载和卸载图集,保证UI贴图占用较少内存
- 增加一些通用处理:变灰操作,Mask遮罩(一般用于新手教程中)等
- 在进行切换的过程可以需要Load新场景需求,虽然这个也可以在UI框架外实现
- 对话系统也算是UI框架的功能,新手引导系统也可以加入到UI框架中,统一管理和处理新手引导逻辑
需求总是驱动着系统逐渐强大,逐渐完善,逐渐发展,一步一步来吧~
实现效果
整个框架的核心部分介绍完毕,有需要的朋友感兴趣的朋友可以下载参考下,希望能够给耐心看到结尾的朋友一点启发或者带来一点帮助,存在错误和改进的地方也希望留言交流共同进步学习~有些时候,我们总是知道这么个理明白该怎样实现,但是关键的就是要动手实现出来,实现的过程会发现自己的想法在慢慢优化,不断的需求和bug的产生让框架慢慢成熟,可以投入项目使用提升一些开发效率和减少工作量。
希望能够帮助到大家~
UI框架介绍
GitHub地址:https://github.com/tinyantstudio/UIFrameWork
持续更新,新的想法,针对新的需求框架进行扩展的优化~希望感兴趣的朋友一起针对游戏开发一起交流和学习~issues~
!有兴趣的朋友请直接移步Github,本帖子已经不做更新,框架的具体的实现已经做了优化和代码整理,本文只介绍了具体的设计思路!
【转】发布一个基于NGUI编写的UI框架的更多相关文章
- artDialog是一个基于javascript编写的对话框组件,它拥有精致的界面与友好的接口
artDialog是一个基于javascript编写的对话框组件,它拥有精致的界面与友好的接口 自适应内容 artDialog的特殊UI框架能够适应内容变化,甚至连外部程序动态插入的内容它仍然能自适应 ...
- 新建一个基于vue.js+Mint UI的项目
上篇文章里面讲到如何新建一个基于vue,js的项目(详细文章请戳用Vue创建一个新的项目). 该项目如果需要组件等都需要自己去写,今天就学习一下如何新建一个基于vue.js+Mint UI的项目,直接 ...
- 基于jquery开发的UI框架整理分析
根据调查得知,现在市场中的UI框架差不多40个左右,不知大家都习惯性的用哪个框架,现在市场中有几款UI框架稍微的成熟一些,也是大家比较喜欢的一种UI框架,那应该是jQuery,有部分UI框架都是根据j ...
- 优秀的基于VUE移动端UI框架合集
1. vonic 一个基于 vue.js 和 ionic 样式的 UI 框架,用于快速构建移动端单页应用,很简约,是我喜欢的风格 star 2.3k 中文文档 在线预览 2.vux 基于WeUI和Vu ...
- 发布一个基于协程和事件循环的c++网络库
目录 介绍 使用 性能 实现 日志库 协程 协程调度 定时器 Hook RPC实现 项目地址:https://github.com/gatsbyd/melon 介绍 开发服务端程序的一个基本任务是处理 ...
- Gitlab + Jenkins 构建,发布一个基于Go的Gin测试项目
部署Go项目简介 对于golang的发布,之前一直没有一套规范的发布流程,来看看之前发布流程: 方案一 • 开发者本地环境需要将环境变量文件改为正式环境配置 • 编译成可执行文件 • 发送给运维 • ...
- 淘宝、天猫又开源了一个动态化、高性能的UI框架
前言 淘宝.天猫一直致力于解决 页面动态化的问题 在2017年的4月发布了v1.0解决方案:Tangram模型 及其对应的 Android库 vlayout,该解决方案在手机淘宝.天猫 Android ...
- ShutIt:一个基于 Python 的 shell 自动化框架
ShutIt是一个易于使用的基于shell的自动化框架.它对基于python的expect库(pexpect)进行了包装.你可以把它看作是“没有痛点的expect”.它可以通过pip进行安装. Hel ...
- Nest.js 6.0.0 正式版发布,基于 TypeScript 的 Node.js 框架
开发四年只会写业务代码,分布式高并发都不会还做程序员? Nest.js 6.0.0 正式版发布了.Nest 是构建高效.可扩展的 Node.js Web 应用程序的框架.它使用现代的 JavaSc ...
随机推荐
- SPFA(负环) LightOJ 1074 Extended Traffic
题目传送门 题意:收过路费.如果最后的收费小于3或不能达到,输出'?'.否则输出到n点最小的过路费 分析:关键权值可为负,如果碰到负环是,小于3的约束条件不够,那么在得知有负环时,把这个环的点都标记下 ...
- Java 16进制、unicode互转
package service; import java.util.regex.Matcher; import java.util.regex.Pattern; public class CodeCh ...
- 每天一个linux命令--locate
linux下,不知道自己安装的程序放在哪里了,可以使用locate命令进行查找. [hongye@dev107 ~]$ locate activemq.xml /home/hongye/hongyeC ...
- [笔记] Duke - 统计预测
Duke大学富卡商学院(Fuqua school of business)的高级选修课. 全名:Statistical forecasting: notes on regression and tim ...
- IEqualityComparer<T>
在linq中使用union和distinct都不起作用,结果发现必须传入一个实现了IEqualityComparer<T>的比较器 public class CompareUser : I ...
- HBase1.0以上版本的API改变
HBase1.0以上版本已经废弃了 HTableInterface,HTable,HBaseAdmin等API的使用,新增了一些API来实现之前的功能: Connectioninterface: Co ...
- 用js读写cookie的简单办法
/* 功能:保存cookies函数 参数:name,cookie名字:value,值 */ function SetCookie(name,value){ var Days = 30*12; //co ...
- 【BUG】wego购物分享系统未登陆分享宝贝时查看宝贝自动新增产品数据
1.登录微购http://demo.wego360.com/站. 2.分享宝贝功能检索第三方平台商品数据. 1.登录微购http://demo.wego360.com/站. 2.分享宝贝功能检索第三方 ...
- 浅谈iOS中的userAgent
浅谈iOS中的userAgent User-Agent(用户代理)字符串是Web浏览器用于声明自身型号版本并随HTTP请求发送给Web服务器的字符串,在Web服务器上可以获取到该字符串. 在公司产 ...
- Struts2中上传图片案列
1.HTML代码 <body> <!--上传一个文件 enctype="multipart/form-data" 上传文件必须设置这个属性和属性值--> ...