344 lines
16 KiB
C#
344 lines
16 KiB
C#
#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
|