feat(cordis): 接入上下文组合与模组事务热替换
This commit is contained in:
@@ -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:
|
||||
Reference in New Issue
Block a user