Files
XericUIActionVessel/Runtime/DynamicActivitiesImpact/UIDynamicActiveImpact.cs
T
lrss3 60d5822a82 添加了一键替换组件的方法。
修改了xericlibrary的api。
修改了菜单路径。
2026-04-16 10:22:39 +08:00

326 lines
10 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.Serialization;
using UnityEngine.UI;
using XericLibrary.Runtime.MacroLibrary;
using XericLibrary.Runtime.Type.SpatialAlgorithm;
using XericUI.Core.Base;
using XericUI.OverrideLayout;
#if UNITY_EDITOR
using XericLibraryEditor.Debug;
#endif
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
namespace XericUI.DynamicActivitiesImpact
{
/// <summary>
/// 导入动态激活组件(需要外部控制部分检查与生命周期事件)
/// </summary>
[Obsolete("直接用XericScrollRect")]
public class UIDynamicActiveImpact : XericUIBehaciour
{
#region 字段属性
#if ODIN_INSPECTOR
[LabelText("主要容器")]
[BoxGroup("基本容器", centerLabel: true)]
#endif
[Tooltip("容器子集应呈现平摊的样式")]
public RectTransform containerTransform = null;
#if ODIN_INSPECTOR
[BoxGroup("基本容器")]
[LabelText("遮罩容器")]
#endif
[Tooltip("用作遮罩计算的组件")]
public RectTransform maskRectTransform = null;
#if ODIN_INSPECTOR
[BoxGroup("基本容器")]
[LabelText("容器排版控制")]
#endif
public LayoutGroup layoutGroup;
#if ODIN_INSPECTOR
[BoxGroup("容器性能")]
[LabelText("预加载像素范围")]
#endif
public float preloadMargin = 200f;
#if ODIN_INSPECTOR
[BoxGroup("容器性能")]
[LabelText("可被察觉的响应变化长度")]
#endif
public float discernibleDistance = 10f;
#if ODIN_INSPECTOR
[BoxGroup("容器性能")]
[LabelText("每秒检查更新的次数")]
#endif
[Range(0.1f, 50)]
public float updateRateLimit = 2;
#endregion
#region 容器成员
internal bool _layoutDirty;
internal bool _transformDirty;
internal int _lastChildrenCount;
internal Vector2 _lastPosition;
internal HashSet<RectTransform> _activeSet = null;
internal HashSet<RectTransform> _allObjsSet = null;
private HashSetPool<RectTransform> _tempQuadUIHashSetPool = new HashSetPool<RectTransform>();
/// <summary>
/// 保存所有ui对象的四叉树
/// </summary>
protected QuadTree<RectTransform> QuadUITargetTree;
public virtual IEnumerable<RectTransform> ContainerChildren => containerTransform.GetChildren().Select(a => a.RectTransform());
public virtual int ContainerChildrenCount => containerTransform.childCount;
public virtual Rect ContainerTransformRectRange => containerTransform.ToQuadTreeRect();
public virtual Rect MaskRectTransformRectRange => maskRectTransform.ToQuadTreeRect();
public bool IsLayoutDirty => _layoutDirty;
public bool IsTransformDirty => _transformDirty;
public float SaftyDiscernibleUpdateTime => Mathf.Max(1f / 60, 1f / updateRateLimit);
public LayoutGroup MainLayoutGroup => transform.GetComponent<LayoutGroup>();
#endregion
#region 生命周期
#if UNITY_EDITOR
private Vector3[] vertex1 = new Vector3[4];
private Vector3[] vertex2 = new Vector3[4];
private Vector3[] vertex3 = new Vector3[4];
private void OnDrawGizmosSelected()
{
var lossyScale = rectTransform.lossyScale;
var scale = lossyScale * preloadMargin;
var dis2 = lossyScale * discernibleDistance;
containerTransform.GetWorldCorners(vertex1);
vertex2[0] = new Vector3(vertex1[0].x - scale.x, vertex1[0].y - scale.y, vertex1[0].z);
vertex2[1] = new Vector3(vertex1[1].x - scale.x, vertex1[1].y + scale.y, vertex1[1].z);
vertex2[2] = new Vector3(vertex1[2].x + scale.x, vertex1[2].y + scale.y, vertex1[2].z);
vertex2[3] = new Vector3(vertex1[3].x + scale.x, vertex1[3].y - scale.y, vertex1[3].z);
vertex3[0] = new Vector3(vertex1[0].x - dis2.x, vertex1[0].y - dis2.y, vertex1[0].z);
vertex3[1] = new Vector3(vertex1[1].x - dis2.x, vertex1[1].y + dis2.y, vertex1[1].z);
vertex3[2] = new Vector3(vertex1[2].x + dis2.x, vertex1[2].y + dis2.y, vertex1[2].z);
vertex3[3] = new Vector3(vertex1[3].x + dis2.x, vertex1[3].y - dis2.y, vertex1[3].z);
Gizmos.color = Color.yellow;
MacroGizmosDraw.DrawPolygonLineByPointArray(vertex3);
MacroGizmosDraw.DrawGizmos2RectLinkCube(vertex1, vertex2, Color.grey, Color.cyan, true);
}
#endif
protected override void Awake()
{
base.Awake();
// 容器
if (containerTransform == null)
containerTransform = rectTransform;
// 遮罩
if (maskRectTransform == null)
{
var mask = transform.GetComponentInParent<Mask>();
maskRectTransform = mask.RectTransform();
}
// 布局
if (layoutGroup == null)
layoutGroup = transform.GetComponentInParent<LayoutGroup>();
if (layoutGroup is XericHorizontalOrVerticalLayoutGroup xericLayoutGroup)
{
xericLayoutGroup.layoutIgnoreInactive = false;
}
CheckIsDirty();
}
protected override void OnEnable()
{
base.OnEnable();
}
protected override void OnDisable()
{
base.OnDisable();
}
private float accTime = 0;
protected virtual void Update()
{
if (accTime < SaftyDiscernibleUpdateTime)
{
accTime += Time.unscaledDeltaTime;
return;
}
// 毕竟这里不是粒子计算器
accTime = accTime % SaftyDiscernibleUpdateTime;
if (IsLayoutDirty)
{
ResetDirtyFlag();
RebuildQurdTree();
}
else if (IsTransformDirty)
{
ResetDirtyFlag();
}
}
protected override void OnRectTransformDimensionsChange()
{
base.OnRectTransformDimensionsChange();
CheckIsDirty();
}
private void OnDestroy()
{
// 恢复所有元素的原始状态
foreach (var element in _allObjsSet)
{
if (element != null && _activeSet.Contains(element))
{
element.gameObject.SetActive(true);
}
}
}
#endregion
#region 刷新框架
/// <summary>
/// 设置布局脏
/// </summary>
public void LayoutDirty()
=> _layoutDirty = true;
/// <summary>
/// 设置变换脏
/// </summary>
public void TransformDirty()
=> _transformDirty = true;
/// <summary>
/// 重置脏标记
/// </summary>
private void ResetDirtyFlag()
{
_layoutDirty = false;
_transformDirty = false;
}
/// <summary>
/// 检查容器脏
/// </summary>
public virtual void CheckIsDirty()
{
if (QuadUITargetTree == null || // 四叉树不存在?
_lastChildrenCount != ContainerChildrenCount // 成员数量明显不对
)
{
LayoutDirty();
BackupQuadRectValue();
return;
}
// todo 这里暂时只简单考虑直来直去的移动方式,如果有其他复杂移动,需要修改判别条件
var deltaPosIsDirty = Mathf.Abs(QuadUITargetTree.RootRect.position.sqrMagnitude - _lastPosition.sqrMagnitude) < discernibleDistance;
if (deltaPosIsDirty // 发生不可忽视的变换?
)
{
TransformDirty();
BackupQuadRectValue();
return;
}
}
/// <summary>
/// 重建四叉树
/// </summary>
public void RebuildQurdTree()
{
// 重建四叉树
QuadUITargetTree = new QuadTree<RectTransform>(ContainerTransformRectRange, a => a.rect);
// 记录参数
BackupQuadRectValue();
}
/// <summary>
/// 保存自动重建四叉树所需的参数
/// </summary>
/// <param name="target"></param>
protected virtual void BackupQuadRectValue()
{
_lastPosition = ContainerTransformRectRange.position;
_lastChildrenCount = ContainerChildrenCount;
}
#if ODIN_INSPECTOR
[Button("更新所有可见性")]
#endif
/// <summary>
/// 更新所有成员的可见性
/// </summary>
public virtual void UpdateAllObjectVisible()
{
if (QuadUITargetTree == null)
{
Debug.LogError($"在更新成员可见性之前,需要确保四叉树存在!");
return;
}
var visibalSet = QuadUITargetTree.Retrieve(MaskRectTransformRectRange);
ContainerChildren.ForEachDo(obj =>
{
obj.gameObject.SetActive(visibalSet.Contains(obj));
});
}
/// <summary>
/// 要求控制布局组的激活一次,提供为何激活的原因
/// </summary>
/// <param name="hasIncreased">由于新增成员导致布局更新</param>
/// <param name="hasDecreased">由于销毁成员导致布局更新</param>
public virtual void UpdateLayoutGroupOnce(bool hasIncreased, bool hasDecreased)
{
MainLayoutGroup.enabled = true;
StartCoroutine(WaitForLayoutDisable());
return;
IEnumerator WaitForLayoutDisable()
{
if (CanvasUpdateRegistry.IsRebuildingLayout())
yield return new WaitForEndOfFrame();
else
yield return new WaitForFixedUpdate();
MainLayoutGroup.enabled = false;
}
}
#endregion
}
}