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
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: dea1943190171294d8592757a6d3a12a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,343 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using System.IO;
using TMPro;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
namespace ShrinkTutorial.Editor
{
public static class ShrinkTutorialSampleSceneBuilder
{
private const string ScenePath = "Assets/Scenes/ShrinkTutorialSample.unity";
private const string SettingsFolder = "Assets/Modules/ShrinkTutorial/Samples/Resources";
private const string SettingsPath = SettingsFolder + "/ShrinkTutorialSettings.asset";
private const string DatabasePath = "Assets/Modules/ShrinkTutorial/Samples/ShrinkTutorialSampleDatabase.asset";
private const string TutorialPath = "Assets/Modules/ShrinkTutorial/Samples/ShrinkTutorialSampleTutorial.asset";
[MenuItem("ShrinkSDK/引导/创建示例场景")]
public static void BuildSampleScene()
{
EnsureFolder("Assets/Modules/ShrinkTutorial");
EnsureFolder("Assets/Modules/ShrinkTutorial/Samples");
EnsureFolder(SettingsFolder);
EnsureFolder("Assets/Scenes");
var settings = EnsureSettings();
var database = EnsureDatabase();
var tutorial = EnsureTutorial();
database.hideFlags = HideFlags.None;
settings.database = database;
EditorUtility.SetDirty(settings);
UpdateTutorialData(tutorial);
SetDatabaseContent(database, tutorial);
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
scene.name = "ShrinkTutorialSample";
CreateCamera();
var canvas = CreateCanvas();
var root = new GameObject("Tutorial Sample Root");
var controller = root.AddComponent<ShrinkTutorialSampleSceneController>();
var openButton = CreateButton(canvas.transform, "OpenInventoryButton", "Open Inventory", new Vector2(0f, 110f), new Vector2(240f, 64f));
var statusText = CreateText(canvas.transform, "StatusText", "Waiting for tutorial start.", 26f,
new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 42f), new Vector2(900f, 48f));
statusText.alignment = TextAlignmentOptions.Center;
statusText.color = new Color(0.92f, 0.92f, 0.92f, 1f);
CreateText(canvas.transform, "TitleText", "ShrinkTutorial Sample Scene", 42f,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -70f), new Vector2(900f, 60f))
.alignment = TextAlignmentOptions.Center;
CreateText(canvas.transform, "DescText", "This scene demonstrates static targets, dynamic anchors, and custom-event completion.", 24f,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -120f), new Vector2(1100f, 50f))
.alignment = TextAlignmentOptions.Center;
var inventoryPanel = CreatePanel(canvas.transform, "InventoryPanel", new Vector2(0f, -60f), new Vector2(720f, 360f),
new Color(0.12f, 0.16f, 0.22f, 0.96f));
inventoryPanel.gameObject.SetActive(false);
CreateText(inventoryPanel, "InventoryTitle", "Inventory Panel", 34f,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -34f), new Vector2(300f, 48f))
.alignment = TextAlignmentOptions.Center;
CreateText(inventoryPanel, "InventoryHint", "Create the runtime reward first, then click the spawned item.", 22f,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -74f), new Vector2(520f, 40f))
.alignment = TextAlignmentOptions.Center;
var spawnButton = CreateButton(inventoryPanel, "SpawnRewardButton", "Spawn Reward Item", new Vector2(0f, 88f), new Vector2(250f, 58f));
var rewardContainer = new GameObject("RewardContainer", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
var rewardContainerRect = rewardContainer.GetComponent<RectTransform>();
rewardContainerRect.SetParent(inventoryPanel, false);
rewardContainerRect.anchorMin = new Vector2(0.5f, 0f);
rewardContainerRect.anchorMax = new Vector2(0.5f, 0f);
rewardContainerRect.pivot = new Vector2(0.5f, 0f);
rewardContainerRect.anchoredPosition = new Vector2(0f, 24f);
rewardContainerRect.sizeDelta = new Vector2(480f, 120f);
var layoutGroup = rewardContainer.GetComponent<VerticalLayoutGroup>();
layoutGroup.childAlignment = TextAnchor.UpperCenter;
layoutGroup.childControlWidth = true;
layoutGroup.childControlHeight = true;
layoutGroup.childForceExpandHeight = false;
layoutGroup.childForceExpandWidth = true;
layoutGroup.spacing = 12f;
var fitter = rewardContainer.GetComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
AssignControllerReferences(controller, openButton, spawnButton, inventoryPanel.gameObject, rewardContainerRect, statusText);
EditorSceneManager.SaveScene(scene, ScenePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
EditorSceneManager.OpenScene(ScenePath);
Selection.activeObject = AssetDatabase.LoadAssetAtPath<SceneAsset>(ScenePath);
Debug.Log("[ShrinkTutorial] Sample scene created: Assets/Scenes/ShrinkTutorialSample.unity");
}
[MenuItem("ShrinkSDK/Tutorial/Create Sample Scene")]
public static void BuildSampleSceneAlias()
{
BuildSampleScene();
}
private static ShrinkTutorialSettings EnsureSettings()
{
var settings = AssetDatabase.LoadAssetAtPath<ShrinkTutorialSettings>(SettingsPath);
if (settings)
return settings;
settings = ScriptableObject.CreateInstance<ShrinkTutorialSettings>();
settings.autoCreateManager = true;
settings.dontDestroyOnLoad = true;
settings.showSkipButton = true;
AssetDatabase.CreateAsset(settings, SettingsPath);
return settings;
}
private static ShrinkTutorialDatabase EnsureDatabase()
{
var database = AssetDatabase.LoadAssetAtPath<ShrinkTutorialDatabase>(DatabasePath);
if (database)
return database;
database = ScriptableObject.CreateInstance<ShrinkTutorialDatabase>();
AssetDatabase.CreateAsset(database, DatabasePath);
return database;
}
private static ShrinkTutorialData EnsureTutorial()
{
var tutorial = AssetDatabase.LoadAssetAtPath<ShrinkTutorialData>(TutorialPath);
if (tutorial)
return tutorial;
tutorial = ScriptableObject.CreateInstance<ShrinkTutorialData>();
AssetDatabase.CreateAsset(tutorial, TutorialPath);
return tutorial;
}
private static void UpdateTutorialData(ShrinkTutorialData tutorial)
{
tutorial.tutorialId = ShrinkTutorialSampleSceneController.TutorialId;
tutorial.priority = 100;
tutorial.prerequisiteIds = new string[0];
tutorial.steps = new List<ShrinkTutorialStep>
{
new()
{
targetMode = ShrinkTutorialTargetMode.Path,
targetPath = "Tutorial Sample Canvas/OpenInventoryButton",
completeCondition = ShrinkTutorialCompleteCondition.ClickTarget,
fallbackText = "Click here to open the inventory panel.",
dialogAnchor = ShrinkTutorialDialogAnchor.Bottom
},
new()
{
targetMode = ShrinkTutorialTargetMode.Path,
targetPath = "Tutorial Sample Canvas/InventoryPanel/SpawnRewardButton",
completeCondition = ShrinkTutorialCompleteCondition.ClickTarget,
fallbackText = "The inventory is open. Click 'Spawn Reward Item' to demonstrate dynamic-anchor waiting.",
dialogAnchor = ShrinkTutorialDialogAnchor.Top
},
new()
{
targetMode = ShrinkTutorialTargetMode.AnchorId,
anchorId = ShrinkTutorialSampleSceneController.RewardAnchorId,
waitForTarget = true,
waitTimeout = 10f,
completeCondition = ShrinkTutorialCompleteCondition.CustomEvent,
customEventName = ShrinkTutorialSampleSceneController.RewardClaimEventName,
fallbackText = "This button was created at runtime. Click it and the step will complete via a custom event.",
dialogAnchor = ShrinkTutorialDialogAnchor.Top
},
new()
{
targetMode = ShrinkTutorialTargetMode.None,
completeCondition = ShrinkTutorialCompleteCondition.AnyClick,
fallbackText = "Demo finished. You completed the static-target, dynamic-anchor, and custom-event flow. Click anywhere to close.",
dialogAnchor = ShrinkTutorialDialogAnchor.Auto,
showArrow = false,
maskShape = ShrinkTutorialMaskShape.None
}
};
EditorUtility.SetDirty(tutorial);
}
private static void SetDatabaseContent(ShrinkTutorialDatabase database, ShrinkTutorialData tutorial)
{
var serializedObject = new SerializedObject(database);
var tutorialsProperty = serializedObject.FindProperty("tutorials");
tutorialsProperty.arraySize = 1;
tutorialsProperty.GetArrayElementAtIndex(0).objectReferenceValue = tutorial;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(database);
}
private static void AssignControllerReferences(
ShrinkTutorialSampleSceneController controller,
Button openButton,
Button spawnButton,
GameObject inventoryPanel,
RectTransform rewardContainer,
TextMeshProUGUI statusText)
{
var serializedObject = new SerializedObject(controller);
serializedObject.FindProperty("openInventoryButton").objectReferenceValue = openButton;
serializedObject.FindProperty("spawnRewardButton").objectReferenceValue = spawnButton;
serializedObject.FindProperty("inventoryPanel").objectReferenceValue = inventoryPanel;
serializedObject.FindProperty("rewardContainer").objectReferenceValue = rewardContainer;
serializedObject.FindProperty("statusText").objectReferenceValue = statusText;
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(controller);
}
private static void CreateCamera()
{
var cameraObject = new GameObject("Main Camera", typeof(Camera));
cameraObject.tag = "MainCamera";
var camera = cameraObject.GetComponent<Camera>();
camera.clearFlags = CameraClearFlags.SolidColor;
camera.backgroundColor = new Color(0.06f, 0.08f, 0.12f, 1f);
camera.orthographic = true;
}
private static Canvas CreateCanvas()
{
var canvasObject = new GameObject("Tutorial Sample Canvas", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster));
var canvas = canvasObject.GetComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
var scaler = canvasObject.GetComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920f, 1080f);
scaler.matchWidthOrHeight = 0.5f;
var background = CreatePanel(canvasObject.transform, "Background", Vector2.zero, new Vector2(0f, 0f), new Color(0.06f, 0.08f, 0.12f, 1f));
Stretch(background);
background.SetAsFirstSibling();
return canvas;
}
private static RectTransform CreatePanel(Transform parent, string name, Vector2 anchoredPosition, Vector2 size, Color color)
{
var panelObject = new GameObject(name, typeof(RectTransform), typeof(Image));
var rectTransform = panelObject.GetComponent<RectTransform>();
rectTransform.SetParent(parent, false);
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.anchoredPosition = anchoredPosition;
rectTransform.sizeDelta = size;
var image = panelObject.GetComponent<Image>();
image.color = color;
return rectTransform;
}
private static Button CreateButton(Transform parent, string name, string label, Vector2 anchoredPosition, Vector2 size)
{
var buttonObject = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button));
var rectTransform = buttonObject.GetComponent<RectTransform>();
rectTransform.SetParent(parent, false);
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
rectTransform.pivot = new Vector2(0.5f, 0.5f);
rectTransform.anchoredPosition = anchoredPosition;
rectTransform.sizeDelta = size;
var image = buttonObject.GetComponent<Image>();
image.color = new Color(0.2f, 0.42f, 0.72f, 1f);
var button = buttonObject.GetComponent<Button>();
button.targetGraphic = image;
var text = CreateText(buttonObject.transform, "Label", label, 28f,
Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
text.alignment = TextAlignmentOptions.Center;
return button;
}
private static TextMeshProUGUI CreateText(
Transform parent,
string name,
string text,
float fontSize,
Vector2 anchorMin,
Vector2 anchorMax,
Vector2 pivot,
Vector2 anchoredPosition,
Vector2 sizeDelta)
{
var textObject = new GameObject(name, typeof(RectTransform), typeof(TextMeshProUGUI));
var rectTransform = textObject.GetComponent<RectTransform>();
rectTransform.SetParent(parent, false);
rectTransform.anchorMin = anchorMin;
rectTransform.anchorMax = anchorMax;
rectTransform.pivot = pivot;
rectTransform.anchoredPosition = anchoredPosition;
rectTransform.sizeDelta = sizeDelta;
var tmp = textObject.GetComponent<TextMeshProUGUI>();
tmp.text = text;
tmp.fontSize = fontSize;
tmp.color = Color.white;
tmp.enableWordWrapping = true;
tmp.font = TMP_Settings.defaultFontAsset;
return tmp;
}
private static void Stretch(RectTransform rectTransform)
{
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.offsetMin = Vector2.zero;
rectTransform.offsetMax = Vector2.zero;
rectTransform.pivot = new Vector2(0.5f, 0.5f);
}
private static void EnsureFolder(string path)
{
if (AssetDatabase.IsValidFolder(path))
return;
var parent = Path.GetDirectoryName(path)?.Replace("\\", "/");
var name = Path.GetFileName(path);
if (!string.IsNullOrEmpty(parent) && !AssetDatabase.IsValidFolder(parent))
EnsureFolder(parent);
AssetDatabase.CreateFolder(parent, name);
}
}
}
#endif
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 761e37eb274476b4aaf7ea7ad1baf078
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+18
View File
@@ -0,0 +1,18 @@
{
"name": "ShrinkTutorial.Editor",
"rootNamespace": "ShrinkTutorial.Editor",
"references": [
"ShrinkTutorial.Runtime",
"Unity.TextMeshPro"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cc9e4ac50c4134a49baf8d86e43979c9
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+181
View File
@@ -0,0 +1,181 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace ShrinkTutorial.Editor
{
public class ShrinkTutorialEditorWindow : EditorWindow
{
private ShrinkTutorialDatabase _database;
private Vector2 _tutorialListScroll;
private Vector2 _detailScroll;
private ShrinkTutorialData _selectedTutorial;
private UnityEditor.Editor _selectedTutorialEditor;
private List<string> _issues = new();
[MenuItem("ShrinkSDK/引导/教程编辑器")]
public static void ShowWindow()
{
var window = GetWindow<ShrinkTutorialEditorWindow>("引导系统");
window.minSize = new Vector2(960f, 580f);
window.Show();
}
[MenuItem("ShrinkSDK/Tutorial/Editor")]
public static void ShowWindowAlias()
{
ShowWindow();
}
[MenuItem("ShrinkSDK/引导/重置教程进度")]
public static void ResetProgress()
{
var settings = ShrinkTutorialSettings.Instance;
var key = settings ? settings.playerPrefsStorageKey : "ShrinkTutorial.Progress";
new PlayerPrefsShrinkTutorialStorage(key).Reset();
Debug.Log("[ShrinkTutorial] 已重置教程进度。");
}
[MenuItem("ShrinkSDK/Tutorial/Reset Progress")]
public static void ResetProgressAlias()
{
ResetProgress();
}
private void OnEnable()
{
TryAssignDefaultDatabase();
}
private void OnGUI()
{
DrawToolbar();
EditorGUILayout.BeginHorizontal();
DrawTutorialList();
DrawDetailPanel();
EditorGUILayout.EndHorizontal();
DrawIssues();
}
private void DrawToolbar()
{
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
_database = (ShrinkTutorialDatabase)EditorGUILayout.ObjectField(_database, typeof(ShrinkTutorialDatabase), false, GUILayout.Width(320f));
if (GUILayout.Button("从设置读取", EditorStyles.toolbarButton, GUILayout.Width(90f)))
TryAssignDefaultDatabase();
if (GUILayout.Button("验证数据库", EditorStyles.toolbarButton, GUILayout.Width(90f)))
ValidateDatabase();
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
}
private void DrawTutorialList()
{
EditorGUILayout.BeginVertical(GUILayout.Width(280f));
EditorGUILayout.LabelField("教程列表", EditorStyles.boldLabel);
if (!_database)
{
EditorGUILayout.HelpBox("请先指定 ShrinkTutorialDatabase。", MessageType.Info);
EditorGUILayout.EndVertical();
return;
}
_tutorialListScroll = EditorGUILayout.BeginScrollView(_tutorialListScroll);
foreach (var tutorial in _database.Tutorials)
{
using (new EditorGUI.DisabledScope(!tutorial))
{
var label = tutorial ? $"{tutorial.tutorialId} ({tutorial.steps.Count} 步)" : "<空引用>";
if (GUILayout.Toggle(_selectedTutorial == tutorial, label, "Button"))
SelectTutorial(tutorial);
}
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
private void DrawDetailPanel()
{
EditorGUILayout.BeginVertical();
if (!_selectedTutorial)
{
EditorGUILayout.HelpBox("左侧选择一个教程后,这里会显示完整配置。", MessageType.Info);
EditorGUILayout.EndVertical();
return;
}
EditorGUILayout.LabelField(_selectedTutorial.tutorialId, EditorStyles.boldLabel);
_detailScroll = EditorGUILayout.BeginScrollView(_detailScroll);
if (_selectedTutorialEditor == null)
_selectedTutorialEditor = UnityEditor.Editor.CreateEditor(_selectedTutorial);
_selectedTutorialEditor.OnInspectorGUI();
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
private void DrawIssues()
{
if (_issues.Count == 0)
return;
EditorGUILayout.Space();
EditorGUILayout.LabelField("校验结果", EditorStyles.boldLabel);
foreach (var issue in _issues)
EditorGUILayout.HelpBox(issue, MessageType.Warning);
}
private void TryAssignDefaultDatabase()
{
var settings = ShrinkTutorialSettings.Instance;
if (settings)
_database = settings.database;
if (!_database)
{
var guids = AssetDatabase.FindAssets("t:ShrinkTutorialDatabase");
if (guids.Length > 0)
{
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
_database = AssetDatabase.LoadAssetAtPath<ShrinkTutorialDatabase>(path);
}
}
}
private void ValidateDatabase()
{
_issues.Clear();
if (!_database)
{
_issues.Add("未指定 ShrinkTutorialDatabase。");
return;
}
_issues.AddRange(_database.Validate());
if (_issues.Count == 0)
_issues.Add("未发现配置问题。");
}
private void SelectTutorial(ShrinkTutorialData tutorial)
{
if (_selectedTutorial == tutorial)
return;
_selectedTutorial = tutorial;
DestroyImmediate(_selectedTutorialEditor);
_selectedTutorialEditor = null;
}
}
}
#endif
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1aace4135844b604f8b73b1dba817e27
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: