173 lines
6.0 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|