chore: initialize standalone UPM package
Publish UPM package / publish (push) Failing after 1s

This commit is contained in:
2026-08-26 02:50:43 +08:00
commit 1180be88a6
72 changed files with 3331 additions and 0 deletions
+222
View File
@@ -0,0 +1,222 @@
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace ShrinkTutorial
{
public class ShrinkTutorialDialog : MonoBehaviour
{
private RectTransform _rectTransform;
private RectTransform _arrowRectTransform;
private TextMeshProUGUI _contentText;
private GameObject _skipButtonObject;
private Action _skipAction;
public void Initialize(Transform parent, Action skipAction)
{
_skipAction = skipAction;
_rectTransform = GetComponent<RectTransform>();
if (!_rectTransform)
_rectTransform = gameObject.AddComponent<RectTransform>();
_rectTransform.SetParent(parent, false);
_rectTransform.sizeDelta = new Vector2(360f, 180f);
_rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
_rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
_rectTransform.pivot = new Vector2(0.5f, 0.5f);
var background = gameObject.AddComponent<Image>();
background.color = new Color(0.08f, 0.1f, 0.14f, 0.96f);
background.raycastTarget = true;
var content = CreateText("Content", "Tutorial", 24, TextAlignmentOptions.TopLeft);
var contentRect = content.rectTransform;
contentRect.SetParent(_rectTransform, false);
contentRect.anchorMin = new Vector2(0f, 0f);
contentRect.anchorMax = new Vector2(1f, 1f);
contentRect.offsetMin = new Vector2(24f, 64f);
contentRect.offsetMax = new Vector2(-24f, -24f);
_contentText = content;
var skipButton = new GameObject("SkipButton", typeof(RectTransform), typeof(Image), typeof(Button));
var skipRect = skipButton.GetComponent<RectTransform>();
skipRect.SetParent(_rectTransform, false);
skipRect.anchorMin = new Vector2(1f, 0f);
skipRect.anchorMax = new Vector2(1f, 0f);
skipRect.pivot = new Vector2(1f, 0f);
skipRect.anchoredPosition = new Vector2(-16f, 16f);
skipRect.sizeDelta = new Vector2(96f, 36f);
var skipImage = skipButton.GetComponent<Image>();
skipImage.color = new Color(0.22f, 0.34f, 0.5f, 1f);
var skipButtonComponent = skipButton.GetComponent<Button>();
skipButtonComponent.onClick.AddListener(() => _skipAction?.Invoke());
_skipButtonObject = skipButton;
var skipLabel = CreateText("Label", "Skip", 20, TextAlignmentOptions.Center);
var skipLabelRect = skipLabel.rectTransform;
skipLabelRect.SetParent(skipRect, false);
skipLabelRect.anchorMin = Vector2.zero;
skipLabelRect.anchorMax = Vector2.one;
skipLabelRect.offsetMin = Vector2.zero;
skipLabelRect.offsetMax = Vector2.zero;
var arrow = new GameObject("Arrow", typeof(RectTransform), typeof(Image));
_arrowRectTransform = arrow.GetComponent<RectTransform>();
_arrowRectTransform.SetParent(_rectTransform, false);
_arrowRectTransform.sizeDelta = new Vector2(20f, 20f);
_arrowRectTransform.localRotation = Quaternion.Euler(0f, 0f, 45f);
var arrowImage = arrow.GetComponent<Image>();
arrowImage.color = background.color;
arrowImage.raycastTarget = false;
}
public void Show(string text, Rect? targetRect, ShrinkTutorialDialogAnchor anchor, Vector2 offset, bool showArrow)
{
if (!_rectTransform)
return;
gameObject.SetActive(true);
_contentText.text = string.IsNullOrWhiteSpace(text) ? " " : text;
LayoutRebuilder.ForceRebuildLayoutImmediate(_rectTransform);
if (!targetRect.HasValue)
{
_rectTransform.anchoredPosition = offset;
_arrowRectTransform.gameObject.SetActive(false);
return;
}
var canvasRect = _rectTransform.parent as RectTransform;
var targetCenterScreen = targetRect.Value.center;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, targetCenterScreen, null, out var targetCenterLocal);
var localTargetRect = ConvertScreenRectToLocalRect(canvasRect, targetRect.Value);
var resolvedAnchor = ResolveAnchor(localTargetRect, anchor, canvasRect.rect);
var panelSize = _rectTransform.rect.size;
var margin = 24f;
var position = targetCenterLocal + offset;
switch (resolvedAnchor)
{
case ShrinkTutorialDialogAnchor.Top:
position += new Vector2(0f, localTargetRect.height * 0.5f + panelSize.y * 0.5f + margin);
break;
case ShrinkTutorialDialogAnchor.Bottom:
position += new Vector2(0f, -(localTargetRect.height * 0.5f + panelSize.y * 0.5f + margin));
break;
case ShrinkTutorialDialogAnchor.Left:
position += new Vector2(-(localTargetRect.width * 0.5f + panelSize.x * 0.5f + margin), 0f);
break;
case ShrinkTutorialDialogAnchor.Right:
position += new Vector2(localTargetRect.width * 0.5f + panelSize.x * 0.5f + margin, 0f);
break;
}
var halfCanvas = canvasRect.rect.size * 0.5f;
position.x = Mathf.Clamp(position.x, -halfCanvas.x + panelSize.x * 0.5f + 12f, halfCanvas.x - panelSize.x * 0.5f - 12f);
position.y = Mathf.Clamp(position.y, -halfCanvas.y + panelSize.y * 0.5f + 12f, halfCanvas.y - panelSize.y * 0.5f - 12f);
_rectTransform.anchoredPosition = position;
ConfigureArrow(resolvedAnchor, showArrow);
}
public void Hide()
{
if (gameObject.activeSelf)
gameObject.SetActive(false);
}
public bool ContainsScreenPoint(Vector2 screenPoint)
{
return _rectTransform && gameObject.activeInHierarchy &&
RectTransformUtility.RectangleContainsScreenPoint(_rectTransform, screenPoint, null);
}
public void SetSkipVisible(bool visible)
{
if (_skipButtonObject)
_skipButtonObject.SetActive(visible);
}
private void ConfigureArrow(ShrinkTutorialDialogAnchor anchor, bool showArrow)
{
_arrowRectTransform.gameObject.SetActive(showArrow);
if (!showArrow)
return;
switch (anchor)
{
case ShrinkTutorialDialogAnchor.Top:
_arrowRectTransform.anchorMin = new Vector2(0.5f, 0f);
_arrowRectTransform.anchorMax = new Vector2(0.5f, 0f);
_arrowRectTransform.anchoredPosition = new Vector2(0f, -10f);
break;
case ShrinkTutorialDialogAnchor.Bottom:
_arrowRectTransform.anchorMin = new Vector2(0.5f, 1f);
_arrowRectTransform.anchorMax = new Vector2(0.5f, 1f);
_arrowRectTransform.anchoredPosition = new Vector2(0f, 10f);
break;
case ShrinkTutorialDialogAnchor.Left:
_arrowRectTransform.anchorMin = new Vector2(1f, 0.5f);
_arrowRectTransform.anchorMax = new Vector2(1f, 0.5f);
_arrowRectTransform.anchoredPosition = new Vector2(10f, 0f);
break;
case ShrinkTutorialDialogAnchor.Right:
_arrowRectTransform.anchorMin = new Vector2(0f, 0.5f);
_arrowRectTransform.anchorMax = new Vector2(0f, 0.5f);
_arrowRectTransform.anchoredPosition = new Vector2(-10f, 0f);
break;
default:
_arrowRectTransform.gameObject.SetActive(false);
break;
}
}
private static ShrinkTutorialDialogAnchor ResolveAnchor(Rect targetRect, ShrinkTutorialDialogAnchor preferredAnchor, Rect canvasRect)
{
if (preferredAnchor != ShrinkTutorialDialogAnchor.Auto)
return preferredAnchor;
var topSpace = canvasRect.yMax - targetRect.yMax;
var bottomSpace = targetRect.yMin - canvasRect.yMin;
if (topSpace >= 240f)
return ShrinkTutorialDialogAnchor.Top;
if (bottomSpace >= 240f)
return ShrinkTutorialDialogAnchor.Bottom;
var rightSpace = canvasRect.xMax - targetRect.xMax;
var leftSpace = targetRect.xMin - canvasRect.xMin;
return rightSpace >= leftSpace
? ShrinkTutorialDialogAnchor.Right
: ShrinkTutorialDialogAnchor.Left;
}
private static Rect ConvertScreenRectToLocalRect(RectTransform canvasRect, Rect screenRect)
{
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect,
new Vector2(screenRect.xMin, screenRect.yMin), null, out var minLocal);
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect,
new Vector2(screenRect.xMax, screenRect.yMax), null, out var maxLocal);
return Rect.MinMaxRect(
Mathf.Min(minLocal.x, maxLocal.x),
Mathf.Min(minLocal.y, maxLocal.y),
Mathf.Max(minLocal.x, maxLocal.x),
Mathf.Max(minLocal.y, maxLocal.y));
}
private static TextMeshProUGUI CreateText(string objectName, string text, float fontSize, TextAlignmentOptions alignment)
{
var textObject = new GameObject(objectName, typeof(RectTransform), typeof(TextMeshProUGUI));
var tmp = textObject.GetComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.alignment = alignment;
tmp.color = Color.white;
tmp.enableWordWrapping = true;
return tmp;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9380f8798cf316c4d9a7d9d743037688
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+135
View File
@@ -0,0 +1,135 @@
using UnityEngine;
using UnityEngine.UI;
namespace ShrinkTutorial
{
public class ShrinkTutorialMask : MaskableGraphic, ICanvasRaycastFilter
{
private Rect _holeRect;
private ShrinkTutorialMaskShape _shape;
private bool _hasHole;
public void SetHighlight(Rect screenRect, ShrinkTutorialMaskShape shape, float padding)
{
enabled = true;
raycastTarget = true;
var camera = GetEventCamera();
var minScreen = new Vector2(screenRect.xMin - padding, screenRect.yMin - padding);
var maxScreen = new Vector2(screenRect.xMax + padding, screenRect.yMax + padding);
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, minScreen, camera, out var minLocal);
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, maxScreen, camera, out var maxLocal);
_holeRect = Rect.MinMaxRect(
Mathf.Min(minLocal.x, maxLocal.x),
Mathf.Min(minLocal.y, maxLocal.y),
Mathf.Max(minLocal.x, maxLocal.x),
Mathf.Max(minLocal.y, maxLocal.y));
_shape = shape;
_hasHole = shape != ShrinkTutorialMaskShape.None;
SetVerticesDirty();
}
public void ClearHighlight()
{
_hasHole = false;
_shape = ShrinkTutorialMaskShape.None;
_holeRect = default;
raycastTarget = false;
SetVerticesDirty();
enabled = false;
}
public bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
if (!_hasHole)
return false;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out var localPoint);
if (_shape == ShrinkTutorialMaskShape.Circle)
{
var radius = Mathf.Min(_holeRect.width, _holeRect.height) * 0.5f;
return Vector2.SqrMagnitude(localPoint - _holeRect.center) > radius * radius;
}
return !_holeRect.Contains(localPoint);
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
var outer = rectTransform.rect;
if (!_hasHole)
return;
var hole = ClampRect(_holeRect, outer);
if (hole.yMax < outer.yMax)
{
AddQuad(vh,
new Vector2(outer.xMin, hole.yMax),
new Vector2(outer.xMin, outer.yMax),
new Vector2(outer.xMax, outer.yMax),
new Vector2(outer.xMax, hole.yMax));
}
if (hole.yMin > outer.yMin)
{
AddQuad(vh,
new Vector2(outer.xMin, outer.yMin),
new Vector2(outer.xMin, hole.yMin),
new Vector2(outer.xMax, hole.yMin),
new Vector2(outer.xMax, outer.yMin));
}
if (hole.xMin > outer.xMin)
{
AddQuad(vh,
new Vector2(outer.xMin, hole.yMin),
new Vector2(outer.xMin, hole.yMax),
new Vector2(hole.xMin, hole.yMax),
new Vector2(hole.xMin, hole.yMin));
}
if (hole.xMax < outer.xMax)
{
AddQuad(vh,
new Vector2(hole.xMax, hole.yMin),
new Vector2(hole.xMax, hole.yMax),
new Vector2(outer.xMax, hole.yMax),
new Vector2(outer.xMax, hole.yMin));
}
}
private static Rect ClampRect(Rect source, Rect bounds)
{
return Rect.MinMaxRect(
Mathf.Clamp(source.xMin, bounds.xMin, bounds.xMax),
Mathf.Clamp(source.yMin, bounds.yMin, bounds.yMax),
Mathf.Clamp(source.xMax, bounds.xMin, bounds.xMax),
Mathf.Clamp(source.yMax, bounds.yMin, bounds.yMax));
}
private void AddQuad(VertexHelper vh, Vector2 bottomLeft, Vector2 topLeft, Vector2 topRight, Vector2 bottomRight)
{
var startIndex = vh.currentVertCount;
vh.AddVert(bottomLeft, color, Vector2.zero);
vh.AddVert(topLeft, color, Vector2.up);
vh.AddVert(topRight, color, Vector2.one);
vh.AddVert(bottomRight, color, Vector2.right);
vh.AddTriangle(startIndex, startIndex + 1, startIndex + 2);
vh.AddTriangle(startIndex, startIndex + 2, startIndex + 3);
}
private Camera GetEventCamera()
{
var currentCanvas = canvas ? canvas : GetComponentInParent<Canvas>();
if (!currentCanvas || currentCanvas.renderMode == RenderMode.ScreenSpaceOverlay)
return null;
return currentCanvas.worldCamera;
}
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d5cadf3e241805441be468ff8eec081a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: