123 lines
4.3 KiB
C#
123 lines
4.3 KiB
C#
using UnityEngine;
|
||
|
||
namespace XericLibrary.Runtime.Blueprint.Canvas
|
||
{
|
||
/// <summary>
|
||
/// 屏幕空间蓝图画布实现,基于 UGUI Canvas。
|
||
/// <para>
|
||
/// 所有坐标转换统一通过 <see cref="RectTransformUtility"/> 完成,
|
||
/// 以 <see cref="_renderRoot"/> RectTransform 为坐标基准,
|
||
/// 不依赖蓝图自身的 RectTransform 是否撑满全屏。
|
||
/// </para>
|
||
/// <para>
|
||
/// 支持 ScreenSpaceOverlay / ScreenSpaceCamera / WorldSpace 三种渲染模式。
|
||
/// 由外层的 Mask(__Bp_GridBackground)裁剪,确保蓝图节点不溢出视图区域。
|
||
/// 缩放范围:0.1x ~ 3x。
|
||
/// </para>
|
||
/// </summary>
|
||
public class ScreenSpaceBlueprintCanvas : BlueprintCanvasBase
|
||
{
|
||
private readonly UnityEngine.Canvas _unityCanvas;
|
||
private readonly RectTransform _renderRoot;
|
||
private readonly RectTransform _viewport;
|
||
|
||
/// <summary>动态获取摄像机引用(不缓存),
|
||
/// 自动适应 Canvas.renderMode 运行时的变化。</summary>
|
||
private Camera WorldCamera => _unityCanvas != null ? _unityCanvas.worldCamera : null;
|
||
|
||
/// <summary>
|
||
/// 构造屏幕空间蓝图画布
|
||
/// </summary>
|
||
/// <param name="unityCanvas">所属的 Unity UGUI Canvas 组件</param>
|
||
/// <param name="renderRoot">蓝图渲染根 RectTransform,所有蓝图层容器和背景挂载于此</param>
|
||
public ScreenSpaceBlueprintCanvas(UnityEngine.Canvas unityCanvas, RectTransform renderRoot, RectTransform viewport = null)
|
||
{
|
||
_unityCanvas = unityCanvas;
|
||
_renderRoot = renderRoot;
|
||
_viewport = viewport != null ? viewport : renderRoot;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将屏幕坐标转换为画布坐标。
|
||
/// 通过 <see cref="RectTransformUtility.ScreenPointToLocalPointInRectangle"/>
|
||
/// 以 <see cref="_renderRoot"/> 为参照转换,
|
||
/// 自动处理 Canvas 渲染模式和 RenderRoot 的偏移。
|
||
/// </summary>
|
||
public override Vector2 ScreenToLocal(Vector2 screenPoint)
|
||
{
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||
_renderRoot, screenPoint, WorldCamera, out Vector2 localPoint);
|
||
return localPoint;
|
||
}
|
||
|
||
public override Rect GetViewportLocalRect()
|
||
{
|
||
if (_viewport == null || _renderRoot == null) return base.GetViewportLocalRect();
|
||
var corners = new Vector3[4];
|
||
_viewport.GetWorldCorners(corners);
|
||
Vector2 min = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
|
||
Vector2 max = new Vector2(float.NegativeInfinity, float.NegativeInfinity);
|
||
for (int i = 0; i < 4; i++)
|
||
{
|
||
Vector3 local = _renderRoot.InverseTransformPoint(corners[i]);
|
||
min = Vector2.Min(min, local);
|
||
max = Vector2.Max(max, local);
|
||
}
|
||
return Rect.MinMaxRect(min.x, min.y, max.x, max.y);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将画布坐标转换为屏幕坐标。
|
||
/// 先计算 RenderRoot 本地空间位置,再通过 RectTransformUtility 转换为屏幕坐标。
|
||
/// </summary>
|
||
public override Vector2 CanvasToScreen(Vector2 canvasPoint)
|
||
{
|
||
Vector2 local = canvasPoint * _zoomLevel + _panOffset;
|
||
Vector3 world = _renderRoot.TransformPoint(local);
|
||
return RectTransformUtility.WorldToScreenPoint(WorldCamera, world);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将画布坐标转换为世界坐标。
|
||
/// </summary>
|
||
public override Vector3 CanvasToWorld(Vector2 canvasPoint)
|
||
{
|
||
Vector2 local = canvasPoint * _zoomLevel + _panOffset;
|
||
return _renderRoot.TransformPoint(local);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将世界坐标转换为画布坐标。
|
||
/// 先转换为屏幕坐标,再通过 RectTransformUtility 转换为 RenderRoot 本地坐标。
|
||
/// </summary>
|
||
public override Vector2 WorldToCanvas(Vector3 worldPoint)
|
||
{
|
||
Vector3 screenPoint = RectTransformUtility.WorldToScreenPoint(WorldCamera, worldPoint);
|
||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||
_renderRoot, screenPoint, WorldCamera, out Vector2 localPoint);
|
||
return (localPoint - _panOffset) / _zoomLevel;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取渲染根 Transform(蓝图层容器和背景的父级)
|
||
/// </summary>
|
||
public override Transform GetRootTransform()
|
||
{
|
||
return _renderRoot;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 底层 Unity Canvas 引用
|
||
/// </summary>
|
||
public UnityEngine.Canvas UnityCanvas
|
||
{
|
||
get { return _unityCanvas; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Canvas 组件未被销毁,画布可正常使用。
|
||
/// </summary>
|
||
public override bool IsAlive => _unityCanvas != null;
|
||
}
|
||
}
|