This commit is contained in:
2025-09-18 18:25:40 +08:00
parent 8ee325cc01
commit ce06ca8571
220 changed files with 165196 additions and 21 deletions

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using YooAsset;
namespace SkierFramework
{
[System.Serializable]
public class UIConfigJson
{
public string uiType;
public string path;
public bool isWindow;
public string uiLayer;
}
public class UIConfig
{
public string path;
public string uiType;
public UILayer uiLayer;
public Type viewType;
public bool isWindow;
private const string UIConfigPath = "Assets/UI/UISystemPackage/UIConfig.json";
public const string UIPackageName = "PanelPackage";
public static async UniTask<List<UIConfig>> GetAllConfigs()
{
var handle = await ResourceManager.Instance.GetResourceCore(UIPackageName)
.LoadAssetAsync<UnityEngine.TextAsset>(UIConfigPath);
var textAsset = handle.GetAssetObject<TextAsset>();
if (textAsset != null)
{
var list = new List<UIConfig>();
var uiConfigs = Newtonsoft.Json.JsonConvert.DeserializeObject<List<UIConfigJson>>(textAsset.text);
foreach (var config in uiConfigs)
{
if (!Enum.TryParse<UILayer>(config.uiLayer, out UILayer layer))
{
layer = UILayer.NormalLayer;
Debug.LogErrorFormat("UIConfig.json 中的:{0} uiLayer解析异常 {1}", config.path, config.uiLayer);
}
Type viewType = GetType(config.uiType.ToString());
if (viewType == null)
{
viewType = GetType($"{typeof(UIConfig).Namespace}.{config.uiType}");
}
list.Add(new UIConfig
{
path = config.path,
uiLayer = layer,
uiType = config.uiType,
viewType = viewType,
isWindow = config.isWindow
});
}
return list;
}
else
{
Debug.LogError("未找到配置:" + UIConfigPath);
return null;
}
}
public static Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null)
{
return type;
}
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (System.Reflection.Assembly assembly in assemblies)
{
type = Type.GetType(string.Format("{0}, {1}", typeName, assembly.FullName));
if (type != null)
{
return type;
}
}
return null;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16d24bc1f42c9344cbd4957ff9212b22
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace SkierFramework
{
public enum UILayer
{
SceneLayer = 1000,
BackgroundLayer = 2000,
NormalLayer = 3000,
InfoLayer = 4000,
TopLayer = 5000,
TipLayer = 6000,
BlackMaskLayer = 7000,
}
public class UILayerLogic
{
public UILayer layer;
public Canvas canvas;
private int maxOrder;
private HashSet<int> orders;
public Stack<UIViewController> openedViews;
public UILayerLogic(UILayer layer, Canvas canvas)
{
this.layer = layer;
this.canvas = canvas;
maxOrder = (int)layer;
orders = new HashSet<int>();
openedViews = new Stack<UIViewController>();
}
public void CloseUI(UIViewController closedUI)
{
int order = closedUI.order;
PushOrder(closedUI);
closedUI.order = 0;
if (openedViews.Count > 0)
{
var topViewController = openedViews.Peek();
// 拿到最上层UI如果被暂停的话则恢复
// 暂停和恢复不影响其是否被覆盖隐藏只要不是最上层UI都应该标记暂停状态
if (topViewController != null && topViewController.isPause)
{
topViewController.isPause = false;
if (topViewController.uiView != null)
{
topViewController.uiView.OnResume();
}
}
if (!closedUI.isWindow)
{
foreach (var viewController in openedViews)
{
if (viewController != closedUI
&& viewController.isOpen
&& viewController.order < order)
{
viewController.AddTopViewNum(-1);
}
}
}
}
}
public void OpenUI(UIViewController openedUI)
{
if (openedUI.order == 0)
{
openedUI.order = PopOrder(openedUI);
}
foreach (var viewController in openedViews)
{
if (viewController != openedUI
&& viewController.isOpen
&& viewController.order < openedUI.order
&& viewController.uiView != null)
{
if (!viewController.isPause)
{
viewController.isPause = true;
viewController.uiView.OnPause();
}
if (!openedUI.isWindow)
{
viewController.AddTopViewNum(1);
}
}
}
}
public void PushOrder(UIViewController closedUI)
{
int order = closedUI.order;
if (orders.Remove(order))
{
// 重新计算最大值
maxOrder = (int)layer;
foreach (var item in orders)
{
maxOrder = Mathf.Max(maxOrder, item);
}
// 移除界面
List<UIViewController> list = ListPool<UIViewController>.Get();
while (openedViews.Count > 0)
{
var view = openedViews.Pop();
if (view != closedUI)
{
list.Add(view);
}
else
{
break;
}
}
for (int i = list.Count - 1; i >= 0; i--)
{
openedViews.Push(list[i]);
}
ListPool<UIViewController>.Release(list);
}
}
public int PopOrder(UIViewController uIViewController)
{
maxOrder += 10;
orders.Add(maxOrder);
openedViews.Push(uIViewController);
return maxOrder;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 79754d061a9aabd43bed6e92b7a958f9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace SkierFramework
{
public class UISubView : MonoBehaviour, IBindableUI
{
protected bool isInit = false;
private void Awake()
{
OnInit();
OnAddListener();
}
private void OnEnable()
{
OnOpen();
}
private void OnDisable()
{
OnClose();
}
private void OnDestroy()
{
OnRelease();
OnRemoveListener();
}
private void Bind()
{
if (isInit) return;
var uIControlData = GetComponent<UIControlData>();
if (uIControlData != null)
uIControlData.BindDataTo(this);
isInit = true;
}
public virtual void OnInit() {
if (isInit) return;
Bind();
}
public virtual void OnAddListener() { }
public virtual void OnRemoveListener() { }
public virtual void OnOpen()
{
if (!isInit)
{
OnInit();
}
}
public virtual void OnClose() { }
public virtual void OnRelease() { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5c72d350471207844abfed3c3f1f1f37
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,141 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;
namespace SkierFramework
{
public class UIView : MonoBehaviour, IBindableUI
{
private UIViewController _controller;
private GameObject _lastSelect;
private Canvas _canvas;
public GameObject DefaultSelect;
public UIViewController Controller => _controller;
public virtual void OnInit(UIControlData uIControlData, UIViewController controller)
{
if (uIControlData != null)
{
uIControlData.BindDataTo(this);
}
_controller = controller;
_canvas = gameObject.GetOrAddComponent<Canvas>();
gameObject.GetOrAddComponent<CanvasScaler>();
gameObject.GetOrAddComponent<GraphicRaycaster>();
}
/// <summary>
/// 事件监听
/// </summary>
public virtual void OnAddListener() { }
/// <summary>
/// 事件移除
/// </summary>
public virtual void OnRemoveListener() { }
/// <summary>
/// 打开
/// </summary>
public virtual void OnOpen(object userData)
{
SortOrder();
_canvas.overrideSorting = true;
_canvas.sortingOrder = _controller.order;
OnAddListener();
_lastSelect = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject;
}
protected virtual void SortOrder()
{
SortOrder(transform, _controller.order + 1);
}
/// <summary>
/// 递归的将所有孩子层级设置正确一些默认摆在UI上的特效等正确分配层级
/// </summary>
protected int SortOrder(Transform target, int order)
{
var canvas = target.GetComponent<Canvas>();
if (canvas != null && canvas != _canvas)
{
canvas.overrideSorting = true;
canvas.sortingOrder = order++;
canvas.gameObject.layer = Layer.UI;
}
var psr = target.GetComponent<ParticleSystemRenderer>();
if (psr != null)
{
psr.sortingOrder = order++;
psr.gameObject.layer = Layer.UI;
}
var sortGroup = target.GetComponent<SortingGroup>();
if (sortGroup != null)
{
sortGroup.sortingOrder = order++;
sortGroup.gameObject.SetLayerRecursively(Layer.UI);
}
var sr = target.GetComponent<SpriteRenderer>();
if (sr != null)
{
sr.sortingOrder = order++;
sr.gameObject.layer = Layer.UI;
}
for (int i = 0; i < target.childCount; i++)
{
order = SortOrder(target.GetChild(i), order);
}
return order;
}
/// <summary>
/// 恢复
/// </summary>
public virtual void OnResume()
{
if (DefaultSelect != null)
{
UnityEngine.EventSystems.EventSystem.current.SetSelectedGameObject(DefaultSelect);
}
}
/// <summary>
/// 被覆盖
/// </summary>
public virtual void OnPause()
{
}
/// <summary>
/// 被关闭
/// </summary>
public virtual void OnClose()
{
OnRemoveListener();
if (_lastSelect != null && _lastSelect.activeInHierarchy)
{
UnityEngine.EventSystems.EventSystem.current.SetSelectedGameObject(_lastSelect);
}
}
/// <summary>
/// 取消按钮响应
/// </summary>
public virtual void OnCancel()
{
UIManager.Instance.Close(this);
}
/// <summary>
/// 被卸载释放
/// </summary>
public virtual void OnRelease() { }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f5c123e21fb60cb40bafea6c26519605
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace SkierFramework
{
public enum UIAppearType
{
None,
Animation,
Alpha,
AlphaAndAnimation,
Scale,
ScaleAndAlpha,
}
public class UIViewAnim : MonoBehaviour
{
public UIAppearType openType = UIAppearType.None;
public UIAppearType closeType = UIAppearType.None;
public Animation animtion;
public Transform target;
public float animTime = 0.2f;
public float playRate;
private float duration;
private Action callback;
private CanvasGroup canvasGroup;
private Transform Target => target != null ? target : transform;
private enum ViewType
{
None,
Open,
Close,
}
private ViewType viewType;
private void Start()
{
canvasGroup = gameObject.GetOrAddComponent<CanvasGroup>();
playRate = 0;
}
public void Open(Action callback = null)
{
playRate = 0;
if (viewType == ViewType.Close)
{
this.callback?.Invoke();
}
if (!gameObject.activeSelf)
{
callback?.Invoke();
return;
}
viewType = ViewType.Open;
this.callback = callback;
duration = (1 - playRate) * animTime;
if (openType == UIAppearType.Animation || openType == UIAppearType.AlphaAndAnimation)
{
if (animtion != null && animtion.clip != null)
{
var state = animtion[animtion.clip.name];
if (state != null)
{
state.normalizedTime = playRate;
state.speed = state.length / animTime;
}
animtion.Play();
}
else if(openType == UIAppearType.Animation)
{
playRate = 1;
Finish();
}
}
}
public void Close(Action callback = null)
{
if (viewType == ViewType.Open)
{
this.callback?.Invoke();
}
if (!gameObject.activeSelf)
{
callback?.Invoke();
return;
}
viewType = ViewType.Close;
this.callback = callback;
duration = playRate * animTime;
if (closeType == UIAppearType.Animation || closeType == UIAppearType.AlphaAndAnimation)
{
if (animtion != null && animtion.clip != null)
{
var state = animtion[animtion.clip.name];
if (state != null)
{
state.normalizedTime = 1 - playRate;
state.speed = -1 * state.length / animTime;
}
animtion.Play();
}
else if (openType == UIAppearType.Animation)
{
playRate = 0;
Finish();
}
}
}
private void LateUpdate()
{
switch (viewType)
{
case ViewType.Open:
PlayAlphaAndScale(openType, false);
break;
case ViewType.Close:
PlayAlphaAndScale(closeType, true);
break;
default:
break;
}
}
/// <summary>
/// 播放动画
/// </summary>
/// <param name="back">是否倒放</param>
private void PlayAlphaAndScale(UIAppearType type,bool back)
{
duration -= Time.deltaTime;
if (duration <= 0)
{
playRate = back ? 0 : 1;
Finish();
}
else
{
playRate += Time.deltaTime / animTime * (back ? -1 : 1);
}
if (type == UIAppearType.Alpha || type == UIAppearType.AlphaAndAnimation || type == UIAppearType.ScaleAndAlpha)
{
canvasGroup.alpha = Mathf.Lerp(0, 1, playRate);
}
if (type == UIAppearType.Scale || type == UIAppearType.ScaleAndAlpha)
{
Target.localScale = Vector3.Lerp(Vector3.zero, Vector3.one, playRate);
}
}
private void Finish()
{
callback?.Invoke();
callback = null;
viewType = ViewType.None;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5535a9c33d7602647adf310b63649cb5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Unity.VisualScripting;
using UnityEngine;
using YooAsset;
namespace SkierFramework
{
public class UIViewController
{
// 配置
public string uiType;
public string uiPath;
public Type uiViewType;
public UILayerLogic uiLayer;
public bool isWindow;
public UIView uiView;
public UIViewAnim uiViewAnim;
public bool isLoading = false;
public bool isOpen = false;
public bool isPause = false;
public int order;
/// <summary>
/// 在我上面的界面(非窗口界面)的数量
/// </summary>
public int topViewNum;
public async UniTask Load(object userData = null, Action callback = null)
{
isLoading = true;
if (isOpen)
{
order = uiLayer.PopOrder(this);
}
GameObject go = await ResourceManager.Instance.GetResourceCore(UIConfig.UIPackageName)
.InstantiateAsync(uiPath);
if (!isLoading)
{
ResourceManager.Instance.GetResourceCore(UIConfig.UIPackageName).Recycle(go);
callback?.Invoke();
Release();
return;
}
isLoading = false;
uiView = (UIView)go.GetOrAddComponent(uiViewType);
uiViewAnim = go.GetComponent<UIViewAnim>();
uiView.transform.SetParentEx(uiLayer.canvas.transform);
RectTransform rectTransform = uiView.transform as RectTransform;
switch (UIManager.Instance.GetUIBlackType())
{
case UIBlackType.None:
// 全适配
rectTransform.SetAnchor(AnchorPresets.StretchAll);
rectTransform.anchoredPosition = Vector2.zero;
rectTransform.sizeDelta = Vector2.zero;
break;
case UIBlackType.Height:
// 保持高度填满,两边留黑边
rectTransform.SetAnchor(AnchorPresets.VertStretchCenter);
rectTransform.anchoredPosition = Vector2.zero;
rectTransform.sizeDelta = new Vector2(UIManager.Instance.width, 0);
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal,
UIManager.Instance.width);
break;
case UIBlackType.Width:
// 保持宽度填满,上下留黑边
rectTransform.SetAnchor(AnchorPresets.HorStretchMiddle);
rectTransform.anchoredPosition = Vector2.zero;
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical,
UIManager.Instance.height);
rectTransform.sizeDelta = new Vector2(0, UIManager.Instance.height);
break;
}
uiView.OnInit(go.GetComponent<UIControlData>(), this);
uiView.transform.SetAsLastSibling();
if (isOpen)
{
Open(userData, callback, true);
}
else
{
Close(callback);
}
}
public void Open(object userData = null, Action callback = null, bool isFirstOpen = false)
{
isOpen = true;
if (isLoading) return;
if (uiView == null)
{
Load(userData, callback);
}
else
{
if (!isFirstOpen && isOpen && order > 0)
{
TrueClose();
}
TrueOpen(userData, callback);
if (uiViewAnim != null)
{
uiViewAnim.Open();
}
}
}
public void Close(Action callback = null)
{
isOpen = false;
if (isLoading) return;
if (uiView != null)
{
if (uiViewAnim != null)
{
uiViewAnim.Close(() => { TrueClose(callback); });
}
else
{
TrueClose(callback);
}
}
}
public void Release()
{
if (uiView != null)
{
if (isOpen)
TrueClose();
uiView.OnRelease();
GameObject.Destroy(uiView.gameObject);
}
uiView = null;
uiViewAnim = null;
isLoading = false;
isOpen = false;
order = 0;
}
private void TrueOpen(object userData = null, Action callback = null)
{
uiLayer.OpenUI(this);
SetVisible(true);
// 刷新一下显示
AddTopViewNum(0);
uiView.OnOpen(userData);
uiView.OnResume();
callback?.Invoke();
}
private void TrueClose(Action callback = null)
{
uiLayer.CloseUI(this);
// 刷新一下显示
AddTopViewNum(-100000);
SetVisible(false);
uiView.OnPause();
uiView.OnClose();
callback?.Invoke();
}
public void SetVisible(bool visible)
{
if (uiView != null)
{
uiView.gameObject.SetActive(visible);
}
}
public void AddTopViewNum(int num)
{
topViewNum += num;
topViewNum = Mathf.Max(0, topViewNum);
SetVisible(topViewNum <= 0);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b3d89571e6c129d40ab1abae7299540b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: