using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using TinaX.Services;
using TinaX.Systems.Pipeline;
using TinaX.UIKit.Const;
using TinaX.UIKit.Entity;
using TinaX.UIKit.Internal;
using TinaX.UIKit.Pipelines.OpenUI;
using TinaX.UIKit.Router;
using TinaX.XComponent;
using UniRx;
using UnityEngine;
using UnityEngine.UI;
namespace TinaX.UIKit
{
public class UIManager : IUIKit, IUIKitInternal
{
[Inject]
public IAssetService Assets { get; set; }
[Inject]
public IXCore m_Core { get; set; }
private UIConfig mConfig;
//private UIGroup mCurUIGroup;
//private UINameMode mUINameMode;
//private string mUIRootDirLoadPath;
//private string mUIRootDirLoadPath_withSlash;
///
/// UIName到加载地址的路由接口
///
private IRouter m_Router;
private bool mInited = false;
private GameObject mUIKit_Root_Go;
//private Canvas mScreenUIRoot_Canvas;
private Camera mScreenUICamera;
private Dictionary mDict_UIRoot_SortingLayer_Go = new Dictionary();
private Dictionary mDict_UIRootCanvas_SortingLayer_Canvas = new Dictionary();
///
/// Key: SortingLayer
///
private Dictionary mDict_UILayers = new Dictionary();
private UIEntityManager UIEntities = new UIEntityManager();
#region Pipelines
///
/// 执行“异步打开UI”流程时候的管线
///
private readonly XPipeline m_OpenUIAsyncPipeline = new XPipeline();
///
/// 执行“同步打开UI”流程时候的管线
///
private readonly XPipeline m_OpenUIPipeline = new XPipeline();
#endregion
public Camera UICamera => mScreenUICamera;
public XPipeline OpenUIAsyncPipeline => m_OpenUIAsyncPipeline;
public XPipeline OpenUIPipeline => m_OpenUIPipeline;
public UIManager()
{
//Pipelines 初始化
this.OpenUIAsyncConfigure(ref m_OpenUIAsyncPipeline);
this.OpenUIConfigure(ref m_OpenUIPipeline);
}
public async Task Start()
{
#region config
if (mInited) return null;
mConfig = XConfig.GetConfig(UIConst.ConfigPath_Resources);
if (mConfig == null)
return new XException("[TinaX.UIKit] Connot found config file.");
if (!mConfig.EnableUIKit) return null;
if(!m_Core.Services.TryGet(out m_Router))
{
//没有发现开发者外部注入的路由,所以使用内置路由
switch (mConfig.UINameMode)
{
default:
throw new UIKitException("Unknow UI Name Mode: " + mConfig.UINameMode.ToString(), UIKitErrorCode.Unknow);
case UINameMode.UIGroup:
m_Router = new UIGroupRouter(mConfig.DefaultUIGroup);
break;
case UINameMode.RelativeDirectory:
m_Router = new RelativeDirectoryRouter(mConfig.UIRootDirectoryLoadPath);
break;
}
}
//if (mConfig.UINameMode == UINameMode.UIGroup)
// mCurUIGroup = mConfig.DefaultUIGroup;
//else
//{
// mUIRootDirLoadPath = mConfig.UIRootDirectoryLoadPath;
// if (!mUIRootDirLoadPath.IsNullOrEmpty())
// {
// if (mUIRootDirLoadPath.EndsWith("/"))
// mUIRootDirLoadPath = mUIRootDirLoadPath.Substring(0, mUIRootDirLoadPath.Length - 1);
// mUIRootDirLoadPath_withSlash = mUIRootDirLoadPath + "/";
// }
//}
//mUINameMode = mConfig.UINameMode;
#endregion
//Init UIKit GameObjects
#region UIKit GameObjects
mUIKit_Root_Go = XCore.GetMainInstance().BaseGameObject
.FindOrCreateGameObject("UIKit")
.SetPosition(new Vector3(-9999, -9999, -9999));
if (mConfig.UseUICamera)
{
var camera_config = mConfig.UICameraConfig;
if (camera_config == null)
camera_config = new UICameraConfig();
mScreenUICamera = mUIKit_Root_Go.FindOrCreateGameObject("UICamera")
.AddComponent();
mScreenUICamera.clearFlags = camera_config.clearFlags;
mScreenUICamera.backgroundColor = camera_config.backgroundColor;
mScreenUICamera.cullingMask = camera_config.cullingMask;
mScreenUICamera.orthographic = camera_config.orthographic;
mScreenUICamera.orthographicSize = camera_config.orthographicSize;
mScreenUICamera.nearClipPlane = camera_config.nearClipPlane;
mScreenUICamera.farClipPlane = camera_config.farClipPlane;
mScreenUICamera.depth = camera_config.depth;
mScreenUICamera.renderingPath = camera_config.renderingPath;
mScreenUICamera.targetTexture = camera_config.targetTexture;
mScreenUICamera.useOcclusionCulling = camera_config.useOcclusionCulling;
mScreenUICamera.allowHDR = camera_config.allowHDR;
mScreenUICamera.allowMSAA = camera_config.allowMSAA;
}
//Default UIRoot
refreshUIRoot(0);
//EventSystem
#if ENABLE_LEGACY_INPUT_MANAGER
if (mConfig.AutoCreateEventSystem)
{
var es_go = GameObjectHelper.FindOrCreateGameObject("EventSystem");
var event_system = es_go.GetComponentOrAdd();
event_system.sendNavigationEvents = true;
event_system.pixelDragThreshold = 10;
var input_module = es_go.GetComponentOrAdd();
if(mConfig.DontDestroyEventSystem)
{
es_go.DontDestroy();
}
}
#endif
#endregion
await Task.Yield();
return null;
}
#region OpenUI方法
//------------UIName 和 参数 -----------------------------------------------------------
public IUIEntity OpenUI(string UIName, params object[] args)
{
var entity = this._PipelineOpenUI(uiName: UIName,
useMask: false,
closeByMask: false,
maskColor: null,
uiRoot: null,
xBehaviour: null,
DI: true,
openUIParam: null,
args);
return entity;
}
public async Task OpenUIAsync(string UIName, params object[] args)
{
var entity = await _PipelineOpenUIAsync(uiName: UIName,
useMask: false,
closeByMask: false,
maskColor: null,
uiRoot: null,
xBehaviour: null,
DI: true,
openUIParam: null,
args);
return entity;
}
public void OpenUIAsync(string UIName, Action callback, params object[] args)
{
this.OpenUIAsync(UIName, args)
.ToObservable()
.ObserveOnMainThread()
.Subscribe(entity =>
{
callback?.Invoke(entity, null);
},
e =>
{
if (e is XException)
callback?.Invoke(null, e as XException);
else
Debug.LogException(e);
});
}
//------------------------------------------------------------------------------------------------------
public IUIEntity OpenUI(string UIName, XBehaviour behaviour, params object[] args)
{
return this._PipelineOpenUI(uiName: UIName,
useMask: false,
closeByMask: false,
maskColor: null,
uiRoot: null,
xBehaviour: behaviour,
DI: true,
openUIParam: null,
args);
}
public IUIEntity OpenUI(string UIName, XBehaviour behaviour, OpenUIParam openUIParam, params object[] args)
{
//有oepnUIParam的话,优先使用这个
if (openUIParam.UIName.IsNullOrEmpty())
openUIParam.UIName = UIName;
if (openUIParam.xBehaviour == null)
openUIParam.xBehaviour = behaviour;
return this._PipelineOpenUI(uiName: default,
useMask: default,
closeByMask: default,
maskColor: default,
uiRoot: default,
xBehaviour: default,
DI: default,
openUIParam: openUIParam,
args);
}
public async Task OpenUIAsync(string UIName, XBehaviour behaviour, params object[] args)
{
//尝试基于Pipeline的新写法
var entity = await _PipelineOpenUIAsync(uiName: UIName,
useMask: false,
closeByMask: false,
maskColor: null,
uiRoot: null,
xBehaviour: behaviour,
DI: true,
openUIParam: null,
args);
return entity;
}
public async Task OpenUIAsync(string UIName, XBehaviour behaviour, OpenUIParam openUIParam, params object[] args)
{
//有oepnUIParam的话,优先使用这个
if (openUIParam.UIName.IsNullOrEmpty())
openUIParam.UIName = UIName;
if (openUIParam.xBehaviour == null)
openUIParam.xBehaviour = behaviour;
var entity = await _PipelineOpenUIAsync(uiName: default,
useMask: default,
closeByMask: default,
maskColor: default,
uiRoot: default,
xBehaviour: default,
DI: default,
openUIParam: openUIParam,
args);
return entity;
}
public void OpenUIAsync(string UIName, XBehaviour behaviour, Action callback, params object[] args)
{
this.OpenUIAsync(UIName, behaviour, args)
.ToObservable()
.ObserveOnMainThread()
.Subscribe(entity =>
{
callback?.Invoke(entity, null);
},
e =>
{
if (e is XException)
callback?.Invoke(null, e as XException);
else
Debug.LogException(e);
});
}
public void OpenUIAsync(string UIName, XBehaviour behaviour, OpenUIParam openUIParam, Action callback, params object[] args)
{
this.OpenUIAsync(UIName, behaviour, openUIParam, args)
.ToObservable()
.ObserveOnMainThread()
.Subscribe(entity =>
{
callback?.Invoke(entity, null);
},
e =>
{
if (e is XException)
callback?.Invoke(null, e as XException);
else
Debug.LogException(e);
});
}
//------------全都有-------------------------------------------------------------------------------------
public IUIEntity OpenUI(string UIName, OpenUIParam openUIParam, params object[] args)
{
//有oepnUIParam的话,优先使用这个
if (openUIParam.UIName.IsNullOrEmpty())
openUIParam.UIName = UIName;
return this._PipelineOpenUI(uiName: default,
useMask: default,
closeByMask: default,
maskColor: default,
uiRoot: default,
xBehaviour: default,
DI: default,
openUIParam: openUIParam,
args);
}
public IUIEntity OpenUIWithParam(string UIName, OpenUIParam openUIParam, params object[] args) => OpenUI(UIName, openUIParam, args);
public async Task OpenUIAsync(string UIName, OpenUIParam openUIParam, params object[] args)
{
if (openUIParam.UIName.IsNullOrEmpty())
openUIParam.UIName = UIName;
var entity = await _PipelineOpenUIAsync(uiName: default,
useMask: default,
closeByMask: default,
maskColor: default,
uiRoot: default,
xBehaviour: default,
DI: default,
openUIParam: openUIParam,
args);
return entity;
}
public void OpenUIAsync(string UIName, OpenUIParam openUIParam, Action callback, params object[] args)
{
this.OpenUIAsync(UIName, openUIParam, args)
.ToObservable()
.ObserveOnMainThread()
.Subscribe(
entity =>
{
callback?.Invoke(entity, null);
},
e =>
{
if (e is XException)
callback?.Invoke(null, e as XException);
else
Debug.LogException(e);
});
}
public void OpenUIWithParamAsync(string UIName, OpenUIParam openUIParam, Action callback, params object[] args) => OpenUIAsync(UIName, openUIParam, callback, args);
#endregion
public void CloseUI(UIEntity entity , params object[] args)
{
if (entity == null)
return;
if (entity.Closed)
return;
this.closeUI(entity, args);
}
public void CloseUI(string UIName, params object[] args)
{
//string ui_path = null;
//if (mUINameMode == UINameMode.UIGroup)
//{
// if (mCurUIGroup != null)
// {
// if (!mCurUIGroup.TryGetPath(UIName, out ui_path))
// throw new UIKitException("[TinaX.UIKit] Invalid UIName : " + UIName, UIKitErrorCode.InvalidUIName);
// }
//}
//else
//{
// ui_path = (mUIRootDirLoadPath.IsNullOrEmpty()) ? UIName : mUIRootDirLoadPath_withSlash + UIName;
//}
//if (ui_path.IsNullOrEmpty())
//{
// throw new UIKitException("[TinaX.UIKit] " + (IsChinese ? $"未能获取到UI \"{UIName}\" 的加载路径,请检查设置或传入参数" : $"Cannot get UI Path by UI Name \"{UIName}\", Please check config or args."), UIKitErrorCode.ConnotGetUIPath);
//}
//if(this.UIEntities.TryGetEntitys(ui_path, out var entities))
//{
// foreach (var item in entities)
// this.CloseUI(item, args);
//}
var entities = this.UIEntities.FindByUIName(UIName).ToArray();
if(entities != null && entities.Length > 0)
{
for (var i = entities.Length - 1; i >= 0; i--)
this.closeUI(entities[i], args);
}
}
public void SetUIRouter(IRouter router)
{
m_Router = router ?? throw new ArgumentNullException(nameof(router));
}
///
/// [新] Pipeline实现版本私有方法总入口
///
///
///
///
///
///
///
/// 启用依赖注入
///
///
///
private async Task _PipelineOpenUIAsync(string uiName, //广义上的UIName, 可能是UI Group里的Name,也有可能是别的命名方式定义的UI Name,甚至直接是路径
bool useMask,
bool closeByMask,
Color? maskColor,
Transform uiRoot,
XBehaviour xBehaviour,
bool DI,
OpenUIParam openUIParam,
object[] args)
{
OpenUIPayload payload = new OpenUIPayload();
if(openUIParam != null) //openUIParam里的参数优先级大于零散参数
{
//要是Unity有AutoMapper用就好了, 先手动抄一遍吧,以后想想看有啥好方法优化一下
payload.UIName = openUIParam.UIName;
payload.UseMask = openUIParam.UseMask;
payload.CloseByMask = openUIParam.CloseByMask;
payload.MaskColor = openUIParam.MaskColor;
payload.UIRoot = openUIParam.UIRoot;
payload.xBehaviour = openUIParam.xBehaviour;
payload.DependencyInjection = openUIParam.DependencyInjection;
}
else
{
payload.UIName = uiName;
payload.UseMask = useMask;
payload.CloseByMask = closeByMask;
payload.MaskColor = maskColor;
payload.UIRoot = uiRoot;
payload.xBehaviour = xBehaviour;
payload.DependencyInjection = DI;
}
payload.OpenUIArgs = args;
//挨个跑一下Pipeline
await m_OpenUIAsyncPipeline.StartAsync((handler, next) =>
{
return handler.OpenUIAsync(payload, next);
});
//然后照理说,Pipeline中会把payload里的UIEntity赋值,我们就不用管了
return payload.UIEntity;
}
private UIEntity _PipelineOpenUI(string uiName, //广义上的UIName, 可能是UI Group里的Name,也有可能是别的命名方式定义的UI Name,甚至直接是路径
bool useMask,
bool closeByMask,
Color? maskColor,
Transform uiRoot,
XBehaviour xBehaviour,
bool DI,
OpenUIParam openUIParam,
object[] args)
{
OpenUIPayload payload = new OpenUIPayload();
if (openUIParam != null) //openUIParam里的参数优先级大于零散参数
{
//要是Unity有AutoMapper用就好了, 先手动抄一遍吧,以后想想看有啥好方法优化一下
payload.UIName = openUIParam.UIName;
payload.UseMask = openUIParam.UseMask;
payload.CloseByMask = openUIParam.CloseByMask;
payload.MaskColor = openUIParam.MaskColor;
payload.UIRoot = openUIParam.UIRoot;
payload.xBehaviour = openUIParam.xBehaviour;
payload.DependencyInjection = openUIParam.DependencyInjection;
}
else
{
payload.UIName = uiName;
payload.UseMask = useMask;
payload.CloseByMask = closeByMask;
payload.MaskColor = maskColor;
payload.UIRoot = uiRoot;
payload.xBehaviour = xBehaviour;
payload.DependencyInjection = DI;
}
payload.OpenUIArgs = args;
//挨个跑一下Pipeline
m_OpenUIPipeline.Start((handler, next) =>
{
return handler.OpenUI(ref payload, next);
});
//然后照理说,Pipeline中会把payload里的UIEntity赋值,我们就不用管了
return payload.UIEntity;
}
///
/// 关闭UI 私有方法总入口
///
///
///
private void closeUI(UIEntity entity, params object[] args)
{
//退还layer
if(mDict_UILayers.TryGetValue(entity.SortingLayerValue,out var layer))
{
layer.Remove(entity);
}
//移除登记
UIEntities.Remove(entity);
//传递参数
if (args != null && args.Length>0)
entity.UIPage.SendCloseUIMessage(args);
//
entity.Dispose();
entity = null;
}
private Transform getScreenUIRoot(int sortingLayerId)
{
if (mDict_UIRoot_SortingLayer_Go.TryGetValue(sortingLayerId, out var go))
return go.transform;
else
{
refreshUIRoot(sortingLayerId);
return mDict_UIRoot_SortingLayer_Go[sortingLayerId].transform;
}
}
private void refreshUIRoot(int sortingLayerId)
{
if (SortingLayer.IsValid(sortingLayerId))
{
GameObject ui_root_go;
if (!mDict_UIRoot_SortingLayer_Go.TryGetValue(sortingLayerId, out ui_root_go))
{
ui_root_go = mUIKit_Root_Go.FindOrCreateGameObject("UIRoot_" + sortingLayerId,
typeof(Canvas),
typeof(CanvasScaler),
typeof(GraphicRaycaster))
.SetLocalPosition(Vector3.zero);
mDict_UIRoot_SortingLayer_Go.Add(sortingLayerId, ui_root_go);
}
var canvas = ui_root_go.GetComponentOrAdd