打开界面策略:
PolicyStack 栈式打开策略 同一时间只允许一个打开操作
using UnityEngine;
namespace UIFrameWork
{
/// <summary>
/// 栈式打开策略 同一时间只允许一个打开操作
/// </summary>
public class PolicyStack : PolicyBase
{
public PolicyStack(GameObject pRoot, int pStartZ, int pOffZ)
{
this.Root = pRoot;
startZ = pStartZ;
offZ = pOffZ;
}
public override void OpenCtrl(UICtrlBase pCtrlBase, System.Action pOnFinish, LastPanelOp pLastPanelOp = LastPanelOp.None, object pData = null)
{
pCtrlBase.SetParent(Root);
var performData = new PerformData(){
ctrlBase = pCtrlBase,
data = pData,
onFinish = pOnFinish,
lastPanelOp = pLastPanelOp
};
if (curPerformData == null)
{
curPerformData = performData;
OpenCtrlInternal(curPerformData, () => {
curPerformData = null;
LoopOpenAllCathePerfroms(null);
});
}
else
{
performCatchDatas.Add(performData);
}
}
}
}
PolicySingle 打开单独界面 有界面的时候,不会再打开其他界面 比例系统级别的提示弹窗 ,
加载界面等
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UIFrameWork
{
/// <summary>
/// 打开单独界面 有界面的时候,不会再打开其他界面 比例系统级别的提示弹窗 ,加载界面等
/// </summary>
public class PolicySingle : PolicyBase
{
public PolicySingle(GameObject pRoot, int pStartZ, int pOffZ)
{
this.Root = pRoot;
startZ = pStartZ;
offZ = pOffZ;
}
public override void OpenCtrl(UICtrlBase pCtrlBase, System.Action pOnFinish, LastPanelOp pLastPanelOp = LastPanelOp.None, object pData = null)
{
if (curPerformData == null && ctrlBases.Count < 1)
{
var performData = new PerformData()
{
ctrlBase = pCtrlBase,
data = pData,
onFinish = pOnFinish,
lastPanelOp = pLastPanelOp
};
curPerformData = performData;
OpenCtrlInternal(curPerformData, () =>
{
curPerformData = null;
});
}
else {
Debug.LogWarning($" is exist one single panel or have one panel is loading !");
}
}
}
}
PolicyMul /// 同时可以打开多个界面
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UIFrameWork
{
/// <summary>
/// 同时可以打开多个界面
/// </summary>
public class PolicyMul : PolicyBase
{
public override void OpenCtrl(UICtrlBase pCtrlBase, System.Action pOnFinish, LastPanelOp pLastPanelOp = LastPanelOp.None, object pData = null){
var performData = new PerformData(){
ctrlBase = pCtrlBase,
data = pData,
onFinish = pOnFinish,
lastPanelOp = pLastPanelOp
};
OpenCtrlInternal(performData, null);
}
}
}
当然可以根据项目需求自由扩展自己打开的扩展需求
界面管理类 UIManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UIFrameWork {
public class UIManager
{
private static UIManager instance;
public static UIManager Instance { get { if (instance == null) instance = new UIManager(); return instance; } }
public PolicyStack loginPolicy; //登录场景界面的策略
public PolicyStack mainPolicy; //主界面场景的策略
public PolicyStack battlePolicy; //战斗界面的策略
public PolicySingle tipPolicy; //玩家的基本提示 确定弹窗等
public PolicySingle systemPolicy; //系统级的提示 高等级 断开链接,报错,等系统级的提示策略
public PolicySingle loadPolicy; //加载界面
private GameObject canvas;
private GameObject mainRoot;
private GameObject systemRoot;
private GameObject loadRoot;
private bool isInit = false;
private string canvasName = "Canvas/UIRoot";
public static void SetFrameRoot(string mainName,GameObject mainRoot,string subName,GameObject subRoot,System.Action<GameObject,GameObject> pOnFinish) {
if (mainRoot == null){
mainRoot = GameObject.Find(mainName);
}
if (mainRoot == null) {
Debug.LogWarning($"mainRoot is nil {mainName}");
pOnFinish?.Invoke(null,null);
return;
}
if (subRoot == null) {
var subRootTrans = mainRoot.transform.Find(subName);
if (subRootTrans != null)
{
subRoot = subRootTrans.gameObject;
}
else
{
subRoot = new GameObject(subName);
subRoot.transform.parent = mainRoot.transform;
subRoot.transform.SetPositionAndRotation(Vector3.zero,Quaternion.identity);
subRoot.transform.localScale = Vector3.one;
subRoot.transform.localPosition = Vector3.zero;
}
var rectTransform = subRoot.GetComponent<RectTransform>();
if (rectTransform == null) {
rectTransform = subRoot.AddComponent<RectTransform>();
}
//rectTransform.pivot = new Vector2(0.5f,0.5f);
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.sizeDelta = Vector2.zero; // new (Screen.width,Screen.height);// (RectTransform.Axis.Horizontal,Screen.width);
//rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, Screen.height);
}
pOnFinish?.Invoke(mainRoot, subRoot);
}
public void OnInit()
{
if (isInit)
return;
isInit = true;
SetFrameRoot(canvasName, canvas,"MainRoot", mainRoot,(oMianRoot,oSubRoot)=> { canvas = oMianRoot; mainRoot = oSubRoot; });
SetFrameRoot(canvasName, canvas, "LoadRoot", loadRoot, (oMianRoot, oSubRoot) => { canvas = oMianRoot; loadRoot = oSubRoot; });
SetFrameRoot(canvasName, canvas, "SystemRoot", systemRoot, (oMianRoot, oSubRoot) => { canvas = oMianRoot; systemRoot = oSubRoot; });
//SetFrameRoot(canvasName, canvas, "LoadRoot", loadRoot);
//SetFrameRoot(canvasName, canvas, "SystemRoot", systemRoot);
loginPolicy = new PolicyStack(mainRoot, 10,100);
mainPolicy = new PolicyStack(mainRoot, 10, 100);
battlePolicy = new PolicyStack(mainRoot,10,100);
systemPolicy = new PolicySingle(systemRoot, 0,100);
loadPolicy = new PolicySingle(loadRoot,0,100);
}
public void OnDispose() {
isInit = false;
}
}
}
下面是View 层代码:
View 层代码比较简单 ,主要是实现显示的功能和自动生成Ctrl 和View 层代码的逻辑
ViewReference 界面的mono 物件挂载者
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using UIFrameWork;
[System.Serializable]
public class UIElementBinder {
public string key;
public ComponentType type;
public GameObject go;
}
public class ViewReference : MonoBehaviour
{
public List<UIElementBinder> binders = new List<UIElementBinder>();
#if UNITY_EDITOR
public UIModelType modelType = UIModelType.Login; //属于哪个模块的
public string subType; //自定义的子模块
public CtrlType cltrType = CtrlType.PanelCtrl; //控制类型
private bool CheckValid(){
var regex = new System.Text.RegularExpressions.Regex(@"^[a-zA-Z0-9_]+$");
if (!regex.IsMatch(gameObject.name)){
Debug.LogError("创建脚本失败 名字中包含非法字符 (请检查预制体名字之后是否多出一个空格):" + gameObject.name);
return false;
}
return true;
}
private void AddComponent(GameObject pTarget, ComponentType pType)
{
var componentSet = UIDef.ComponentSettings.Find((oInfo)=> {
return oInfo.type == pType;
});
if (componentSet != null)
{
var csComponent = componentSet.CompName;
if (pTarget.GetComponent(csComponent) != null){
var elementBinder = new UIElementBinder();
elementBinder.go = pTarget;
elementBinder.type = pType;
elementBinder.key = pTarget.name;
binders.Add(elementBinder);
}
else {
Debug.LogError($"get component is null type is : {pType} name is :{pTarget .name}");
}
}
else
{
Debug.LogError($"is not exist component type is {pType}");
}
}
private Transform[] AddTargetComponents(GameObject pTarget)
{
if (!ignoreList.Contains(pTarget))
{
var nameT = pTarget.name;
if (nameT.StartsWith(UIDef.dynamicEleKey))
{
for (var i = 0; i < UIDef.ComponentSettings.Count; i++)
{
var temp = UIDef.ComponentSettings[i];
if (nameT.Contains(temp.key))
{
AddComponent(pTarget, temp.type);
if (temp.isIngoreChild)
{
var childs = pTarget.GetComponentsInChildren<Transform>(true);
var viewReference_ = pTarget.GetComponentInChildren<ViewReference>(true);
if (viewReference_ != null)
{
viewReference_.PickAllComponent();
}
return childs;
}
break;
}
else
{
var csComponent = temp.CompName;
var com = pTarget.GetComponent(csComponent);
//Debug.LogError($"csComponent{csComponent}");
//if (csComponent == "UnityEngine.Transform") {
// Debug.LogError("11");
//}
if (com != null)
{
AddComponent(pTarget, temp.type);
if (temp.isIngoreChild)
{
var childs = pTarget.GetComponentsInChildren<Transform>(true);
var viewReference_ = pTarget.GetComponentInChildren<ViewReference>(true);
if (viewReference_ != null)
{
viewReference_.PickAllComponent();
}
return childs;
}
break;
}
}
}
}
}
return null;
}
private List<GameObject> ignoreList = new List<GameObject>();
[NaughtyAttributes.Button("绑定动态节点信息")]
private void PickAllComponent() {
binders.Clear();
ignoreList.Clear();
ignoreList.Add(this.gameObject);
var nameT = this.gameObject.name;
foreach (var temp in UIDef.ComponentSettings)
{
if (nameT.Contains(temp.key))
{
AddComponent(this.gameObject, temp.type);
}
}
var childs = this.gameObject.GetComponentsInChildren<Transform>(true);
foreach (var temp in childs)
{
var childs_ = AddTargetComponents(temp.gameObject);
if (childs_ != null)
{
foreach (var temp_ in childs_)
{
ignoreList.Add(temp_.gameObject);
}
}
}
}
[NaughtyAttributes.Button("生成ViewBase.cs")]
private void GenViewCsFile() {
if (!CheckValid())
return;
var filePath = GetCsFilePath();
CSGenerater.GenerateUIViewCSFile(filePath, this);
}
[NaughtyAttributes.Button("生成ViewBaseCtrl.cs")]
private void GenViewCsCtrlFile()
{
if (!CheckValid())
return;
var filePath = GetCsFilePath();
CSGenerater.GenerateUIBaseCtrlCSFile(filePath, this, cltrType);
}
private string GetCsFilePath() {
var gameName = gameObject.name;
if (gameName.StartsWith(UIDef.dynamicEleKey))
{
gameName = gameName.Substring(1, gameName.Length - 1);
}
string filePath = $"{modelType}/{gameName}";// gameObject.name;
if (!string.IsNullOrEmpty(subType))
{
filePath = $"{modelType}/{subType}/{gameName}";
}
return filePath;
}
[NaughtyAttributes.Button("<<定位ViewBase.cs>>")]
private void PingViewCsFile() {
var pFilePath = GetCsFilePath();
var filePath = Application.dataPath + "/" + UIDef.uiRootPanel + pFilePath + ".cs";
if (System.IO.File.Exists(filePath)){
var assetPath = "Assets/" + UIDef.uiRootPanel + pFilePath + ".cs";
var assetObj = UnityEditor.AssetDatabase.LoadAssetAtPath<TextAsset>(assetPath);
UnityEditor.EditorGUIUtility.PingObject(assetObj);
//UnityEditor.Selection.activeObject = assetObj;
}
else {
Debug.LogError($"file is not exist path is {filePath}");
}
}
[NaughtyAttributes.Button("<<定位ViewBaseCtrl.cs>>")]
private void PingViewCsCtrlFile()
{
var pFilePath = GetCsFilePath();
var filePath = Application.dataPath + "/" + UIDef.uiRootPanel + pFilePath + "Ctrl.cs";
if (System.IO.File.Exists(filePath))
{
var assetPath = "Assets/" + UIDef.uiRootPanel + pFilePath + "Ctrl.cs";
var assetObj = UnityEditor.AssetDatabase.LoadAssetAtPath<TextAsset>(assetPath);
//UnityEditor.Selection.objects = new UnityEngine.Object[1] { assetObj };
UnityEditor.EditorGUIUtility.PingObject(assetObj);
//UnityEditor.Selection.activeObject = assetObj;
}
else
{
Debug.LogError($"file is not exist path is {filePath}");
}
}
#endif
}
界面的显示逻辑
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIViewBase
{
protected ViewReference viewReference;
protected EventG.StringObjectEvent ctrlEvent;
public void SetCtrlEvnet(EventG.StringObjectEvent pCtrlEvent) {
ctrlEvent = pCtrlEvent;
}
public virtual void SetViewReference(ViewReference pViewReference) {
viewReference = pViewReference;
}
public T GetComponent<T>(int index) where T : Component
{
if (this.viewReference != null && this.viewReference.binders.Count > index)
{
return this.viewReference.binders[index].go.GetComponent<T>();
}
return null;
}
public virtual void OnInit()
{
}
public virtual void OnForward()
{
}
public virtual void OnBackground()
{
}
public virtual void OnDestroy()
{
}
}
自动构建代码逻辑
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UIFrameWork;
using UnityEditor;
using UnityEngine;
public static class CSGenerater
{
private static string modelViewName = "XUIView";
private static string modelCtrlName = "XViewCtrl";
public static void GenerateUIBaseCtrlCSFile(string pFilePath, ViewReference pViewRef, CtrlType pCtrlType)
{
var rootPath = Application.dataPath + "/" + UIDef.uiRootModel;
var modeulFilepath = $"{rootPath}{modelCtrlName}.cs"; //Application.dataPath + "/ZFrameWork/Scripts/UI/Model/XPanelCtrl.cs";
pFilePath = pFilePath.Replace("\\", "/");
var filepath = Application.dataPath + "/" + UIDef.uiRootPanel + pFilePath + "Ctrl.cs";
var fileName = filepath.Substring(filepath.LastIndexOf("/") + 1);
fileName = fileName.Replace(".cs", "");
//var releatePath = pFilePath.Replace("UI/Exts/", "");
//releatePath = releatePath.Replace("Ctrl", "");
if (File.Exists(filepath))
{
if (UnityEditor.EditorUtility.DisplayDialog("面板的LuaCtrl文件已经存在", "是否删除重新生成 ?", "确定", "取消"))
{
File.Delete(filepath);
StartGeneralCtrlCS(pViewRef, modeulFilepath, fileName, filepath, pCtrlType);
}
}
else
{
StartGeneralCtrlCS(pViewRef, modeulFilepath, fileName, filepath, pCtrlType);
}
}
private static void StartGeneralCtrlCS(ViewReference pViewRef, string modeulFilepath, string fileName, string filepath, CtrlType pCtrlType)
{
var modeulContext = ReadModel(modeulFilepath);
Debug.Log(fileName);
modeulContext = modeulContext.Replace(modelCtrlName, fileName);
//var assetPath = AssetDatabase.GetAssetPath(pComponent.gameObject);
var assetPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(pViewRef.gameObject);
switch (pCtrlType) {
case CtrlType.SubCtrl:
modeulContext = modeulContext.Replace("UICtrlPanel", "UICtrlSub");
break;
case CtrlType.ItemCtrl:
modeulContext = modeulContext.Replace("UICtrlPanel", "UICtrlItem");
break;
}
modeulContext = modeulContext.Replace("--respath--", assetPath);
modeulContext = modeulContext.Replace("--resname--", pViewRef.gameObject.name);
//fillviewinit
var initViewContent = GetCSCtrlInitViewContent(pViewRef,out string oViewName);
modeulContext = modeulContext.Replace("uiViewBase", oViewName.ToLower());
//modeulContext = modeulContext.Replace("UIViewBase", oViewName);
modeulContext = modeulContext.Replace("//fillviewinit", initViewContent);
//filladdevents
var addEventContent = GetCSCtrlAddEventsContent(pViewRef);
modeulContext = modeulContext.Replace("//filladdevents", addEventContent);
//fillremoveevents
var removeEventContent = GetCSCtrlRemoveEventsContent(pViewRef);
modeulContext = modeulContext.Replace("//fillremoveevents", removeEventContent);
//fillevents
var eventContent = GetCsCtrlEventContent(pViewRef);
modeulContext = modeulContext.Replace("//fillevents", eventContent);
WriteFile(filepath, modeulContext);
}
private static string GetCsCtrlEventContent(ViewReference pViewRef) {
string result = "";
var binds = pViewRef.binders;
int addCount = 0;
for (var i = 0; i < binds.Count; i++)
{
var eleBiner = binds[i];
var comType = eleBiner.type;
if (comType == ComponentType.Button)
{
if (addCount > 0)
{
result += "\n";
}
var conet = "\tprivate void OnClick"+ eleBiner .key+ "(System.Object obj) \n\t{\n\t\t//TODO finish this event \n\t}";
result += conet;
addCount += 1;
}
}
return result;
}
private static string GetCSCtrlRemoveEventsContent(ViewReference pViewRef)
{
string result = "";
var binds = pViewRef.binders;
int addCount = 0;
for (var i = 0; i < binds.Count; i++)
{
var eleBiner = binds[i];
var comType = eleBiner.type;
if (comType == ComponentType.Button)
{
if (addCount > 0)
{
result += "\n";
}
var conet = $"\t\tRemoveCtrlEvent(\"onClick{eleBiner.key}\", OnClick{eleBiner.key});";
result += conet;
addCount += 1;
}
}
return result;
}
private static string GetCSCtrlAddEventsContent(ViewReference pViewRef)
{
string result = "";
var binds = pViewRef.binders;
int addCount = 0;
for (var i = 0; i < binds.Count; i++)
{
var eleBiner = binds[i];
var comType = eleBiner.type;
if (comType == ComponentType.Button) {
if (addCount > 0) {
result += "\n";
}
var conet = $"\t\tAddCtrlEvent(\"onClick{eleBiner.key}\", OnClick{eleBiner.key});";
result += conet;
addCount+=1;
}
}
return result;
}
private static string GetCSCtrlInitViewContent(ViewReference pViewRef,out string oViewName) {
var viewName = pViewRef.name;
if (viewName.StartsWith("_")) {
viewName = viewName.Substring(1, viewName.Length - 1);
}
oViewName = viewName;
var lowerViewName = oViewName.ToLower();
string reuslt = $"\t\t{lowerViewName} = new {viewName}();\n";
reuslt += $"\t\t{lowerViewName}.SetViewReference(viewReference);\n";
reuslt += $"\t\t{lowerViewName}.OnInit();\n";
reuslt += $"\t\tSetUIViewBase({lowerViewName});";
//uiViewBase.SetViewReference(viewReference);
//uiViewBase.OnInit();
//SetUIViewBase(uiViewBase);
return reuslt;
}
public static void GenerateUIViewCSFile(string pFilePath, ViewReference pComponent)
{
var rootPath = Application.dataPath + "/"+UIDef.uiRootModel;
var modeulFilepath = $"{rootPath}{modelViewName}.cs";
pFilePath = pFilePath.Replace("\\", "/");
var filepath = Application.dataPath + "/"+ UIDef.uiRootPanel + pFilePath + ".cs";
var fileName = filepath.Substring(filepath.LastIndexOf("/") + 1);
fileName = fileName.Replace(".cs", "");
if (File.Exists(filepath)){
if (UnityEditor.EditorUtility.DisplayDialog("面板的cs文件已经存在", "是否删除重新生成 ?", "确定", "取消")){
File.Delete(filepath);
StartGeneralCsFile(pComponent, modeulFilepath, fileName, filepath);
}
}
else{
StartGeneralCsFile(pComponent, modeulFilepath, fileName, filepath);
}
}
private static void StartGeneralCsFile(ViewReference pViewRef, string modeulFilepath, string fileName, string filepath)
{
var modeulContext = ReadModel(modeulFilepath);
Debug.Log(fileName);
modeulContext = modeulContext.Replace(modelViewName, fileName);
var csLoclString = GetCsLocalString(pViewRef);
modeulContext = modeulContext.Replace("//Local Compnent", csLoclString);
var csInitString = GetCsInitString(pViewRef);
modeulContext = modeulContext.Replace("//Init Component", csInitString);
var csForwardText = GetCsForwardString(pViewRef);
modeulContext = modeulContext.Replace("//On Forward", csForwardText);
WriteFile(filepath, modeulContext);
}
private static string GetCsForwardString(ViewReference pViewRef) {
var result = "";
var binds = pViewRef.binders;
for (var i = 0; i < binds.Count; i++)
{
var eleBiner = binds[i];
var comType = eleBiner.type;
var compoentTypeInfo = UIDef.GetComponentTypeInfoByType(comType);
var defaultValue = compoentTypeInfo.CompValue;
if(comType == ComponentType.Text)
{
var textValue = eleBiner.go.GetComponent<UnityEngine.UI.Text>();
defaultValue = $".text = \"{textValue.text}\"";
}
if (!defaultValue.IsNullOrEmpty()) {
var conent = $"\t\tthis.{eleBiner.key}{defaultValue};";
if (i < binds.Count - 1)
{
conent += "\n";
}
result += conent;
}
}
return result;
}
private static string GetCsLocalString(ViewReference pViewRef){
var result = "";
var binds = pViewRef.binders;
for (var i = 0; i < binds.Count; i++) {
var eleBiner = binds[i];
var comType = eleBiner.type;
var compoentTypeInfo = UIDef.GetComponentTypeInfoByType(comType);
var csCom = compoentTypeInfo.CompSName;
var conent = $"\tprivate {csCom} {eleBiner.key};";
if (i < binds.Count - 1) {
conent += "\n";
}
result += conent;
}
return result;
}
private static string GetCsInitString(ViewReference pViewRef) {
var result = "";
var binds = pViewRef.binders;
for (var i = 0; i < binds.Count; i++)
{
var eleBiner = binds[i];
var comType = eleBiner.type;
var compoentTypeInfo = UIDef.GetComponentTypeInfoByType(comType);
var csCom = compoentTypeInfo.CompSName;
var conent = $"\t\tthis.{eleBiner.key} = this.viewReference.binders[{i}].go.GetComponent<{csCom}>();";
if (comType == ComponentType.Button)
{
conent += "\n";
var clickEventMsg = $"onClick{eleBiner.key}";
var CsComponentEx = "\t\tthis." + eleBiner.key + ".onClick.AddListener(() => { ctrlEvent.SendMessage(" + "\"" + clickEventMsg + "\"" + ", null); });";
conent += CsComponentEx;
}
else if (comType == ComponentType.GameObject) {
conent = $"\t\tthis.{eleBiner.key} = this.viewReference.binders[{i}].go;";
}
if(i < binds.Count-1){
conent += "\n";
}
result += conent;
}
return result;
}
public static string ReadModel(string pFilePath)
{
StreamReader streamReader = new StreamReader(pFilePath, Encoding.UTF8);
string context = streamReader.ReadToEnd();
streamReader.Close();
return context;
}
public static void WriteFile(string pFilePath, string pContext)
{
pFilePath.CreateDirectoryIfNotExists();
FileStream fs = new FileStream(pFilePath, FileMode.Create, FileAccess.Write);//创建写入文件
StreamWriter streamWriter = new StreamWriter(fs);
streamWriter.WriteLine(pContext);
streamWriter.Close();
fs.Close();
UnityEditor.AssetDatabase.Refresh();
Debug.Log(string.Format("File has created at path :------{0}.", pFilePath));
}
}
#endif
上面都是框架层代码,后面会介绍使用实例