2026-02-12 01:24:40 +08:00

468 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}
}
}