基本的逻辑如下:
1:我把各个面板的Prefab存到资源目录下,Resources、StreamAssets、Addressable
我是使用了Addressable,因为可能要涉及到WebGL,要节约我的打包空间,所以干脆把所有的面板都丢到了服务器上,然后我从服务器上远程加载
2:给本地的面板类型和路径创建一个Json文件,如下:
{
"panelInfoList": [
{
"panelType": "PopUp",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/PopUpPanel.prefab"
},
{
"panelType":"Enter",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/EnterTrainingPanel.prefab"
},
{
"panelType": "Login",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/LoginPanel.prefab"
},
{
"panelType": "Introduction",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/IntroductionPanel.prefab"
},
{
"panelType": "Loading",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/LoadingPanel.prefab"
},
{
"panelType": "PlanBG",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/PlanBGPanel.prefab"
},
{
"panelType": "YieldPlan",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/YieldPlanPanel.prefab"
},
{
"panelType": "MoneyPlan",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/MoneyPlanPanel.prefab"
},
{
"panelType": "SelectPatten",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/SelectPattenPanel.prefab"
},
{
"panelType": "TaskPlan",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/TaskPlanPanel.prefab"
},
{
"panelType": "InsurePlan",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPanels/InsurePlanPanel.prefab"
},
{
"panelType": "MainTask",
"path": "Assets/Art/Prefabs(CiShenUI)/MainPagePanels/MainTaskPanel.prefab"
},
{
"panelType":"Market",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/MarketPanel.prefab"
},
{
"panelType":"ShoppingCartPanel",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/ShoppingCartPanel.prefab"
},
{
"panelType":"Warehouse",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/WarehousePanel.prefab"
},
{
"panelType":"YieldPlanCheck",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/YieldPlanCheckPanel.prefab"
},
{
"panelType":"LiveState",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/LiveStatePanel.prefab"
},
{
"panelType":"KnowLedge",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/KnowLedgePanel.prefab"
},
{
"panelType":"Financial",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/FinancialPanel.prefab"
},
{
"panelType":"Assemble",
"path":"Assets/Art/Prefabs(CiShenUI)/MainPagePanels/AssemblePanel.prefab"
}
]
}
3:然后做一个数据类,专门存储字段和面板名称一样的常量,如下:
/// <summary>
/// 所有的面板名称
/// </summary>
public class UIPanelType
{
public const string PopUp = "PopUp";
public const string Enter = "Enter";
public const string Login = "Login";
public const string Introduction = "Introduction";
public const string Loading = "Loading";
public const string PlanBG = "PlanBG";
public const string YieldPlan = "YieldPlan";
public const string MoneyPlan = "MoneyPlan";
public const string SelectPatten = "SelectPatten";
public const string TaskPlan = "TaskPlan";
public const string InsurePlan = "InsurePlan";
public const string MainTask = "MainTask";
public const string Market = "Market";
public const string ShoppingCartPanel = "ShoppingCartPanel";
public const string Warehouse = "Warehouse";
public const string YieldPlanCheck = "YieldPlanCheck";
public const string LiveState = "LiveState";
public const string KnowLedge = "KnowLedge";
public const string Financial = "Financial";
public const string Assemble = "Assemble";
}
4:然后再来一个UIManager来解析和读取,我把注释都写得很清楚
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using Cysharp.Threading.Tasks;
using UnityEngine.Networking;
/// <summary>
/// 该类用于控制面板的字典和UI面板的加载方式
/// </summary>
public class UIManagerCi
{
private static UIManagerCi _instance;
private Transform canvasTransform;
private bool isInitialized = false;
private Transform CanvasTransform
{
get
{
if (canvasTransform == null)
{
canvasTransform = GameObject.Find("MainCanvas").transform;
}
return canvasTransform;
}
}
public static UIManagerCi Instance
{
get
{
if(_instance == null)
{
_instance = new UIManagerCi();
}
return _instance;
}
}
/// <summary>
/// 存储面板名称和路径的字典
/// </summary>
private Dictionary<string, string> panelPathDic;
/// <summary>
/// 存储面板名称和具体面板的字典
/// </summary>
public Dictionary<string, BaseUIPanel> panelDic;
/// <summary>
/// 用于控制界面切换的栈
/// </summary>
private Stack<BaseUIPanel> panelStack;
string jsonFilePath = Application.streamingAssetsPath + "/UIPanelType.json";
//string jsonFilePath = new System.Uri(Application.streamingAssetsPath + "/UIPanelType.json").AbsoluteUri;
/// <summary>
/// 界面入栈方法
/// </summary>
/// <param name="panelType">界面的名称</param>
public async UniTask<BaseUIPanel> PushPanel(string panelType)
{
if (!isInitialized)
{
await InitializeAsync();
}
if (panelStack == null)
{
panelStack = new Stack<BaseUIPanel>();
}
// if (panelStack.Count > 0)
// {
// BaseUIPanel topPanel = panelStack.Peek();
// // topPanel.OnPause();
// }
BaseUIPanel panel = await GetPanel(panelType);
panelStack.Push(panel);
return panel;
//然后执行当界面进入时的方法
//panel.OnEnter();
}
/// <summary>
/// 界面出栈方法
/// </summary>
public void PopPanel()
{
if(panelStack == null)
{
panelStack = new Stack<BaseUIPanel>();
}
if(panelStack.Count <= 0)
{
return;
}
BaseUIPanel topPanel = panelStack.Pop();
topPanel.OnExit();
// //如果栈中还有元素,就获取该元素,然后调用方法
// if(panelStack.Count > 0)
// {
// BaseUIPanel panel = panelStack.Peek();
// // panel.OnResume();
// }
}
/// <summary>
/// 对UI面板的Prefab进行实例化创建和管理
/// </summary>
/// <param name="panelType"></param>
/// <returns></returns>
private async UniTask<BaseUIPanel> GetPanel(string panelType)
{
if (!isInitialized)
{
await InitializeAsync();
}
if (panelDic == null)
{
panelDic = new Dictionary<string, BaseUIPanel>();
}
BaseUIPanel panel = panelDic.GetValue(panelType);
//如果面板没有实例化的话,就从路径字典中进行实例化,然后存储到已经实例化好的字典中
if (panel == null)
{
//从路径字典里面获取界面的存储路径并加载xin
string panelPath = panelPathDic.GetValue(panelType);
var loadOperation = Addressables.LoadAssetAsync<GameObject>(panelPath);
await loadOperation.Task;
// loadOperation.WaitForCompletion();
if(loadOperation.Status == AsyncOperationStatus.Succeeded)
{
GameObject panelPrefab = loadOperation.Result;
GameObject panelGo = GameObject.Instantiate(panelPrefab,CanvasTransform,false);
panel = panelGo.GetComponent<BaseUIPanel>();
panelDic.Add(panelType, panel);
}
else
{
Debug.LogError("Panel loading failed: " + loadOperation.OperationException);
}
// Addressables.Release(loadOperation);
// GameObject panelGo = (GameObject)GameObject.Instantiate(Resources.Load(panelPath), CanvasTransform, false);
}
return panel;
}
public async UniTask InitializeAsync()
{
if (!isInitialized)
{
await ParseUIPanelTypeJson();
isInitialized = true;
}
}
/// <summary>
/// 将Json文件读取到数据类
/// 然后把名称和路径都存在字典里面
/// </summary>
private async UniTask ParseUIPanelTypeJson()
{
panelPathDic = new Dictionary<string, string>();
//string jsonUIString = File.ReadAllText(jsonFilePath);
UnityWebRequest request = UnityWebRequest.Get(jsonFilePath);
await request.SendWebRequest();
string str = request.downloadHandler.text;
// 把和Json文件映射的类写入Json
UIPanelInfoList panelInfoList = JsonUtility.FromJson<UIPanelInfoList>(str);
foreach(UIPanelInfo uIPanelInfo in panelInfoList.panelInfoList)
{
panelPathDic.Add(uIPanelInfo.panelType, uIPanelInfo.path);
// Debug.Log(panelPathDic[uIPanelInfo.panelType]);
}
}
最主要得方法就是Pop和Push方法,用于把面板加载或卸载,在Push里面有一个GetPanel方法可以讲一下
private async UniTask<BaseUIPanel> GetPanel(string panelType)
{
if (!isInitialized)
{
await InitializeAsync();
}
if (panelDic == null)
{
panelDic = new Dictionary<string, BaseUIPanel>();
}
BaseUIPanel panel = panelDic.GetValue(panelType);
//如果面板没有实例化的话,就从路径字典中进行实例化,然后存储到已经实例化好的字典中
if (panel == null)
{
//从路径字典里面获取界面的存储路径并加载xin
string panelPath = panelPathDic.GetValue(panelType);
var loadOperation = Addressables.LoadAssetAsync<GameObject>(panelPath);
await loadOperation.Task;
// loadOperation.WaitForCompletion();
if(loadOperation.Status == AsyncOperationStatus.Succeeded)
{
GameObject panelPrefab = loadOperation.Result;
GameObject panelGo = GameObject.Instantiate(panelPrefab,CanvasTransform,false);
panel = panelGo.GetComponent<BaseUIPanel>();
panelDic.Add(panelType, panel);
}
else
{
Debug.LogError("Panel loading failed: " + loadOperation.OperationException);
}
// Addressables.Release(loadOperation);
// GameObject panelGo = (GameObject)GameObject.Instantiate(Resources.Load(panelPath), CanvasTransform, false);
}
return panel;
}
在每次Push得时候都会先判断panelDic是否创建,如果没有创建,就重新创建,如果创建了就从该字典中获取到面板得名称
string panelPath = panelPathDic.GetValue(panelType);
var loadOperation = Addressables.LoadAssetAsync<GameObject>(panelPath);
await loadOperation.Task;
这就是使用Addressable来加载面板
if(loadOperation.Status == AsyncOperationStatus.Succeeded)
{
GameObject panelPrefab = loadOperation.Result;
GameObject panelGo = GameObject.Instantiate(panelPrefab,CanvasTransform,false);
panel = panelGo.GetComponent<BaseUIPanel>();
panelDic.Add(panelType, panel);
}
如果加载成功了,就实例化,然后把这个面板存储到字典里面,否则就打印加载失败
5:这些面板有基类,如下:
using UnityEngine;
public abstract class BaseUIPanel : MonoBehaviour
{
/// <summary>
/// 当面板进入时
/// </summary>
public abstract void OnEnter();
/// <summary>
/// 当个面板停止的时候调用
/// </summary>
public abstract void OnPause();
/// <summary>
/// 当面板恢复的时候
/// </summary>
public abstract void OnResume();
/// <summary>
/// 当面板退出的时候调用
/// </summary>
public abstract void OnExit();
}
都会在相应得时机执行相应得方法
然后让你的各个面板类去继承该基类
6:最后有一个启动节点,挂载到MainCanvas下面即可
using System;
using UnityEngine;
public class CanvasRoot : MonoBehaviour
{
刺参计划购买,材料购买,饲料购买,药品购买名称
//public string CiShenPlan, MaterialPlan, FoodPlan, DrugPlan;
计划购买具体金额
//public float CiShenPlanNum, MaterialPlanNum, FoodPlanNum, DrugPlanNum;
计划密度,数量,体长
//public string PlanDensity, PlanCount, PlanLength;
计划具体数值
//public float PlanDensityNum, PlanCountNum, PlanLengthNum;
计划的单位
//public string PlanDensityUnit, PlanCountUnit, PlanLengthUnit;
private async void Awake()
{
UIManagerCi panelManager = UIManagerCi.Instance;
BaseUIPanel PopUpPanel = await panelManager.PushPanel(UIPanelType.PopUp);
BaseUIPanel EnterPanel = await panelManager.PushPanel(UIPanelType.Enter);
}
}
创建面板就是下面两行代码就行,我因为是异步加载,所以使用了await
BaseUIPanel PopUpPanel = await panelManager.PushPanel(UIPanelType.PopUp);
BaseUIPanel EnterPanel = await panelManager.PushPanel(UIPanelType.Enter);
基本逻辑就是上面的,剩下的你如果想用本地加载就把面板Prefab放到Resources或者StreamingAssets下,如果远程下载,就把面板放到后台就行