468 lines
13 KiB
C#
468 lines
13 KiB
C#
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using TH1_Core.Managers;
|
||
using TH1_UI;
|
||
using TH1_UI.Core;
|
||
using TH1_UI.View.Base;
|
||
using UnityEngine;
|
||
|
||
// 这是你原有的代码,为了完整性我把它折叠起来了,无需修改
|
||
#region Unchanged Interfaces and Usings
|
||
namespace TH1_UI.Controller.Base
|
||
{
|
||
|
||
public interface IViewControllerInterface
|
||
{
|
||
ViewDestroyDomain Domain { get; }
|
||
ViewLevelValue ViewLevel { get; }
|
||
ViewDepthProperty DepthProperty { get; }
|
||
ViewOpenProperty OpenProperty { get; }
|
||
ViewCloseProperty CloseProperty { get; }
|
||
ViewLoadProperty LoadProperty { get; }
|
||
string Name { get; }
|
||
int Depth { get; set; }
|
||
Action OnClosedCallback { get; set; }
|
||
void Start(string subFolderPath, string resourceName, ViewDestroyDomain domain,UIRootType rootType);
|
||
bool Open();
|
||
void UpdateView();
|
||
bool IsShow();
|
||
bool OpenWithParam(object param);
|
||
bool Close();
|
||
void Destroy();
|
||
}
|
||
#endregion
|
||
|
||
public class ViewController<T> : IViewControllerInterface where T : View.Base.View
|
||
{
|
||
protected object _openParameter;
|
||
public ViewDestroyDomain Domain => _domain;
|
||
public Action OnClosedCallback { get; set; }
|
||
public ViewDepthProperty DepthProperty => _depthProperty;
|
||
public ViewCloseProperty CloseProperty { get; } = new ViewCloseProperty();
|
||
|
||
public ViewOpenProperty OpenProperty { get; } = new ViewOpenProperty();
|
||
|
||
public ViewLoadProperty LoadProperty => _loadProperty;
|
||
protected ViewHierarchyProperty HierarchyProperty { get; } = new ViewHierarchyProperty();
|
||
|
||
protected ViewCameraProperty CameraProperty { get; } = new ViewCameraProperty();
|
||
|
||
protected ViewMaskProperty MaskProperty { get; } = new ViewMaskProperty();
|
||
|
||
|
||
public ViewLevelValue ViewLevel => HierarchyProperty.ViewLevel;
|
||
public string Name => _loadProperty.ControllerName;
|
||
|
||
public int Depth
|
||
{
|
||
get => _depthProperty.Depth;
|
||
set
|
||
{
|
||
if (IsShow())
|
||
{
|
||
_script.SetSortingOrder(value);
|
||
}
|
||
}
|
||
}
|
||
public T WindowScript => _script;
|
||
|
||
public GameObject WindowObj => _windowObj;
|
||
|
||
public virtual void OnMatchStart()
|
||
{
|
||
|
||
}
|
||
|
||
public virtual void AfterMatchStart()
|
||
{
|
||
|
||
}
|
||
|
||
protected void AddLoadAtlas(string name)
|
||
{
|
||
UIResourceController.Instance.AddAtlasHolder(name, LoadProperty.ControllerName);
|
||
if (!_atlasLoadList.Contains(name))
|
||
{
|
||
_atlasLoadList.Add(name);
|
||
}
|
||
}
|
||
|
||
public virtual void Start(string subFolderPath, string resourceName, ViewDestroyDomain domain,UIRootType rootType)
|
||
{
|
||
//step #1 初始化,处理销毁域
|
||
_domain = domain;
|
||
//step #2 处理loadProperty相关的参数以供prefab加载
|
||
LoadProperty.SubFolderPath = subFolderPath;
|
||
LoadProperty.ResourceName = resourceName;
|
||
LoadProperty.ControllerName = this.GetType().Name;
|
||
LoadProperty.UIRootType = rootType;
|
||
//step #3 处理深度、层级和camra,目前全部架空了,没用,仅保留步骤供将来使用。深度目前使用UIRootType方案来替代(分组了)
|
||
DepthProperty.Start(this);
|
||
HierarchyProperty.Start(this);
|
||
CameraProperty.ThisController = this;
|
||
}
|
||
public virtual bool Open()
|
||
{
|
||
|
||
_atlasPreloadingValue.Clear();
|
||
if (EnableOpen())
|
||
{
|
||
_TryOpen();
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
//带参数open,目前架空了
|
||
public virtual bool OpenWithParam(object param)
|
||
{
|
||
_openParameter = param;
|
||
return Open();
|
||
}
|
||
|
||
|
||
//判断能否打开。目前架空了
|
||
protected virtual bool EnableOpen()
|
||
{
|
||
return true;
|
||
}
|
||
|
||
bool _TryOpen()
|
||
{
|
||
//step #1 如果已经在显示了,那么更新数据就行 return false
|
||
if (IsShow())
|
||
{
|
||
UpdateView();
|
||
return false;
|
||
}
|
||
|
||
// step #2 预加载 目前架空了,一定不会走预加载
|
||
bool preLoadWait = _PreLoad();
|
||
if (preLoadWait)
|
||
{
|
||
_atlasPreloadingValue.Add("PreLoad", 30, true);
|
||
}
|
||
|
||
// step #3 如果找不到窗口 ,那么要先create对应的prefab
|
||
if (WindowObj == null)
|
||
{
|
||
// 现在的 GetWindow 只负责查找,Key 使用 ResourceName
|
||
GameObject window = UIResourceController.Instance.GetWindow(LoadProperty.ResourceName);
|
||
if (window == null)
|
||
{
|
||
// 如果找不到,就启动我们改造后的 ViewLoadProperty 去加载
|
||
if (LoadProperty.LoadState == LoadWindowState.NONE)
|
||
{
|
||
//LoadProperty.Attach(_loadViewCallback, _OnViewLoadedCallback);
|
||
LoadProperty.Start(_OnViewLoadedCallback);
|
||
//_OnViewLoadedCallback回调触发的时候会触发_OnViewStart
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 如果找到了,说明是之前加载过的,直接开始后续流程
|
||
LoadProperty.Invoke();
|
||
_OnWindowLoadNotify(window);
|
||
_OnViewStart();
|
||
}
|
||
}
|
||
// step #4 如果已经有窗口可用 ,直接开干
|
||
else
|
||
{
|
||
_OnViewStart();
|
||
}
|
||
return true;
|
||
}
|
||
|
||
protected virtual bool _PreLoad()
|
||
{
|
||
//通知所有子界面也做好preload的准备
|
||
_PreLoadSubViewController();
|
||
//加载所有图集资源 ,不过现在我们不用,默认返回false
|
||
return UIResourceController.Instance.LoadAtlasList(_atlasLoadList, _OnCompletePreLoad);
|
||
}
|
||
|
||
void _OnCompletePreLoad()
|
||
{
|
||
if (_atlasPreloadingValue.Has("PreLoad"))
|
||
{
|
||
_atlasPreloadingValue.Del("PreLoad");
|
||
_OnViewStart();
|
||
}
|
||
}
|
||
|
||
void _OnViewLoadedCallback()//UInt32 eveID) 原来传入一个eveId,现在我干掉了
|
||
{
|
||
//在异步(现在是同步)回调中,同样使用新的 GetWindow 方法
|
||
GameObject window = UIResourceController.Instance.GetWindow(LoadProperty.ResourceName);
|
||
_OnWindowLoadNotify(window);
|
||
_OnViewStart();
|
||
}
|
||
|
||
//window加载完了通知用的
|
||
void _OnWindowLoadNotify(System.Object obj)
|
||
{
|
||
// step #1 将obj设为当前viewController派生类的_windowObj窗口对象
|
||
_windowObj = obj as GameObject;
|
||
if (_windowObj == null)
|
||
{
|
||
// 为了兼容性,暂时注释掉需要ViDebuger的代码
|
||
// ViDebuger.Error("Window GameObject is NULL! [res: " + LoadProperty.ResourceName + "]");
|
||
Debug.LogError("Window GameObject is NULL! [res: " + LoadProperty.ResourceName + "]");
|
||
return;
|
||
}
|
||
|
||
// step #2 获取子节点的UIWindowProperty对象
|
||
UIWindowProperty propertyScript = _windowObj?.GetComponentInChildren<UIWindowProperty>();
|
||
if (propertyScript == null)
|
||
{
|
||
// ViDebuger.Error(WindowObj.name + " Script is null, Then Initializtion error!");
|
||
Debug.LogError(WindowObj.name + " Script is null, Then Initializtion error!");
|
||
}
|
||
|
||
// step #3 获取对应的View派生类的对象,设置好_script
|
||
_script = _windowObj?.GetComponent<T>();
|
||
if (_script == null)
|
||
{
|
||
_script = _windowObj?.AddComponent<T>();
|
||
}
|
||
_script?.Initialize(propertyScript);
|
||
|
||
|
||
// step #4 加载完毕,触发OnLoaded
|
||
OnLoaded();
|
||
|
||
//step #5 依次处理subview的情况,目前架空,没有subview的情景
|
||
_LoadSubViewController(_windowObj);
|
||
}
|
||
|
||
void _OnViewStart()
|
||
{
|
||
if (WindowObj != null && WindowScript != null && !_atlasPreloadingValue.Value)
|
||
{
|
||
_OnOpenWindowNotify(WindowObj);
|
||
}
|
||
}
|
||
|
||
void _OnOpenWindowNotify(System.Object obj)
|
||
{
|
||
_TrueOpen();
|
||
}
|
||
|
||
void _TrueOpen()
|
||
{
|
||
if (_script != null)
|
||
{
|
||
_script.SetEnableClick(MaskProperty.EnableClick);
|
||
_script.Show();
|
||
}
|
||
DepthProperty.Open();
|
||
RegisterEventCallback();
|
||
OpenProperty.Execute();
|
||
HierarchyProperty.Open();
|
||
CameraProperty.Open();
|
||
OnOpen();
|
||
_OpenSubViewController();
|
||
OnOpenSub();
|
||
}
|
||
|
||
|
||
|
||
void _TrueClose()
|
||
{
|
||
// 这些是需要在动画开始前就执行的逻辑
|
||
UnregisterEventCallback();
|
||
OnClose();
|
||
_CloseSubViewController();
|
||
DepthProperty.Close();
|
||
CameraProperty.Close();
|
||
_openParameter = null;
|
||
// 定义一个 Action,它包含了所有必须在动画播放完毕后才执行的逻辑
|
||
Action postHideAction = () =>
|
||
{
|
||
// 添加调试日志
|
||
//Debug.Log($"[VIEW] '{Name}' hide animation complete. Preparing to invoke OnClosedCallback.");
|
||
//Debug.Log($"[VIEW] Before Invoke, OnClosedCallback is: '{(object)OnClosedCallback ?? "null"}'.");
|
||
|
||
HierarchyProperty.Close();
|
||
CloseProperty.Execute();
|
||
OnClosedCallback?.Invoke();
|
||
//血的教训:再这里会设置下一个OnClosedCallback,结果下一行直接给 = null了,白设置。这里不应该清空OnClosedCallback的!!!
|
||
//OnClosedCallback = null;
|
||
};
|
||
// 如果 View 脚本存在,就调用带回调的 Hide 方法
|
||
if (_script != null)
|
||
{
|
||
// 把清理逻辑作为回调传进去,这样 View 就会在动画播放完后调用它
|
||
// 添加调试日志
|
||
//Debug.Log($"[VIEW] '{Name}' calling _script.Hide() with callback.");
|
||
|
||
_script.Hide(postHideAction);
|
||
}
|
||
else
|
||
{
|
||
// 如果 View 脚本不存在(例如,窗口还未加载完就被关闭),
|
||
// 就没有动画可播,直接执行清理逻辑。
|
||
//Debug.Log($"[VIEW] '{Name}' script is null, executing postHideAction immediately.");
|
||
postHideAction();
|
||
}
|
||
}
|
||
|
||
|
||
public virtual bool Close()
|
||
{
|
||
_loadViewCallback.End();
|
||
_atlasPreloadingValue.Clear();
|
||
if (IsShow())
|
||
{
|
||
_ReleasePreLoad();
|
||
_TrueClose();
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
_atlasLoadList.Clear();
|
||
if (LoadProperty.LoadState == LoadWindowState.LOADING)
|
||
{
|
||
_loadViewCallback.End();
|
||
LoadProperty.LoadState = LoadWindowState.NONE;
|
||
}
|
||
else if (LoadProperty.LoadState == LoadWindowState.LOADED)
|
||
{
|
||
_loadViewCallback.End();
|
||
}
|
||
return true;
|
||
}
|
||
}
|
||
protected bool _ReleasePreLoad()
|
||
{
|
||
for (int iter = 0; iter < _atlasLoadList.Count; ++iter)
|
||
{
|
||
string iterAtlas = _atlasLoadList[iter];
|
||
if (UIResourceController.Instance.DelAtlasHolder(iterAtlas, LoadProperty.ControllerName))
|
||
{
|
||
UIResourceController.Instance.ReleaseAtlas(iterAtlas);
|
||
}
|
||
}
|
||
_atlasLoadList.Clear();
|
||
return true;
|
||
}
|
||
|
||
public virtual void Destroy()
|
||
{
|
||
Close();
|
||
_ReleasePreLoad();
|
||
_DestroySubViewController();
|
||
|
||
// ✅ 核心修改 #3: 删除窗口时,使用的key必须和获取/添加窗口时一致,即 ResourceName
|
||
UIResourceController.Instance.DelWindow(LoadProperty.ResourceName);
|
||
if (_windowObj != null)
|
||
{
|
||
// GameObject.Destroy() 已经是 UIResourceController.DelWindow 的一部分了
|
||
// 所以这里不需要再 Destroy,只需要将引用置空
|
||
_windowObj = null;
|
||
}
|
||
|
||
_script = null;
|
||
LoadProperty.End();
|
||
}
|
||
|
||
public virtual bool IsShow()
|
||
{
|
||
if (_script == null)
|
||
{
|
||
return false;
|
||
}
|
||
return _script.IsShow();
|
||
}
|
||
public virtual void UpdateView() { }
|
||
|
||
// ... (这里往后的代码,包括子界面管理等,都保持原样,无需改动) ...
|
||
protected virtual void OnLoaded() { }
|
||
protected virtual void OnOpen() { }
|
||
protected virtual void OnOpenSub() { }
|
||
protected virtual void RegisterEventCallback() { }
|
||
protected virtual void UnregisterEventCallback() { }
|
||
protected virtual void OnClose() { }
|
||
|
||
|
||
T _script;
|
||
ViewDestroyDomain _domain;
|
||
GameObject _windowObj;
|
||
ViAsynCallback _loadViewCallback = new ViAsynCallback(); // Vi的异步回调事件
|
||
ViewDepthProperty _depthProperty = new ViewDepthProperty();
|
||
ViewLoadProperty _loadProperty = new ViewLoadProperty();
|
||
ViPriorityValue<bool> _atlasPreloadingValue = new ViPriorityValue<bool>(false); //
|
||
List<string> _atlasLoadList = new List<string>();
|
||
Dictionary<string, ViewControllerSubInterface> _subViewDict = new Dictionary<string, ViewControllerSubInterface>();
|
||
|
||
protected T1 AddSubViewController<T1>(string childPath, string ex = null) where T1 : ViewControllerSubInterface, new()
|
||
{
|
||
var sub = new T1();
|
||
sub.Start(childPath, this);
|
||
_subViewDict.Add(typeof(T1).Name + ex, sub);
|
||
return sub;
|
||
}
|
||
public T1 GetSubViewController<T1>(string ex = null) where T1 : ViewControllerSubInterface, new()
|
||
{
|
||
_subViewDict.TryGetValue(typeof(T1).Name + ex, out var subViewController);
|
||
return (T1) subViewController;
|
||
}
|
||
void _PreLoadSubViewController()
|
||
{
|
||
foreach (var kv in _subViewDict)
|
||
{
|
||
var subViewController = kv.Value;
|
||
subViewController.PreLoad();
|
||
var loadAtlas = subViewController.GetLoadAtlas();
|
||
foreach (var atlas in loadAtlas)
|
||
{
|
||
AddLoadAtlas(atlas);
|
||
}
|
||
}
|
||
}
|
||
void _LoadSubViewController(GameObject windowObj)
|
||
{
|
||
foreach (var kv in _subViewDict)
|
||
{
|
||
kv.Value.OnWindowLoadNotify(windowObj);
|
||
}
|
||
}
|
||
void _OpenSubViewController()
|
||
{
|
||
foreach (var kv in _subViewDict)
|
||
{
|
||
if (kv.Value.DefaultOpen)
|
||
{
|
||
kv.Value.Open();
|
||
}
|
||
}
|
||
}
|
||
void _CloseSubViewController()
|
||
{
|
||
foreach (var kv in _subViewDict)
|
||
{
|
||
kv.Value.Close();
|
||
}
|
||
}
|
||
void _DestroySubViewController()
|
||
{
|
||
foreach (var kv in _subViewDict)
|
||
{
|
||
kv.Value.Destroy();
|
||
}
|
||
}
|
||
protected void CloseAllSubView()
|
||
{
|
||
foreach (var kv in _subViewDict)
|
||
{
|
||
kv.Value.Close();
|
||
}
|
||
}
|
||
}
|
||
}
|