Files
ShrinkTutorial/Runtime/Core/ShrinkTutorialTargetUtility.cs
cneicy 1180be88a6
Publish UPM package / publish (push) Failing after 1s
chore: initialize standalone UPM package
2026-08-26 02:50:43 +08:00

173 lines
6.0 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace ShrinkTutorial
{
internal static class ShrinkTutorialTargetUtility
{
public static Transform ResolveTarget(ShrinkTutorialStep step)
{
var target = step.targetMode switch
{
ShrinkTutorialTargetMode.Path => FindByHierarchyPath(step.targetPath),
ShrinkTutorialTargetMode.AnchorId => ShrinkTutorialAnchorRegistry.Get(step.anchorId),
_ => null
};
return IsTargetAvailable(target) ? target : null;
}
public static bool TryBuildScreenRect(Transform targetTransform, out Rect rect)
{
rect = default;
if (!IsTargetAvailable(targetTransform))
return false;
if (targetTransform is RectTransform rectTransform)
return TryBuildRectTransformScreenRect(rectTransform, out rect);
if (targetTransform.TryGetComponent<Renderer>(out var renderer))
return TryBuildBoundsScreenRect(renderer.bounds, out rect);
if (targetTransform.TryGetComponent<Collider>(out var collider))
return TryBuildBoundsScreenRect(collider.bounds, out rect);
var screenPoint = RectTransformUtility.WorldToScreenPoint(GetWorldCamera(), targetTransform.position);
rect = new Rect(screenPoint.x - 24f, screenPoint.y - 24f, 48f, 48f);
return true;
}
private static bool TryBuildRectTransformScreenRect(RectTransform rectTransform, out Rect rect)
{
var corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);
var camera = GetCanvasCamera(rectTransform);
var min = RectTransformUtility.WorldToScreenPoint(camera, corners[0]);
var max = min;
for (var i = 1; i < corners.Length; i++)
{
var point = RectTransformUtility.WorldToScreenPoint(camera, corners[i]);
min = Vector2.Min(min, point);
max = Vector2.Max(max, point);
}
rect = Rect.MinMaxRect(min.x, min.y, max.x, max.y);
return rect.width > 0f && rect.height > 0f;
}
private static bool TryBuildBoundsScreenRect(Bounds bounds, out Rect rect)
{
rect = default;
var camera = GetWorldCamera();
if (!camera)
return false;
var corners = new List<Vector3>(8)
{
new(bounds.min.x, bounds.min.y, bounds.min.z),
new(bounds.min.x, bounds.min.y, bounds.max.z),
new(bounds.min.x, bounds.max.y, bounds.min.z),
new(bounds.min.x, bounds.max.y, bounds.max.z),
new(bounds.max.x, bounds.min.y, bounds.min.z),
new(bounds.max.x, bounds.min.y, bounds.max.z),
new(bounds.max.x, bounds.max.y, bounds.min.z),
new(bounds.max.x, bounds.max.y, bounds.max.z)
};
var min = new Vector2(float.MaxValue, float.MaxValue);
var max = new Vector2(float.MinValue, float.MinValue);
var hasVisiblePoint = false;
foreach (var corner in corners)
{
var screenPoint = camera.WorldToScreenPoint(corner);
if (screenPoint.z < 0f)
continue;
hasVisiblePoint = true;
var point = new Vector2(screenPoint.x, screenPoint.y);
min = Vector2.Min(min, point);
max = Vector2.Max(max, point);
}
if (!hasVisiblePoint)
return false;
rect = Rect.MinMaxRect(min.x, min.y, max.x, max.y);
return true;
}
private static Camera GetCanvasCamera(RectTransform rectTransform)
{
var canvas = rectTransform.GetComponentInParent<Canvas>();
if (!canvas || canvas.renderMode == RenderMode.ScreenSpaceOverlay)
return null;
return canvas.worldCamera ? canvas.worldCamera : Camera.main;
}
private static Camera GetWorldCamera()
{
if (Camera.main)
return Camera.main;
foreach (var camera in Camera.allCameras)
{
if (camera && camera.enabled)
return camera;
}
return null;
}
private static bool IsTargetAvailable(Transform targetTransform)
{
return targetTransform && targetTransform.gameObject.activeInHierarchy;
}
private static Transform FindByHierarchyPath(string hierarchyPath)
{
if (string.IsNullOrWhiteSpace(hierarchyPath))
return null;
var segments = hierarchyPath.Split('/');
for (var sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++)
{
var scene = SceneManager.GetSceneAt(sceneIndex);
if (!scene.isLoaded)
continue;
foreach (var root in scene.GetRootGameObjects())
{
if (TryMatchPath(root.transform, segments, 0, out var result))
return result;
}
}
return null;
}
private static bool TryMatchPath(Transform current, string[] segments, int segmentIndex, out Transform result)
{
result = null;
if (!current || segmentIndex >= segments.Length || current.name != segments[segmentIndex])
return false;
if (segmentIndex == segments.Length - 1)
{
result = current;
return true;
}
for (var i = 0; i < current.childCount; i++)
{
if (TryMatchPath(current.GetChild(i), segments, segmentIndex + 1, out result))
return true;
}
return false;
}
}
}