mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-29 10:35:42 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
031262d374 | ||
|
|
92a81c0ce6 | ||
|
|
78fdefea9e | ||
|
|
920d0f72bb | ||
|
|
43b6526f58 | ||
|
|
3bbaf2b839 | ||
|
|
23a9c11d9d | ||
|
|
bca005b264 | ||
|
|
774f3e80cb |
@@ -341,7 +341,6 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 52abae862492497cb7a3f8c72f837b37, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Assembly-CSharp::Eagle0.Tutorial.TutorialUIManager
|
||||
ModalPanel: {fileID: 0}
|
||||
OverlayController: {fileID: 0}
|
||||
HintIndicatorPrefab: {fileID: 0}
|
||||
TutorialCanvas: {fileID: 0}
|
||||
@@ -38288,14 +38287,14 @@ RectTransform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 896085699}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -0.000012874603}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1295407723}
|
||||
- {fileID: 267176577}
|
||||
m_Father: {fileID: 1117263354}
|
||||
m_Father: {fileID: 1128356212}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
@@ -52817,10 +52816,8 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 1117263354}
|
||||
- component: {fileID: 1117263357}
|
||||
- component: {fileID: 1117263356}
|
||||
- component: {fileID: 1117263358}
|
||||
- component: {fileID: 1117263359}
|
||||
- component: {fileID: 1117263360}
|
||||
m_Layer: 0
|
||||
m_Name: Tutorial Panel
|
||||
m_TagString: Untagged
|
||||
@@ -52840,46 +52837,15 @@ RectTransform:
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 896085700}
|
||||
- {fileID: 2070655606}
|
||||
- {fileID: 1726547713}
|
||||
- {fileID: 1762433297}
|
||||
- {fileID: 1128356212}
|
||||
m_Father: {fileID: 300036923}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 800, y: 328.52}
|
||||
m_AnchorMin: {x: 0.5, y: 0}
|
||||
m_AnchorMax: {x: 0.5, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 200}
|
||||
m_SizeDelta: {x: 800, y: -400}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1117263356
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1117263353}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.7058824, g: 0.7058824, b: 0.7058824, a: 0.9647059}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!222 &1117263357
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -52909,6 +52875,9 @@ MonoBehaviour:
|
||||
type: 2}
|
||||
continueButton: {fileID: 1936854701}
|
||||
skipButton: {fileID: 606632695}
|
||||
tutorialPanel: {fileID: 1117263354}
|
||||
defaultBottom: 400
|
||||
battleBottom: 650
|
||||
--- !u!114 &1117263359
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -52924,10 +52893,10 @@ MonoBehaviour:
|
||||
m_Padding:
|
||||
m_Left: 0
|
||||
m_Right: 0
|
||||
m_Top: 20
|
||||
m_Bottom: 10
|
||||
m_ChildAlignment: 4
|
||||
m_Spacing: 10
|
||||
m_Top: 0
|
||||
m_Bottom: 0
|
||||
m_ChildAlignment: 7
|
||||
m_Spacing: 0
|
||||
m_ChildForceExpandWidth: 0
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
@@ -52935,20 +52904,6 @@ MonoBehaviour:
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &1117263360
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1117263353}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 2
|
||||
--- !u!1 &1118217569
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -53166,6 +53121,147 @@ CanvasRenderer:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1125275705}
|
||||
m_CullTransparentMesh: 0
|
||||
--- !u!1 &1128356211
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1128356212}
|
||||
- component: {fileID: 1128356216}
|
||||
- component: {fileID: 1128356215}
|
||||
- component: {fileID: 1128356214}
|
||||
- component: {fileID: 1128356213}
|
||||
- component: {fileID: 1128356217}
|
||||
m_Layer: 0
|
||||
m_Name: Content
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1128356212
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1128356211}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 896085700}
|
||||
- {fileID: 2070655606}
|
||||
- {fileID: 1726547713}
|
||||
m_Father: {fileID: 1117263354}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 400, y: -515.74}
|
||||
m_SizeDelta: {x: 800, y: 328.52}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1128356213
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1128356211}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.VerticalLayoutGroup
|
||||
m_Padding:
|
||||
m_Left: 0
|
||||
m_Right: 0
|
||||
m_Top: 20
|
||||
m_Bottom: 10
|
||||
m_ChildAlignment: 7
|
||||
m_Spacing: 10
|
||||
m_ChildForceExpandWidth: 0
|
||||
m_ChildForceExpandHeight: 0
|
||||
m_ChildControlWidth: 1
|
||||
m_ChildControlHeight: 1
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!114 &1128356214
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1128356211}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.ContentSizeFitter
|
||||
m_HorizontalFit: 0
|
||||
m_VerticalFit: 2
|
||||
--- !u!114 &1128356215
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1128356211}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 0.7058824, g: 0.7058824, b: 0.7058824, a: 0.9647059}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_Type: 1
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!222 &1128356216
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1128356211}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &1128356217
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1128356211}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: -1
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: 1
|
||||
m_FlexibleHeight: 0
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &1131569341
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -82976,14 +83072,14 @@ RectTransform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1726547712}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 606632693}
|
||||
- {fileID: 1936854700}
|
||||
m_Father: {fileID: 1117263354}
|
||||
m_Father: {fileID: 1128356212}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
@@ -85505,6 +85601,62 @@ MonoBehaviour:
|
||||
m_ChildScaleWidth: 0
|
||||
m_ChildScaleHeight: 0
|
||||
m_ReverseArrangement: 0
|
||||
--- !u!1 &1762433296
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1762433297}
|
||||
- component: {fileID: 1762433298}
|
||||
m_Layer: 0
|
||||
m_Name: Spacer
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1762433297
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1762433296}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0.000012874603}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1117263354}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
m_AnchoredPosition: {x: 400, y: -175.73999}
|
||||
m_SizeDelta: {x: 800, y: 351.48}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &1762433298
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1762433296}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.LayoutElement
|
||||
m_IgnoreLayout: 0
|
||||
m_MinWidth: -1
|
||||
m_MinHeight: -1
|
||||
m_PreferredWidth: -1
|
||||
m_PreferredHeight: -1
|
||||
m_FlexibleWidth: 1
|
||||
m_FlexibleHeight: 1
|
||||
m_LayoutPriority: 1
|
||||
--- !u!1 &1765511185
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -94143,13 +94295,13 @@ RectTransform:
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2070655605}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: -0.000012874603}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 647524251}
|
||||
m_Father: {fileID: 1117263354}
|
||||
m_Father: {fileID: 1128356212}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 1}
|
||||
m_AnchorMax: {x: 0, y: 1}
|
||||
|
||||
+2
-5
@@ -153,10 +153,7 @@ namespace Eagle0.Tutorial {
|
||||
|
||||
_activeScript = script;
|
||||
_currentStepIndex = 0;
|
||||
// In combat, always anchor to top so the panel doesn't cover the hex grid.
|
||||
// On the strategic map, always use the default position.
|
||||
var position = _shardokController?.Model != null ? "top" : null;
|
||||
panelController.SetPanelPosition(position);
|
||||
panelController.SetBattleMode(_shardokController?.Model != null);
|
||||
ShowCurrentStep();
|
||||
}
|
||||
|
||||
@@ -287,7 +284,7 @@ namespace Eagle0.Tutorial {
|
||||
_activeScript = null;
|
||||
_currentStepIndex = 0;
|
||||
|
||||
panelController.SetPanelPosition(null);
|
||||
panelController.SetBattleMode(false);
|
||||
panelController.Hide();
|
||||
panelController.continueButton.onClick.RemoveAllListeners();
|
||||
panelController.skipButton.onClick.RemoveAllListeners();
|
||||
|
||||
+15
-27
@@ -24,36 +24,24 @@ namespace Eagle0.Tutorial {
|
||||
public Button continueButton;
|
||||
public Button skipButton;
|
||||
|
||||
private Vector2 _defaultAnchorMin;
|
||||
private Vector2 _defaultAnchorMax;
|
||||
private Vector2 _defaultPivot;
|
||||
private bool _defaultsSaved;
|
||||
[Header("Battle Layout")]
|
||||
[Tooltip("The Tutorial Panel RectTransform (vertically-stretched container)")]
|
||||
public RectTransform tutorialPanel;
|
||||
|
||||
[Tooltip("Bottom offset of the Tutorial Panel in default (Eagle) mode")]
|
||||
public float defaultBottom = 400f;
|
||||
|
||||
[Tooltip("Bottom offset of the Tutorial Panel in battle mode")]
|
||||
public float battleBottom = 650f;
|
||||
|
||||
/// <summary>
|
||||
/// Repositions the panel vertically. Call before Show().
|
||||
/// "top" anchors to the top of the screen; null/empty restores default.
|
||||
/// Raises the panel's bottom edge for battle view, or restores default.
|
||||
/// </summary>
|
||||
public void SetPanelPosition(string position) {
|
||||
var rt = GetComponent<RectTransform>();
|
||||
|
||||
if (!_defaultsSaved) {
|
||||
_defaultAnchorMin = rt.anchorMin;
|
||||
_defaultAnchorMax = rt.anchorMax;
|
||||
_defaultPivot = rt.pivot;
|
||||
_defaultsSaved = true;
|
||||
}
|
||||
|
||||
if (position == "top") {
|
||||
rt.anchorMin = new Vector2(rt.anchorMin.x, 1f);
|
||||
rt.anchorMax = new Vector2(rt.anchorMax.x, 1f);
|
||||
rt.pivot = new Vector2(rt.pivot.x, 1f);
|
||||
rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, -10f);
|
||||
} else {
|
||||
rt.anchorMin = _defaultAnchorMin;
|
||||
rt.anchorMax = _defaultAnchorMax;
|
||||
rt.pivot = _defaultPivot;
|
||||
rt.anchoredPosition = new Vector2(rt.anchoredPosition.x, 0f);
|
||||
}
|
||||
public void SetBattleMode(bool battle) {
|
||||
if (tutorialPanel == null) return;
|
||||
var offset = tutorialPanel.offsetMin;
|
||||
offset.y = battle ? battleBottom : defaultBottom;
|
||||
tutorialPanel.offsetMin = offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
-472
@@ -1,472 +0,0 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Eagle0.Tutorial {
|
||||
/// <summary>
|
||||
/// Builds the tutorial Canvas UI programmatically at runtime.
|
||||
/// Creates a complete TutorialModalPanel with all required UI elements.
|
||||
/// </summary>
|
||||
public static class TutorialCanvasBuilder {
|
||||
// Colors matching the game's fantasy RPG style
|
||||
private static readonly Color PanelBackgroundColor = new Color(0.15f, 0.12f, 0.2f, 0.95f);
|
||||
private static readonly Color BorderColor = new Color(0.6f, 0.5f, 0.3f, 1f);
|
||||
private static readonly Color TitleColor = new Color(1f, 0.9f, 0.7f, 1f);
|
||||
private static readonly Color TextColor = new Color(0.9f, 0.85f, 0.8f, 1f);
|
||||
private static readonly Color ButtonColor = new Color(0.4f, 0.3f, 0.2f, 1f);
|
||||
private static readonly Color ButtonHoverColor = new Color(0.5f, 0.4f, 0.25f, 1f);
|
||||
private static readonly Color ButtonTextColor = new Color(1f, 0.95f, 0.85f, 1f);
|
||||
private static readonly Color ModalBlockerColor = new Color(0f, 0f, 0f, 0.75f);
|
||||
private static readonly Color ProgressBarColor = new Color(0.6f, 0.5f, 0.3f, 1f);
|
||||
private static readonly Color ProgressBackgroundColor = new Color(0.2f, 0.15f, 0.1f, 1f);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a complete tutorial Canvas with modal panel.
|
||||
/// Returns the Canvas GameObject and sets up the TutorialModalPanel component.
|
||||
/// </summary>
|
||||
/// <param name="font">TextMeshPro font to use (assign Stoke-Regular-SDF in
|
||||
/// inspector)</param>
|
||||
public static Canvas BuildTutorialCanvas(TMP_FontAsset font) {
|
||||
// Create Canvas
|
||||
GameObject canvasObj = new GameObject("TutorialCanvas");
|
||||
Canvas canvas = canvasObj.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = 1000;
|
||||
|
||||
CanvasScaler scaler = canvasObj.AddComponent<CanvasScaler>();
|
||||
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
||||
scaler.referenceResolution = new Vector2(1920, 1080);
|
||||
scaler.matchWidthOrHeight = 0.5f;
|
||||
|
||||
canvasObj.AddComponent<GraphicRaycaster>();
|
||||
|
||||
// Create Modal Blocker
|
||||
GameObject blockerObj = CreateModalBlocker(canvasObj.transform);
|
||||
|
||||
// Create Panel Container
|
||||
GameObject panelContainer = CreatePanelContainer(canvasObj.transform, font);
|
||||
|
||||
// Add TutorialModalPanel component and wire it up
|
||||
TutorialModalPanel modalPanel = canvasObj.AddComponent<TutorialModalPanel>();
|
||||
WireUpModalPanel(modalPanel, blockerObj, panelContainer, font);
|
||||
|
||||
// Set up button listeners (Awake runs before fields are wired, so do it here)
|
||||
SetupButtonListeners(modalPanel);
|
||||
|
||||
// Start with everything hidden
|
||||
blockerObj.SetActive(false);
|
||||
panelContainer.SetActive(false);
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
private static GameObject CreateModalBlocker(Transform parent) {
|
||||
GameObject blocker = new GameObject("ModalBlocker");
|
||||
blocker.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = blocker.AddComponent<RectTransform>();
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.offsetMin = Vector2.zero;
|
||||
rect.offsetMax = Vector2.zero;
|
||||
|
||||
Image image = blocker.AddComponent<Image>();
|
||||
image.color = ModalBlockerColor;
|
||||
image.raycastTarget = true;
|
||||
|
||||
return blocker;
|
||||
}
|
||||
|
||||
private static GameObject CreatePanelContainer(Transform parent, TMP_FontAsset font) {
|
||||
// Panel Container (centered)
|
||||
GameObject panel = new GameObject("PanelContainer");
|
||||
panel.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform panelRect = panel.AddComponent<RectTransform>();
|
||||
panelRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
panelRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
panelRect.sizeDelta = new Vector2(540, 500);
|
||||
panelRect.anchoredPosition = Vector2.zero;
|
||||
|
||||
// Panel Background
|
||||
Image panelBg = panel.AddComponent<Image>();
|
||||
panelBg.color = PanelBackgroundColor;
|
||||
panelBg.raycastTarget = true;
|
||||
|
||||
// Add outline/border effect
|
||||
Outline outline = panel.AddComponent<Outline>();
|
||||
outline.effectColor = BorderColor;
|
||||
outline.effectDistance = new Vector2(3, 3);
|
||||
|
||||
// Add vertical layout group
|
||||
VerticalLayoutGroup layout = panel.AddComponent<VerticalLayoutGroup>();
|
||||
layout.padding = new RectOffset(20, 20, 15, 15);
|
||||
layout.spacing = 10;
|
||||
layout.childAlignment = TextAnchor.UpperCenter;
|
||||
layout.childControlHeight = true; // Let layout control child heights
|
||||
layout.childControlWidth = true;
|
||||
layout.childForceExpandHeight = false;
|
||||
layout.childForceExpandWidth = true;
|
||||
|
||||
// Add ContentSizeFitter to auto-size panel to content
|
||||
ContentSizeFitter fitter = panel.AddComponent<ContentSizeFitter>();
|
||||
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
// Title
|
||||
CreateTitle(panel.transform, font);
|
||||
|
||||
// Icon (placeholder - starts hidden)
|
||||
CreateIcon(panel.transform);
|
||||
|
||||
// Description
|
||||
CreateDescription(panel.transform, font);
|
||||
|
||||
// Progress section (VerticalLayoutGroup spacing handles gaps)
|
||||
CreateProgressSection(panel.transform, font);
|
||||
|
||||
// Button row
|
||||
CreateButtonRow(panel.transform, font);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private static void CreateTitle(Transform parent, TMP_FontAsset font) {
|
||||
GameObject titleObj = new GameObject("TitleText");
|
||||
titleObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = titleObj.AddComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(460, 36);
|
||||
|
||||
TextMeshProUGUI text = titleObj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = "Tutorial";
|
||||
text.fontSize = 28;
|
||||
text.fontStyle = FontStyles.Bold;
|
||||
text.color = TitleColor;
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
if (font != null) text.font = font;
|
||||
|
||||
LayoutElement layoutElement = titleObj.AddComponent<LayoutElement>();
|
||||
layoutElement.preferredHeight = 36;
|
||||
}
|
||||
|
||||
private static void CreateIcon(Transform parent) {
|
||||
GameObject iconObj = new GameObject("IconImage");
|
||||
iconObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = iconObj.AddComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(100, 100);
|
||||
|
||||
Image image = iconObj.AddComponent<Image>();
|
||||
image.color = Color.white;
|
||||
image.preserveAspect = true;
|
||||
|
||||
LayoutElement layoutElement = iconObj.AddComponent<LayoutElement>();
|
||||
layoutElement.preferredHeight = 100;
|
||||
layoutElement.preferredWidth = 100;
|
||||
|
||||
// Start hidden
|
||||
iconObj.SetActive(false);
|
||||
}
|
||||
|
||||
private static void CreateDescription(Transform parent, TMP_FontAsset font) {
|
||||
GameObject descObj = new GameObject("DescriptionText");
|
||||
descObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = descObj.AddComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(460, 180);
|
||||
|
||||
TextMeshProUGUI text = descObj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = "";
|
||||
text.fontSize = 18;
|
||||
text.color = TextColor;
|
||||
text.alignment = TextAlignmentOptions.TopLeft;
|
||||
text.enableWordWrapping = true;
|
||||
text.overflowMode = TextOverflowModes.Overflow; // Don't truncate
|
||||
if (font != null) text.font = font;
|
||||
|
||||
// Auto-size height to fit text content
|
||||
ContentSizeFitter fitter = descObj.AddComponent<ContentSizeFitter>();
|
||||
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
LayoutElement layoutElement = descObj.AddComponent<LayoutElement>();
|
||||
layoutElement.minHeight = 40; // Minimum height for short text
|
||||
layoutElement.preferredWidth = 460;
|
||||
}
|
||||
|
||||
private static void CreateSpacer(Transform parent, float height) {
|
||||
GameObject spacer = new GameObject("Spacer");
|
||||
spacer.transform.SetParent(parent, false);
|
||||
|
||||
spacer.AddComponent<RectTransform>();
|
||||
|
||||
LayoutElement layoutElement = spacer.AddComponent<LayoutElement>();
|
||||
layoutElement.preferredHeight = height;
|
||||
}
|
||||
|
||||
private static void CreateFlexibleSpacer(Transform parent) {
|
||||
GameObject spacer = new GameObject("FlexibleSpacer");
|
||||
spacer.transform.SetParent(parent, false);
|
||||
|
||||
spacer.AddComponent<RectTransform>();
|
||||
|
||||
LayoutElement layoutElement = spacer.AddComponent<LayoutElement>();
|
||||
layoutElement.flexibleHeight = 1;
|
||||
}
|
||||
|
||||
private static void CreateProgressSection(Transform parent, TMP_FontAsset font) {
|
||||
GameObject progressSection = new GameObject("ProgressSection");
|
||||
progressSection.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = progressSection.AddComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(720, 50);
|
||||
|
||||
VerticalLayoutGroup layout = progressSection.AddComponent<VerticalLayoutGroup>();
|
||||
layout.spacing = 8;
|
||||
layout.childAlignment = TextAnchor.MiddleCenter;
|
||||
layout.childControlHeight = false;
|
||||
layout.childControlWidth = true;
|
||||
|
||||
LayoutElement layoutElement = progressSection.AddComponent<LayoutElement>();
|
||||
layoutElement.preferredHeight = 50;
|
||||
|
||||
// Progress Text
|
||||
GameObject progressTextObj = new GameObject("ProgressText");
|
||||
progressTextObj.transform.SetParent(progressSection.transform, false);
|
||||
|
||||
RectTransform textRect = progressTextObj.AddComponent<RectTransform>();
|
||||
textRect.sizeDelta = new Vector2(720, 24);
|
||||
|
||||
TextMeshProUGUI progressText = progressTextObj.AddComponent<TextMeshProUGUI>();
|
||||
progressText.text = "Step 1 of 1";
|
||||
progressText.fontSize = 14;
|
||||
progressText.color = TextColor;
|
||||
progressText.alignment = TextAlignmentOptions.Center;
|
||||
if (font != null) progressText.font = font;
|
||||
|
||||
LayoutElement textLayout = progressTextObj.AddComponent<LayoutElement>();
|
||||
textLayout.preferredHeight = 24;
|
||||
|
||||
// Progress Slider
|
||||
CreateProgressSlider(progressSection.transform);
|
||||
}
|
||||
|
||||
private static void CreateProgressSlider(Transform parent) {
|
||||
GameObject sliderObj = new GameObject("ProgressSlider");
|
||||
sliderObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform sliderRect = sliderObj.AddComponent<RectTransform>();
|
||||
sliderRect.sizeDelta = new Vector2(400, 12);
|
||||
|
||||
Slider slider = sliderObj.AddComponent<Slider>();
|
||||
slider.minValue = 0;
|
||||
slider.maxValue = 1;
|
||||
slider.value = 0;
|
||||
slider.interactable = false;
|
||||
|
||||
LayoutElement sliderLayout = sliderObj.AddComponent<LayoutElement>();
|
||||
sliderLayout.preferredHeight = 12;
|
||||
sliderLayout.preferredWidth = 400;
|
||||
|
||||
// Background
|
||||
GameObject bgObj = new GameObject("Background");
|
||||
bgObj.transform.SetParent(sliderObj.transform, false);
|
||||
|
||||
RectTransform bgRect = bgObj.AddComponent<RectTransform>();
|
||||
bgRect.anchorMin = Vector2.zero;
|
||||
bgRect.anchorMax = Vector2.one;
|
||||
bgRect.offsetMin = Vector2.zero;
|
||||
bgRect.offsetMax = Vector2.zero;
|
||||
|
||||
Image bgImage = bgObj.AddComponent<Image>();
|
||||
bgImage.color = ProgressBackgroundColor;
|
||||
|
||||
// Fill Area
|
||||
GameObject fillArea = new GameObject("Fill Area");
|
||||
fillArea.transform.SetParent(sliderObj.transform, false);
|
||||
|
||||
RectTransform fillAreaRect = fillArea.AddComponent<RectTransform>();
|
||||
fillAreaRect.anchorMin = Vector2.zero;
|
||||
fillAreaRect.anchorMax = Vector2.one;
|
||||
fillAreaRect.offsetMin = Vector2.zero;
|
||||
fillAreaRect.offsetMax = Vector2.zero;
|
||||
|
||||
// Fill
|
||||
GameObject fill = new GameObject("Fill");
|
||||
fill.transform.SetParent(fillArea.transform, false);
|
||||
|
||||
RectTransform fillRect = fill.AddComponent<RectTransform>();
|
||||
fillRect.anchorMin = Vector2.zero;
|
||||
fillRect.anchorMax = new Vector2(0, 1);
|
||||
fillRect.offsetMin = Vector2.zero;
|
||||
fillRect.offsetMax = Vector2.zero;
|
||||
|
||||
Image fillImage = fill.AddComponent<Image>();
|
||||
fillImage.color = ProgressBarColor;
|
||||
|
||||
// Wire up slider
|
||||
slider.fillRect = fillRect;
|
||||
}
|
||||
|
||||
private static void SetupButtonListeners(TutorialModalPanel panel) {
|
||||
// Button listeners need to be set up after wiring since Awake runs before fields exist
|
||||
if (panel.ContinueButton != null) {
|
||||
panel.ContinueButton.onClick.RemoveAllListeners();
|
||||
panel.ContinueButton.onClick.AddListener(panel.OnContinueClicked);
|
||||
}
|
||||
if (panel.SkipAllButton != null) {
|
||||
panel.SkipAllButton.onClick.RemoveAllListeners();
|
||||
panel.SkipAllButton.onClick.AddListener(panel.OnSkipAllClicked);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateButtonRow(Transform parent, TMP_FontAsset font) {
|
||||
GameObject buttonRow = new GameObject("ButtonRow");
|
||||
buttonRow.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = buttonRow.AddComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(720, 60);
|
||||
|
||||
HorizontalLayoutGroup layout = buttonRow.AddComponent<HorizontalLayoutGroup>();
|
||||
layout.spacing = 20;
|
||||
layout.childAlignment = TextAnchor.MiddleCenter;
|
||||
layout.childControlHeight = false;
|
||||
layout.childControlWidth = false;
|
||||
layout.childForceExpandHeight = false;
|
||||
layout.childForceExpandWidth = false;
|
||||
|
||||
LayoutElement layoutElement = buttonRow.AddComponent<LayoutElement>();
|
||||
layoutElement.preferredHeight = 44;
|
||||
|
||||
// Skip Tutorial Button (left) - only shown during onboarding
|
||||
CreateButton(buttonRow.transform, "SkipAllButton", "Skip Tutorial", 140, 36, font);
|
||||
|
||||
// Spacer
|
||||
GameObject spacer = new GameObject("ButtonSpacer");
|
||||
spacer.transform.SetParent(buttonRow.transform, false);
|
||||
spacer.AddComponent<RectTransform>();
|
||||
LayoutElement spacerLayout = spacer.AddComponent<LayoutElement>();
|
||||
spacerLayout.flexibleWidth = 1;
|
||||
|
||||
// Continue Button (right)
|
||||
CreateButton(buttonRow.transform, "ContinueButton", "Continue", 120, 36, font);
|
||||
}
|
||||
|
||||
private static GameObject CreateButton(
|
||||
Transform parent,
|
||||
string name,
|
||||
string text,
|
||||
float width,
|
||||
float height,
|
||||
TMP_FontAsset font) {
|
||||
GameObject buttonObj = new GameObject(name);
|
||||
buttonObj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = buttonObj.AddComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(width, height);
|
||||
|
||||
Image image = buttonObj.AddComponent<Image>();
|
||||
image.color = ButtonColor;
|
||||
|
||||
Button button = buttonObj.AddComponent<Button>();
|
||||
ColorBlock colors = button.colors;
|
||||
colors.normalColor = ButtonColor;
|
||||
colors.highlightedColor = ButtonHoverColor;
|
||||
colors.pressedColor = ButtonHoverColor;
|
||||
colors.selectedColor = ButtonHoverColor;
|
||||
button.colors = colors;
|
||||
|
||||
// Add outline
|
||||
Outline outline = buttonObj.AddComponent<Outline>();
|
||||
outline.effectColor = BorderColor;
|
||||
outline.effectDistance = new Vector2(2, 2);
|
||||
|
||||
LayoutElement layoutElement = buttonObj.AddComponent<LayoutElement>();
|
||||
layoutElement.preferredWidth = width;
|
||||
layoutElement.preferredHeight = height;
|
||||
|
||||
// Button text
|
||||
GameObject textObj = new GameObject("Text");
|
||||
textObj.transform.SetParent(buttonObj.transform, false);
|
||||
|
||||
RectTransform textRect = textObj.AddComponent<RectTransform>();
|
||||
textRect.anchorMin = Vector2.zero;
|
||||
textRect.anchorMax = Vector2.one;
|
||||
textRect.offsetMin = Vector2.zero;
|
||||
textRect.offsetMax = Vector2.zero;
|
||||
|
||||
TextMeshProUGUI buttonText = textObj.AddComponent<TextMeshProUGUI>();
|
||||
buttonText.text = text;
|
||||
buttonText.fontSize = 16;
|
||||
buttonText.color = ButtonTextColor;
|
||||
buttonText.alignment = TextAlignmentOptions.Center;
|
||||
if (font != null) buttonText.font = font;
|
||||
|
||||
return buttonObj;
|
||||
}
|
||||
|
||||
private static void WireUpModalPanel(
|
||||
TutorialModalPanel panel,
|
||||
GameObject blocker,
|
||||
GameObject panelContainer,
|
||||
TMP_FontAsset font) {
|
||||
panel.ModalBlocker = blocker;
|
||||
panel.PanelContainer = panelContainer;
|
||||
|
||||
// Find and wire up components
|
||||
Transform containerTransform = panelContainer.transform;
|
||||
|
||||
// Title
|
||||
Transform titleTransform = containerTransform.Find("TitleText");
|
||||
if (titleTransform != null) {
|
||||
panel.TitleText = titleTransform.GetComponent<TextMeshProUGUI>();
|
||||
}
|
||||
|
||||
// Icon
|
||||
Transform iconTransform = containerTransform.Find("IconImage");
|
||||
if (iconTransform != null) { panel.IconImage = iconTransform.GetComponent<Image>(); }
|
||||
|
||||
// Description
|
||||
Transform descTransform = containerTransform.Find("DescriptionText");
|
||||
if (descTransform != null) {
|
||||
panel.DescriptionText = descTransform.GetComponent<TextMeshProUGUI>();
|
||||
}
|
||||
|
||||
// Progress Section
|
||||
Transform progressSection = containerTransform.Find("ProgressSection");
|
||||
if (progressSection != null) {
|
||||
panel.ProgressSection = progressSection.gameObject;
|
||||
|
||||
Transform progressTextTransform = progressSection.Find("ProgressText");
|
||||
if (progressTextTransform != null) {
|
||||
panel.ProgressText = progressTextTransform.GetComponent<TextMeshProUGUI>();
|
||||
}
|
||||
|
||||
Transform sliderTransform = progressSection.Find("ProgressSlider");
|
||||
if (sliderTransform != null) {
|
||||
panel.ProgressSlider = sliderTransform.GetComponent<Slider>();
|
||||
}
|
||||
}
|
||||
|
||||
// Button Row
|
||||
Transform buttonRow = containerTransform.Find("ButtonRow");
|
||||
if (buttonRow != null) {
|
||||
Transform skipAllTransform = buttonRow.Find("SkipAllButton");
|
||||
if (skipAllTransform != null) {
|
||||
panel.SkipAllButton = skipAllTransform.GetComponent<Button>();
|
||||
}
|
||||
|
||||
Transform continueTransform = buttonRow.Find("ContinueButton");
|
||||
if (continueTransform != null) {
|
||||
panel.ContinueButton = continueTransform.GetComponent<Button>();
|
||||
Transform continueTextTransform = continueTransform.Find("Text");
|
||||
if (continueTextTransform != null) {
|
||||
panel.ContinueButtonText =
|
||||
continueTextTransform.GetComponent<TextMeshProUGUI>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98467fdbaf584fbb928a373340b542d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
-360
@@ -1,360 +0,0 @@
|
||||
using System;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Eagle0.Tutorial {
|
||||
/// <summary>
|
||||
/// Modal dialog panel for displaying tutorial content.
|
||||
/// Shows title, description, icon, and action buttons.
|
||||
/// </summary>
|
||||
public class TutorialModalPanel : MonoBehaviour {
|
||||
[Header("Content")]
|
||||
[Tooltip("Title text")]
|
||||
public TextMeshProUGUI TitleText;
|
||||
|
||||
[Tooltip("Description text")]
|
||||
public TextMeshProUGUI DescriptionText;
|
||||
|
||||
[Tooltip("Icon image")]
|
||||
public Image IconImage;
|
||||
|
||||
[Header("Buttons")]
|
||||
[Tooltip("Continue/Got it button")]
|
||||
public Button ContinueButton;
|
||||
|
||||
[Tooltip("Text on continue button")]
|
||||
public TextMeshProUGUI ContinueButtonText;
|
||||
|
||||
[Tooltip("Skip tutorial button (onboarding only)")]
|
||||
public Button SkipAllButton;
|
||||
|
||||
[Header("Progress")]
|
||||
[Tooltip("Progress section container (hidden for single-step tutorials)")]
|
||||
public GameObject ProgressSection;
|
||||
|
||||
[Tooltip("Progress text (e.g., 'Step 3 of 7')")]
|
||||
public TextMeshProUGUI ProgressText;
|
||||
|
||||
[Tooltip("Progress slider/bar")]
|
||||
public Slider ProgressSlider;
|
||||
|
||||
[Header("Background")]
|
||||
[Tooltip("Modal blocker/dimmer")]
|
||||
public GameObject ModalBlocker;
|
||||
|
||||
[Tooltip("Panel container")]
|
||||
public GameObject PanelContainer;
|
||||
|
||||
[Header("Animation")]
|
||||
[Tooltip("Animator for panel animations")]
|
||||
public Animator PanelAnimator;
|
||||
|
||||
// Callbacks
|
||||
private Action _onContinue;
|
||||
|
||||
private void Awake() {
|
||||
// Set up button listeners
|
||||
if (ContinueButton != null) { ContinueButton.onClick.AddListener(OnContinueClicked); }
|
||||
if (SkipAllButton != null) { SkipAllButton.onClick.AddListener(OnSkipAllClicked); }
|
||||
|
||||
// Start hidden
|
||||
Hide();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the modal with tutorial step content.
|
||||
/// </summary>
|
||||
public void Show(
|
||||
TutorialStep step,
|
||||
int currentIndex,
|
||||
int totalSteps,
|
||||
Action onContinue,
|
||||
bool isOnboarding) {
|
||||
_onContinue = onContinue;
|
||||
|
||||
// Set content
|
||||
if (TitleText != null) { TitleText.text = step.Title ?? ""; }
|
||||
|
||||
if (DescriptionText != null) {
|
||||
DescriptionText.text = step.Description ?? "";
|
||||
// Force layout rebuild so ContentSizeFitter recalculates
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(
|
||||
DescriptionText.GetComponent<RectTransform>());
|
||||
}
|
||||
|
||||
if (IconImage != null) {
|
||||
if (step.Icon != null) {
|
||||
IconImage.sprite = step.Icon;
|
||||
IconImage.gameObject.SetActive(true);
|
||||
} else {
|
||||
IconImage.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Set button text
|
||||
if (ContinueButtonText != null) {
|
||||
ContinueButtonText.text = currentIndex >= totalSteps - 1 ? "Got it!" : "Continue";
|
||||
}
|
||||
|
||||
// Show Skip Tutorial button only during onboarding (and when skipping is allowed)
|
||||
if (SkipAllButton != null) {
|
||||
SkipAllButton.gameObject.SetActive(isOnboarding && step.AllowSkip);
|
||||
}
|
||||
|
||||
// Set progress - hide entire section for single-step tutorials
|
||||
bool showProgress = totalSteps > 1;
|
||||
if (ProgressSection != null) { ProgressSection.SetActive(showProgress); }
|
||||
if (showProgress) {
|
||||
if (ProgressText != null) {
|
||||
ProgressText.text = $"Step {currentIndex + 1} of {totalSteps}";
|
||||
}
|
||||
if (ProgressSlider != null) {
|
||||
ProgressSlider.value = (float)(currentIndex + 1) / totalSteps;
|
||||
}
|
||||
}
|
||||
|
||||
// Force layout rebuild so panel resizes to fit content
|
||||
if (PanelContainer != null) {
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(
|
||||
PanelContainer.GetComponent<RectTransform>());
|
||||
}
|
||||
|
||||
// Position panel - either adjacent to target or based on anchor setting
|
||||
if (!string.IsNullOrEmpty(step.AdjacentTargetPath)) {
|
||||
PositionAdjacentTo(step.AdjacentTargetPath, step.PanelAnchor);
|
||||
} else {
|
||||
PositionPanel(step.PanelAnchor);
|
||||
}
|
||||
|
||||
// Show panel - ensure all parent containers are active first
|
||||
ActivateParents();
|
||||
|
||||
// Only show modal blocker if step blocks interaction
|
||||
if (ModalBlocker != null) { ModalBlocker.SetActive(step.BlocksInteraction); }
|
||||
|
||||
if (PanelContainer != null) { PanelContainer.SetActive(true); }
|
||||
gameObject.SetActive(true);
|
||||
|
||||
// Trigger animation
|
||||
if (PanelAnimator != null) { PanelAnimator.SetTrigger("Show"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Positions the panel based on the anchor setting.
|
||||
/// Size is determined by ContentSizeFitter, so we only set position.
|
||||
/// </summary>
|
||||
private void PositionPanel(TutorialPanelAnchor anchor) {
|
||||
if (PanelContainer == null) return;
|
||||
|
||||
RectTransform rect = PanelContainer.GetComponent<RectTransform>();
|
||||
if (rect == null) return;
|
||||
|
||||
switch (anchor) {
|
||||
case TutorialPanelAnchor.Center:
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.anchoredPosition = Vector2.zero;
|
||||
break;
|
||||
|
||||
case TutorialPanelAnchor.Left:
|
||||
rect.anchorMin = new Vector2(0f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0f, 0.5f);
|
||||
rect.pivot = new Vector2(0f, 0.5f);
|
||||
rect.anchoredPosition = new Vector2(20f, 0f); // 20px padding from edge
|
||||
break;
|
||||
|
||||
case TutorialPanelAnchor.Right:
|
||||
rect.anchorMin = new Vector2(1f, 0.5f);
|
||||
rect.anchorMax = new Vector2(1f, 0.5f);
|
||||
rect.pivot = new Vector2(1f, 0.5f);
|
||||
rect.anchoredPosition = new Vector2(-20f, 0f); // 20px padding from edge
|
||||
break;
|
||||
|
||||
case TutorialPanelAnchor.Top:
|
||||
rect.anchorMin = new Vector2(0.5f, 1f);
|
||||
rect.anchorMax = new Vector2(0.5f, 1f);
|
||||
rect.pivot = new Vector2(0.5f, 1f);
|
||||
rect.anchoredPosition = new Vector2(0f, -20f); // 20px padding from edge
|
||||
break;
|
||||
|
||||
case TutorialPanelAnchor.Bottom:
|
||||
rect.anchorMin = new Vector2(0.5f, 0f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0f);
|
||||
rect.pivot = new Vector2(0.5f, 0f);
|
||||
rect.anchoredPosition = new Vector2(0f, 20f); // 20px padding from edge
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Positions the panel adjacent to a target GameObject.
|
||||
/// Size is determined by ContentSizeFitter, so we only set position.
|
||||
/// </summary>
|
||||
private void PositionAdjacentTo(string targetId, TutorialPanelAnchor side) {
|
||||
if (PanelContainer == null) return;
|
||||
|
||||
// Use registry to find target
|
||||
Transform target = GetTarget(targetId);
|
||||
if (target == null) {
|
||||
Debug.LogWarning($"TutorialModalPanel: Could not find target '{targetId}'");
|
||||
PositionPanel(side);
|
||||
return;
|
||||
}
|
||||
|
||||
RectTransform targetRect = target.GetComponent<RectTransform>();
|
||||
if (targetRect == null) {
|
||||
PositionPanel(side);
|
||||
return;
|
||||
}
|
||||
|
||||
RectTransform panelRect = PanelContainer.GetComponent<RectTransform>();
|
||||
if (panelRect == null) return;
|
||||
|
||||
// Get canvas for coordinate conversion
|
||||
Canvas canvas = GetComponentInParent<Canvas>();
|
||||
RectTransform canvasRect = canvas?.GetComponent<RectTransform>();
|
||||
|
||||
if (canvasRect == null) {
|
||||
PositionPanel(side);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get target bounds in screen space
|
||||
Vector3[] targetCorners = new Vector3[4];
|
||||
targetRect.GetWorldCorners(targetCorners);
|
||||
|
||||
// Convert target corners to canvas space
|
||||
for (int i = 0; i < 4; i++) {
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
canvasRect,
|
||||
RectTransformUtility.WorldToScreenPoint(
|
||||
canvas.worldCamera,
|
||||
targetCorners[i]),
|
||||
canvas.worldCamera,
|
||||
out Vector2 localPoint);
|
||||
targetCorners[i] = localPoint;
|
||||
}
|
||||
|
||||
// Calculate target bounds
|
||||
float targetLeft = Mathf.Min(targetCorners[0].x, targetCorners[1].x);
|
||||
float targetRight = Mathf.Max(targetCorners[2].x, targetCorners[3].x);
|
||||
float targetBottom = Mathf.Min(targetCorners[0].y, targetCorners[3].y);
|
||||
float targetTop = Mathf.Max(targetCorners[1].y, targetCorners[2].y);
|
||||
float targetCenterX = (targetLeft + targetRight) / 2f;
|
||||
float targetWidth = targetRight - targetLeft;
|
||||
|
||||
float padding = 10f;
|
||||
Vector2 position;
|
||||
|
||||
// Use center anchor for the panel
|
||||
panelRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
panelRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
|
||||
// Position based on which side
|
||||
switch (side) {
|
||||
case TutorialPanelAnchor.Top:
|
||||
// Position panel just above target, with bottom edge touching target top
|
||||
// Use bottom-center pivot so panel extends upward from the target
|
||||
panelRect.pivot = new Vector2(0.5f, 0f);
|
||||
position = new Vector2(targetCenterX, targetTop + padding);
|
||||
|
||||
// Ensure minimum width so text isn't too tall
|
||||
if (panelRect.sizeDelta.x < 720f) {
|
||||
panelRect.sizeDelta = new Vector2(720f, panelRect.sizeDelta.y);
|
||||
}
|
||||
break;
|
||||
|
||||
case TutorialPanelAnchor.Bottom:
|
||||
// Position panel below target with top edge aligned
|
||||
panelRect.pivot = new Vector2(0.5f, 1f);
|
||||
position = new Vector2(targetCenterX, targetBottom - padding);
|
||||
break;
|
||||
|
||||
case TutorialPanelAnchor.Right:
|
||||
// Position panel to the right of target
|
||||
panelRect.pivot = new Vector2(0f, 0.5f);
|
||||
position = new Vector2(targetRight + padding, (targetTop + targetBottom) / 2f);
|
||||
break;
|
||||
|
||||
case TutorialPanelAnchor.Left:
|
||||
// Position panel to the left of target
|
||||
panelRect.pivot = new Vector2(1f, 0.5f);
|
||||
position = new Vector2(targetLeft - padding, (targetTop + targetBottom) / 2f);
|
||||
break;
|
||||
|
||||
default: PositionPanel(side); return;
|
||||
}
|
||||
|
||||
// Clamp X to stay within canvas bounds
|
||||
float canvasHalfWidth = canvasRect.rect.width / 2f;
|
||||
float screenPadding = 20f;
|
||||
position.x = Mathf.Clamp(
|
||||
position.x,
|
||||
-canvasHalfWidth + screenPadding,
|
||||
canvasHalfWidth - screenPadding);
|
||||
|
||||
panelRect.anchoredPosition = position;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates all parent GameObjects to ensure this object can be active in hierarchy.
|
||||
/// </summary>
|
||||
private void ActivateParents() {
|
||||
Transform parent = transform.parent;
|
||||
while (parent != null) {
|
||||
if (!parent.gameObject.activeSelf) { parent.gameObject.SetActive(true); }
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the modal panel.
|
||||
/// </summary>
|
||||
public void Hide() {
|
||||
if (PanelAnimator != null) { PanelAnimator.SetTrigger("Hide"); }
|
||||
|
||||
if (ModalBlocker != null) { ModalBlocker.SetActive(false); }
|
||||
if (PanelContainer != null) { PanelContainer.SetActive(false); }
|
||||
gameObject.SetActive(false);
|
||||
|
||||
_onContinue = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when Continue button is clicked.
|
||||
/// Public to allow external setup of button listeners.
|
||||
/// </summary>
|
||||
public void OnContinueClicked() {
|
||||
var callback = _onContinue;
|
||||
Hide();
|
||||
callback?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when Skip Tutorial button is clicked.
|
||||
/// Skips all remaining tutorial steps. User can restart from Settings.
|
||||
/// </summary>
|
||||
public void OnSkipAllClicked() {
|
||||
Hide();
|
||||
TutorialManager.Instance?.SkipAllOnboarding();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a target Transform by ID, using the registry if available.
|
||||
/// Falls back to GameObject.Find for unregistered targets.
|
||||
/// </summary>
|
||||
private Transform GetTarget(string targetId) {
|
||||
if (string.IsNullOrEmpty(targetId)) return null;
|
||||
|
||||
// Try registry first
|
||||
var registry = TutorialManager.Instance?.TargetRegistry;
|
||||
if (registry != null) { return registry.GetTargetOrFind(targetId); }
|
||||
|
||||
// Fallback to GameObject.Find
|
||||
var targetObj = GameObject.Find(targetId);
|
||||
return targetObj?.transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 461d5eac2fdd4d509a15185a42677886
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+1
-1
@@ -8,7 +8,7 @@ namespace Eagle0.Tutorial {
|
||||
/// Creates highlight frame, tooltip, and dimmer components for TutorialOverlayController.
|
||||
/// </summary>
|
||||
public static class TutorialOverlayBuilder {
|
||||
// Colors matching the game's fantasy RPG style (same as TutorialCanvasBuilder)
|
||||
// Colors matching the game's fantasy RPG style
|
||||
private static readonly Color DimmerColor = new Color(0f, 0f, 0f, 0.7f);
|
||||
private static readonly Color HighlightBorderColor =
|
||||
new Color(1f, 0.85f, 0.4f, 1f); // Gold
|
||||
|
||||
+1
-40
@@ -10,9 +10,6 @@ namespace Eagle0.Tutorial {
|
||||
/// </summary>
|
||||
public class TutorialUIManager : MonoBehaviour {
|
||||
[Header("UI Components")]
|
||||
[Tooltip("Modal dialog panel for important tutorials")]
|
||||
public TutorialModalPanel ModalPanel;
|
||||
|
||||
[Tooltip("Overlay controller for highlighting UI elements")]
|
||||
public TutorialOverlayController OverlayController;
|
||||
|
||||
@@ -51,9 +48,6 @@ namespace Eagle0.Tutorial {
|
||||
private bool _fallbackIsOnboarding;
|
||||
|
||||
private void Awake() {
|
||||
// Auto-build Canvas UI if needed
|
||||
if (ModalPanel == null && AutoBuildCanvasUI) { BuildCanvasUI(); }
|
||||
|
||||
// Auto-build overlay UI if needed
|
||||
if (OverlayController == null && AutoBuildCanvasUI && TutorialCanvas != null) {
|
||||
BuildOverlayUI();
|
||||
@@ -65,25 +59,6 @@ namespace Eagle0.Tutorial {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the Canvas-based tutorial UI at runtime.
|
||||
/// Called automatically if AutoBuildCanvasUI is true and ModalPanel is not assigned.
|
||||
/// </summary>
|
||||
private void BuildCanvasUI() {
|
||||
TutorialCanvas = TutorialCanvasBuilder.BuildTutorialCanvas(CanvasFont);
|
||||
if (TutorialCanvas != null) {
|
||||
// Make canvas a child of this object so it persists with TutorialManager
|
||||
TutorialCanvas.transform.SetParent(transform, false);
|
||||
|
||||
// Get the modal panel that was created
|
||||
ModalPanel = TutorialCanvas.GetComponent<TutorialModalPanel>();
|
||||
|
||||
Debug.Log("TutorialUIManager: Built Canvas UI at runtime");
|
||||
} else {
|
||||
Debug.LogWarning("TutorialUIManager: Failed to build Canvas UI");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the overlay UI components at runtime.
|
||||
/// Called automatically if AutoBuildCanvasUI is true and OverlayController is not assigned.
|
||||
@@ -284,20 +259,7 @@ namespace Eagle0.Tutorial {
|
||||
}
|
||||
}
|
||||
|
||||
if (ModalPanel != null) {
|
||||
// Ensure canvas is active
|
||||
if (TutorialCanvas != null && !TutorialCanvas.gameObject.activeSelf) {
|
||||
TutorialCanvas.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
// Wrap callback to clear highlight when modal is dismissed
|
||||
Action wrappedCallback = () => {
|
||||
OverlayController?.ClearHighlight();
|
||||
onComplete?.Invoke();
|
||||
};
|
||||
|
||||
ModalPanel.Show(step, currentIndex, totalSteps, wrappedCallback, isOnboarding);
|
||||
} else if (UseFallbackUI) {
|
||||
if (UseFallbackUI) {
|
||||
ShowFallbackModal(step, currentIndex, totalSteps, onComplete, isOnboarding);
|
||||
} else {
|
||||
Debug.LogWarning("TutorialUIManager: No ModalPanel assigned and fallback disabled");
|
||||
@@ -389,7 +351,6 @@ namespace Eagle0.Tutorial {
|
||||
/// Hides all tutorial UI.
|
||||
/// </summary>
|
||||
public void HideAll() {
|
||||
ModalPanel?.Hide();
|
||||
OverlayController?.HideOverlay();
|
||||
HideFallbackModal();
|
||||
// Note: Don't hide hints automatically - they persist until dismissed
|
||||
|
||||
Reference in New Issue
Block a user