最新:
参考公司lua的mvvm写了一个c#版的,wang-er-s/Framework: a unity mvvm framework, building... (github.com),欢迎提建议
后续会新开一篇文章补充开发思路
------------------------------弃用---------------------------------
之前学了刘国柱老师的UI框架加上我自己的理解做了一个UI框架正好可以使用
这里附上刘国柱老师的博客 http://www.cnblogs.com/LiuGuozhu/ 受益颇深
基本UI框架的类图就是这样了
大体是根据MVC加上我的魔改而成的框架,至少我自己暂时觉得还是思路挺清晰的
Panel的切换是使用的Canvas Group的更改透明度,在加上使用
此方法来更改显示顺序和显隐的
其中的界面反向切换是使用的栈后入先出的特性
反向切换是指一个窗口弹出小窗口后,父窗口的Raycast需要取消,下面贴上核心代码
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIManager :MonoBehaviour {
/// <summary>
/// 单例模式:
/// 1.定义一个静态的对象 在外界访问 内部构造
/// 2.构造方法私有化
/// </summary>
#region 单例模式
private static UIManager _instance;
public static UIManager Instance
{
get
{
if (_instance == null)
{
UIManager[] instances = FindObjectsOfType<UIManager>();
if (instances != null)
{
_instance = instances[0];
for (var i = 1; i < instances.Length; i++)
{
Destroy(instances[i].gameObject);
}
}
}
return _instance;
}
}
#endregion
private Transform _canvas;
public Transform Canvas
{
get
{
if (_canvas == null)
_canvas = GameObject.Find("Canvas").transform;
return _canvas;
}
}
//存储所有面板prefab的地址
private Dictionary<UIPanelType, string> _DicPanelPath;
//存储所有实例化的面板身上的BasePanel组件
private Dictionary<UIPanelType, BasePanel> _DicPanel;
//存储所有显示的面板上的BasePanel组件
private Dictionary<UIPanelType, BasePanel> _DicShowPanel;
//表示显示面板的栈
private Stack<BasePanel> _StackPanel;
//全屏显示的节点
private Transform _TraFullScreen = null;
//固定显示的节点
private Transform _TraFixed = null;
//弹出节点
private Transform _TraPopUp = null;
脚本节点
//private Transform _TraScript = null;
private void Awake()
{
ParseUIPanelPathJson();
_StackPanel = new Stack<BasePanel>();
_DicPanel = new Dictionary<UIPanelType, BasePanel>();
_DicShowPanel = new Dictionary<UIPanelType, BasePanel>();
_TraFullScreen = Canvas.Find("FullScreen");
_TraFixed = Canvas.Find("Fixed");
_TraPopUp = Canvas.Find("PopUp");
DontDestroyOnLoad(Canvas.gameObject);
//_TraScript = Canvas.Find("Script");
}
/// <summary>
/// 把类型为反向切换的窗体入栈
/// </summary>
/// <param name="type"></param>
private void PushPanel(UIPanelType type)
{
BasePanel basePanel = GetPanel(type);
if (_StackPanel.Count >= 1)
{
BasePanel topPanel = _StackPanel.Peek();
topPanel.OnPause();
}
_StackPanel.Push(basePanel);
basePanel.OnEnter();
}
/// <summary>
/// 把类型为反向切换的窗体出栈
/// </summary>
/// <param name="type"></param>
private void PopPanel(UIPanelType type)
{
//如果栈里面为空,则返回
if (_StackPanel.Count <= 0) return;
BasePanel basePanel = GetPanel(type);
BasePanel topPanel = _StackPanel.Peek();
//如果当前要出栈的界面跟栈顶界面相同时 才会出栈
if(basePanel.gameObject == topPanel.gameObject)
{
_StackPanel.Pop();
topPanel.OnExit();
//如果出栈后栈里面还有界面则恢复
if(_StackPanel.Count >= 1)
{
_StackPanel.Peek().OnResume();
}
}
else
{
Debug.LogError("栈顶界面不是此界面,请检查进出栈!!");
}
}
/// <summary>
/// 显示界面
/// </summary>
/// <param name="type"></param>
public void ShowUIForms(UIPanelType type)
{
BasePanel basePanel = GetPanel(type);
switch (basePanel.uiType.uiFormShowMode)
{
case UIFormShowMode.Normal:
EnterUIFormsCache(type);
break;
//如果是反切换类型则入栈
case UIFormShowMode.ReverseChange:
PushPanel(type);
break;
//如果是隐藏其他的类型,清空显示的界面
case UIFormShowMode.HideOther:
HideOtherPanel(type);
break;
}
}
/// <summary>
/// 隐藏界面 如果显示界面字典里面存在此界面
/// </summary>
/// <param name="type"></param>
public void HideUIForms(UIPanelType type)
{
BasePanel basePanel = GetPanel(type);
switch (basePanel.uiType.uiFormShowMode)
{
case UIFormShowMode.Normal:
HideUIFormsCache(type);
break;
//如果是反切换类型则出栈
case UIFormShowMode.ReverseChange:
PopPanel(type);
break;
case UIFormShowMode.HideOther:
HideUIFormsCache(type);
break;
}
}
/// <summary>
/// 隐藏除此之外的Panel,如果不赋值则清空所有
/// </summary>
/// <param name="type"></param>
public void HideOtherPanel(UIPanelType type = UIPanelType.Null)
{
foreach (KeyValuePair<UIPanelType,BasePanel> item in _DicShowPanel)
{
item.Value.OnExit();
}
_DicShowPanel.Clear();
if (type != UIPanelType.Null)
{
BasePanel basePanel = GetPanel(type);
basePanel.OnEnter();
_DicShowPanel.Add(type, GetPanel(type));
}
}
/// <summary>
/// 将UI加入到已显示面板的字典中
/// </summary>
/// <param name="type"></param>
private void EnterUIFormsCache(UIPanelType type)
{
//如果显示面板字典里面有当前面板则返回
if (_DicShowPanel.ContainsKey(type)) return;
BasePanel basePanel = GetPanel(type);
_DicShowPanel.Add(type, basePanel);
basePanel.OnEnter();
}
/// <summary>
/// 将UI隐藏
/// </summary>
/// <param name="type"></param>
private void HideUIFormsCache(UIPanelType type)
{
//如果显示面板字典里面没有当前面板则返回
if (!_DicShowPanel.ContainsKey(type)) return;
BasePanel basePanel = GetPanel(type);
_DicShowPanel.Remove(type);
basePanel.OnExit();
}
/// <summary>
/// 根据面板类型得到实例化的面板
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private BasePanel GetPanel(UIPanelType type)
{
if (_DicPanel.ContainsKey(type))
{
return _DicPanel[type];
}
else
{
//如果找不到就找这个面板的prefab的路径,然后根据prefab去实例化面板
BasePanel panel;
string panelPath = _DicPanelPath.GetValue(type);
GameObject insGo = GameObject.Instantiate(Resources.Load(panelPath),Canvas) as GameObject;
insGo.name = type.ToString();
panel = insGo.GetComponent<BasePanel>();
switch (panel.uiType.uIFormParentType)
{
case UIFormParentType.FUllScreen:
insGo.transform.SetParent(_TraFullScreen);
break;
case UIFormParentType.Fixed:
insGo.transform.SetParent(_TraFixed);
break;
case UIFormParentType.PopUp:
insGo.transform.SetParent(_TraPopUp);
break;
}
_DicPanel.Add(type, panel);
return panel;
}
}
#region 解析地址json 并存储到字典中
private void ParseUIPanelPathJson()
{
TextAsset text = Resources.Load<TextAsset>("UIPanelType");
JSONObject jSONObject = new JSONObject(text.text);
if (_DicPanelPath == null)
_DicPanelPath = new Dictionary<UIPanelType, string>();
foreach (JSONObject item in jSONObject.list)
{
string panelTypeString = item["panelTypeString"].str;
string path = item["path"].str;
UIPanelType uIPanelType = (UIPanelType)System.Enum.Parse(typeof(UIPanelType), panelTypeString);
_DicPanelPath.Add(uIPanelType, path);
}
}
#endregion
}
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BasePanel : MonoBehaviour {
protected CanvasGroup cg;
public UIType uiType;
private void Awake()
{
InitData();
}
public virtual void OnCloseBtnClick()
{
UIManager.Instance.HideUIForms(uiType.uiPanelType);
}
public virtual void InitData()
{
if (cg == null)
cg = this.gameObject.GetAndAddComponent<CanvasGroup>();
if (uiType == null)
uiType = new UIType();
}
/// <summary>
/// 界面被显示出来
/// </summary>
public virtual void OnEnter()
{
cg.alpha = 1;
cg.blocksRaycasts = true;
this.gameObject.transform.SetAsLastSibling();
}
/// <summary>
/// 界面暂停 指的是当其他界面在此界面之上,此界面不是主界面的时候,此界面不接受鼠标检测,如果不需要此功能可以适当更改
/// </summary>
public virtual void OnPause()
{
cg.blocksRaycasts = false;
}
/// <summary>
/// 界面继续
/// </summary>
public virtual void OnResume()
{
cg.blocksRaycasts = true;
}
/// <summary>
/// 界面不显示,退出这个界面
/// </summary>
public virtual void OnExit()
{
cg.alpha = 0;
cg.blocksRaycasts = false;
}
}
using System.Collections;
using System;
using System.Collections.Generic;
using UnityEngine;
public class UIType {
/// <summary>
/// panel类别
/// </summary>
public UIPanelType uiPanelType;
/// <summary>
/// Panel的显示方式,有正常显示,隐藏其他和反向切换
/// </summary>
public UIFormShowMode uiFormShowMode = UIFormShowMode.Normal;
/// <summary>
/// 这个透明度暂时没用
/// </summary>
public UIFormLucencyType uIFormLucencyType = UIFormLucencyType.Lucency;
/// <summary>
/// Panel的父物体类型
/// </summary>
public UIFormParentType uIFormParentType = UIFormParentType.PopUp;
代码比较多,不过都有贴心的注释
-------------------------------加了一个代码自动生成的工具-----------------------------------
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading.Tasks;
using System;
using UnityEditor;
using System.IO;
using System.Text;
using UnityEngine.UI;
public class UIElement
{
public string Name;
public string Path;
public string ComponentName;
public UIElement(string name,string path,string componentName)
{
Name = name;
Path = path;
ComponentName = componentName;
}
public override string ToString()
{
string str = string.Format("Name={0} || Path={1} || ComponentName={2}", Name, Path, ComponentName);
return str;
}
}
public class UICodeGenerator
{
[MenuItem ( "Assets/CreateCodeDeleteComponent" )]
public static void CreateCodeDeleteComponent ()
{
GetPath ( true );
}
[MenuItem ( "Assets/OnlyCreateCode" )]
public static void OnlyCreateCode ()
{
GetPath ( false );
}
public static void GetPath (bool isDeleteComponent)
{
var objs =
Selection.GetFiltered ( typeof ( GameObject ), SelectionMode.Assets | SelectionMode.TopLevel );
GameObject obj = objs[ 0 ] as GameObject;
elements = new List<UIElement> ();
GetPathAs ( obj.transform, isDeleteComponent );
foreach ( var item in elements )
{
Debug.Log ( item );
}
GeneratePane ( "Assets/" + obj.name + ".cs", obj.name, elements );
GenerateCtrl ( "Assets/" + obj.name + "Ctrl.cs", obj.name, elements );
}
public static List<UIElement> elements;
static void GetPathAs ( Transform transform ,bool isDeleteComponent)
{
foreach ( Transform child in transform )
{
if ( child.gameObject.GetComponent<UIMark> () )
{
elements.Add ( new UIElement ( child.name, GetPath ( child ),
child.gameObject.GetComponent<UIMark> ().ComponentName ) );
if ( isDeleteComponent )
GameObject.DestroyImmediate ( child.gameObject.GetComponent<UIMark> (), true );
}
if ( child.childCount != 0 )
{
GetPathAs ( child, isDeleteComponent );
}
}
}
public static void GeneratePane ( string generateFilePath, string behaviourName, List<UIElement> elements )
{
var sw = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
var strBuilder = new StringBuilder ();
strBuilder.AppendLine ( "using UnityEngine;" );
strBuilder.AppendLine ( "using UnityEngine.UI;" );
strBuilder.AppendLine ();
strBuilder.AppendFormat ( "public class {0} : BasePanel ", behaviourName );
strBuilder.AppendLine ();
strBuilder.AppendLine ( "{" );
foreach ( var item in elements )
{
strBuilder.AppendLine ( "\tpublic " + item.ComponentName + " " + item.Name + " { get; private set; }" );
}
strBuilder.AppendLine ();
strBuilder.AppendLine ( "\tpublic override void InitData()" );
strBuilder.AppendLine ( "\t{" );
foreach ( var item in elements )
{
strBuilder.AppendFormat ( "\t\t{0} = transform.Find(\"{1}\").GetComponent<{2}>();", item.Name,
item.Path.Replace ( behaviourName + "/", "" ), item.ComponentName );
strBuilder.AppendLine ();
}
strBuilder.AppendLine ();
strBuilder.AppendLine ( "\t\tuiType.uIFormParentType = UIFormParentType.PopUp;" );
strBuilder.AppendLine ( "\t\tuiType.uiFormShowMode = UIFormShowMode.Normal;" );
strBuilder.AppendLine ( "\t\tuiType.uiPanelType = UIPanelType.BoxPanel;" );
strBuilder.AppendLine ( "\t}" );
strBuilder.AppendLine ( "}" );
sw.Write ( strBuilder );
sw.Flush ();
sw.Close ();
AssetDatabase.SaveAssets ();
AssetDatabase.Refresh ();
}
public static void GenerateCtrl ( string generateFilePath, string behaviourName , List<UIElement> elements)
{
var sw = new StreamWriter ( generateFilePath, false, Encoding.UTF8 );
var strBuilder = new StringBuilder ();
List<UIElement> temp = new List<UIElement> ();
foreach ( UIElement element in elements )
{
if(element.ComponentName.Equals("Button"))
temp.Add(element);
}
strBuilder.AppendLine ( "using UnityEngine;" );
strBuilder.AppendLine ( "using UnityEngine.UI;" );
strBuilder.AppendLine ();
strBuilder.AppendFormat ( "public class {0}Ctrl : BaseCtrl ", behaviourName );
strBuilder.AppendLine ( "{" );
strBuilder.AppendLine ();
strBuilder.AppendFormat ( "\tprivate {0} panel;", behaviourName );
strBuilder.AppendLine ();
strBuilder.AppendLine ();
strBuilder.AppendLine ( "\tpublic override void InitPanel()" );
strBuilder.AppendLine ( "\t{" );
strBuilder.AppendFormat ( "\t\tpanel = GetComponent<{0}>();", behaviourName );
strBuilder.AppendLine ();
foreach ( UIElement element in temp )
{
strBuilder.AppendFormat ( "\t\tpanel.{0}.AddListenerGracefully( {1}Click );", element.Name, element.Name );
strBuilder.AppendLine ();
}
strBuilder.AppendLine ( "\t}" );
strBuilder.AppendLine ();
foreach ( UIElement element in temp )
{
strBuilder.AppendFormat ("\tvoid {0}Click()",element.Name);
strBuilder.AppendLine ();
strBuilder.AppendLine ( "\t{" );
strBuilder.AppendLine ();
strBuilder.AppendLine ( "\t}" );
strBuilder.AppendLine ();
}
strBuilder.AppendLine ( "}" );
sw.Write ( strBuilder );
sw.Flush ();
sw.Close ();
AssetDatabase.SaveAssets ();
AssetDatabase.Refresh ();
}
public static string GetPath ( Transform transform )
{
var sb = new System.Text.StringBuilder ();
var t = transform;
while ( true )
{
sb.Insert ( 0, t.name );
t = t.parent;
if ( t )
{
sb.Insert ( 0, "/" );
}
else
{
return sb.ToString ();
}
}
}
}
using UnityEngine.UI;
using UnityEngine;
/// <inheritdoc />
/// <summary>
/// UI的标记
/// </summary>
public class UIMark : MonoBehaviour
{
//public UIMarkType MarkType = UIMarkType.DefaultUnityElement;
public Transform Transform
{
get { return transform; }
}
public string CustomComponentName = "";
public virtual string ComponentName
{
get
{
if ( !string.IsNullOrEmpty ( CustomComponentName ) )
return CustomComponentName;
if (null != GetComponent("SkeletonAnimation"))
return "SkeletonAnimation";
if (null != GetComponent<ScrollRect>())
return "ScrollRect";
if (null != GetComponent<InputField>())
return "InputField";
if (null != GetComponent<Text>())
return "Text";
if (null != GetComponent("TMP.TextMeshProUGUI"))
return "TextMeshProUGUI";
if (null != GetComponent<Button>())
return "Button";
if (null != GetComponent<RawImage>())
return "RawImage";
if (null != GetComponent<Toggle>())
return "Toggle";
if (null != GetComponent<Slider>())
return "Slider";
if (null != GetComponent<Scrollbar>())
return "Scrollbar";
if (null != GetComponent<Image>())
return "Image";
if (null != GetComponent<ToggleGroup>())
return "ToggleGroup";
if (null != GetComponent<Animator>())
return "Animator";
if (null != GetComponent<Canvas>())
return "Canvas";
if (null != GetComponent("Empty4Raycast"))
return "Empty4Raycast";
if (null != GetComponent<RectTransform>())
return "RectTransform";
return "Transform";
}
}
}
使用方法,把需要使用的组件加上UIMark标记,拖成预设体,右键点击自动生成代码,就会自动生成视图和控制层
-------------------------------弃用-------------------------------------
思想可以借鉴,但是使用起来不是很方便
最新版框架借鉴QFramework,喜欢的可以去看看