app.hypergames.hypersdk 1.8.19 → 1.8.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/HyperLoader.unity +3 -3
- package/Runtime/Internal/Managers/DataManager.cs +38 -2
- package/Runtime/Internal/Managers/GameManager.cs +34 -23
- package/Runtime/Internal/UI/Animation/SlideUp.cs +6 -1
- package/Runtime/Internal/UI/Components/RoundedRawImage.cs +146 -0
- package/Runtime/Internal/UI/Components/RoundedRawImage.cs.meta +11 -0
- package/Runtime/Internal/UI/Modals/GamePausedModal.cs +2 -0
- package/Runtime/Internal/UI/Modals/TutorialButton.cs +10 -0
- package/Runtime/Internal/UI/Modals/TutorialLoaderModal.cs +31 -4
- package/Runtime/Internal/UI/Modals/TutorialModal.cs +814 -47
- package/Runtime/Plugins/WebGL/HyperTutorialCachePlugin.jslib +35 -53
- package/Runtime/Resources/Materials/RoundedRawImage.mat +65 -0
- package/Runtime/Resources/Materials/RoundedRawImage.mat.meta +8 -0
- package/Runtime/Resources/Materials.meta +8 -0
- package/Runtime/Resources/RenderTextures/TutorialVideoTexture P.renderTexture +3 -3
- package/Runtime/Resources/Shaders/RoundedRawImage.shader +142 -0
- package/Runtime/Resources/Shaders/RoundedRawImage.shader.meta +10 -0
- package/Runtime/Resources/Shaders/RoundedStrip.shader +152 -0
- package/Runtime/Resources/Shaders/RoundedStrip.shader.meta +10 -0
- package/Runtime/Resources/Shaders.meta +8 -0
- package/Runtime/Resources/Sprites/Frames/Square.png +0 -0
- package/Runtime/Resources/Sprites/Frames/Square.png.meta +186 -0
- package/Runtime/Resources/Sprites/TutorialModal/Button_Back.png.meta +1 -1
- package/Runtime/Resources/Sprites/TutorialModal/Button_Next.png.meta +1 -1
- package/Runtime/Resources/Sprites/TutorialModal/Knob.png.meta +1 -1
- package/Runtime/Resources/Sprites/TutorialModal/Tutorial_Background_Portrait.png.meta +3 -3
- package/Runtime/Resources/UIPrefabs/GameOverModal_Battle.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/GameOverModal_Practice.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/GamePausedModal_Battle.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/IndicatorCircle.prefab +2 -2
- package/Runtime/Resources/UIPrefabs/TutorialLandscapeModal.prefab +1614 -1184
- package/Runtime/Resources/UIPrefabs/TutorialLandscapeModal.prefab.meta +1 -1
- package/Runtime/Resources/UIPrefabs/TutorialPortraitModal.prefab +1409 -948
- package/Runtime/Shared/DTOs/TutorialData.cs +18 -1
- package/package.json +1 -1
|
@@ -6,13 +6,24 @@ using UnityEngine;
|
|
|
6
6
|
using UnityEngine.UI;
|
|
7
7
|
using UnityEngine.Video;
|
|
8
8
|
using UnityEngine.Networking;
|
|
9
|
+
using Hyper.Internal.Animation;
|
|
9
10
|
using Hyper.Internal.Managers;
|
|
10
11
|
using Hyper.Shared;
|
|
12
|
+
using TMPro;
|
|
11
13
|
|
|
12
14
|
namespace Hyper.Internal
|
|
13
15
|
{
|
|
14
16
|
internal class TutorialModal : MonoBehaviour
|
|
15
17
|
{
|
|
18
|
+
// Keep the seek wiring available while the tutorial video uses a passive progress bar.
|
|
19
|
+
private const bool VideoSeekingEnabled = false;
|
|
20
|
+
private const float LoaderFadeOutDelay = 2.15f;
|
|
21
|
+
private const float SlideInDuration = 0.33f;
|
|
22
|
+
private const float ContentFadeInDuration = 0.28f;
|
|
23
|
+
private const float ContentFadeOutDuration = 0.18f;
|
|
24
|
+
private const float MediaFadeInDuration = 0.18f;
|
|
25
|
+
private const float VideoRevealDelay = 0.05f;
|
|
26
|
+
private const float HiddenPreloadPlayDuration = 0.75f;
|
|
16
27
|
|
|
17
28
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
18
29
|
[DllImport("__Internal")]
|
|
@@ -23,20 +34,25 @@ namespace Hyper.Internal
|
|
|
23
34
|
private CanvasGroup tutorialCG;
|
|
24
35
|
|
|
25
36
|
[SerializeField] TutorialLoaderModal tutorialLoaderModal;
|
|
37
|
+
[SerializeField] private CanvasGroup tutorialContentGroup;
|
|
26
38
|
|
|
27
39
|
[Header("Display Components")]
|
|
28
40
|
[SerializeField] private RawImage tutorialDisplayP;
|
|
29
41
|
[SerializeField] private RawImage tutorialDisplayL;
|
|
30
42
|
[SerializeField] private VideoPlayer videoPlayer;
|
|
43
|
+
[SerializeField] private RawImage tutorialImageDisplay;
|
|
44
|
+
[SerializeField] private TMP_Text descriptionText;
|
|
45
|
+
|
|
46
|
+
#if UNITY_EDITOR
|
|
47
|
+
[Header("Editor Tutorial Test")]
|
|
48
|
+
[SerializeField] private EditorTutorialCard[] editorTutorialCards;
|
|
49
|
+
[SerializeField] private Vector2 editorLoadDelayRange = new Vector2(1f, 2f);
|
|
50
|
+
#endif
|
|
31
51
|
|
|
32
52
|
[Header("Video Textures")]
|
|
33
53
|
[SerializeField] private RenderTexture tutorialVideoTextureP;
|
|
34
54
|
[SerializeField] private RenderTexture tutorialVideoTextureL;
|
|
35
55
|
|
|
36
|
-
[Header("Aspect Ratio Fitters")]
|
|
37
|
-
[SerializeField] private AspectRatioFitter aspectRatioFitterP;
|
|
38
|
-
[SerializeField] private AspectRatioFitter aspectRatioFitterL;
|
|
39
|
-
|
|
40
56
|
[Header("UI Controls")]
|
|
41
57
|
[SerializeField] private Slider timeSlider;
|
|
42
58
|
[SerializeField] private Button backButton;
|
|
@@ -46,16 +62,39 @@ namespace Hyper.Internal
|
|
|
46
62
|
[SerializeField] private Transform indicatorParent;
|
|
47
63
|
[SerializeField] private GameObject indicatorPrefab;
|
|
48
64
|
private Sprite[] tutorialImages;
|
|
65
|
+
private readonly List<TutorialCardData> tutorialCards = new List<TutorialCardData>();
|
|
49
66
|
private RawImage currentDisplay;
|
|
50
67
|
private int currentImageIndex = 0;
|
|
68
|
+
private int currentCardIndex = 0;
|
|
51
69
|
private bool isDraggingSlider = false;
|
|
52
70
|
private float cachedVideoLength = 0f;
|
|
53
71
|
private Coroutine updateVideoTimeCo = null;
|
|
72
|
+
private Coroutine editorTutorialLoadCo = null;
|
|
73
|
+
private Coroutine hiddenPreloadCloseCo = null;
|
|
74
|
+
private Coroutine mediaDisplayFadeCo = null;
|
|
54
75
|
private TutorialData tutorialData;
|
|
76
|
+
private bool isClosing;
|
|
77
|
+
private bool hasInvokedTutorialLoadComplete;
|
|
78
|
+
private bool hasPreparedTutorialContent;
|
|
79
|
+
private bool isDirectContentOpen;
|
|
80
|
+
private bool contentRevealScheduled;
|
|
81
|
+
private HyperTween.TweenHandle contentRevealDelayTween;
|
|
55
82
|
|
|
56
83
|
private EventManager _eventManager;
|
|
57
84
|
private DataManager _dataManager;
|
|
58
85
|
|
|
86
|
+
#if UNITY_EDITOR
|
|
87
|
+
[Serializable]
|
|
88
|
+
private class EditorTutorialCard
|
|
89
|
+
{
|
|
90
|
+
public TutorialType type = TutorialType.video;
|
|
91
|
+
public VideoClip videoClip;
|
|
92
|
+
public Sprite image;
|
|
93
|
+
[TextArea] public string description;
|
|
94
|
+
public int position;
|
|
95
|
+
}
|
|
96
|
+
#endif
|
|
97
|
+
|
|
59
98
|
private void Awake()
|
|
60
99
|
{
|
|
61
100
|
_eventManager = HyperRuntime.EventManager;
|
|
@@ -63,31 +102,32 @@ namespace Hyper.Internal
|
|
|
63
102
|
|
|
64
103
|
slideUp = GetComponent<SlideUp>();
|
|
65
104
|
tutorialCG = GetComponent<CanvasGroup>();
|
|
105
|
+
Hide();
|
|
66
106
|
|
|
67
107
|
tutorialLoaderModal.gameObject.SetActive(true);
|
|
68
108
|
|
|
69
109
|
_eventManager.OnTutorialResourcesReferenced += ShowTutorialBasedOnType;
|
|
110
|
+
_eventManager.OnTutorialLoadingComplete += HandleTutorialLoadingComplete;
|
|
70
111
|
}
|
|
71
112
|
|
|
72
113
|
void OnEnable()
|
|
73
114
|
{
|
|
74
115
|
SetupSliderControls();
|
|
75
116
|
|
|
117
|
+
if (hasPreparedTutorialContent) return;
|
|
118
|
+
|
|
76
119
|
if (_dataManager.HasReferencedTutorialResources)
|
|
77
120
|
{
|
|
78
|
-
|
|
79
|
-
if (tutorialData != null && tutorialData.type == TutorialType.video)
|
|
80
|
-
{
|
|
81
|
-
PlayCurrentVideo();
|
|
82
|
-
}
|
|
83
|
-
else if(tutorialData != null && tutorialData.type == TutorialType.images)
|
|
84
|
-
{
|
|
85
|
-
ShowImagesTutorial(tutorialData);
|
|
86
|
-
}
|
|
121
|
+
ShowTutorialData(_dataManager.TutorialData);
|
|
87
122
|
}
|
|
88
123
|
else
|
|
89
124
|
{
|
|
90
|
-
|
|
125
|
+
#if UNITY_EDITOR
|
|
126
|
+
if (TryStartEditorTutorialLoad())
|
|
127
|
+
{
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
#endif
|
|
91
131
|
}
|
|
92
132
|
}
|
|
93
133
|
|
|
@@ -99,11 +139,47 @@ namespace Hyper.Internal
|
|
|
99
139
|
|
|
100
140
|
public void Show()
|
|
101
141
|
{
|
|
142
|
+
isClosing = false;
|
|
143
|
+
isDirectContentOpen = false;
|
|
144
|
+
contentRevealScheduled = false;
|
|
145
|
+
CancelHiddenPreloadClose();
|
|
146
|
+
HideTutorialContent();
|
|
102
147
|
tutorialCG.alpha = 1f;
|
|
103
148
|
tutorialCG.interactable = true;
|
|
104
149
|
tutorialCG.blocksRaycasts = true;
|
|
105
150
|
|
|
106
|
-
slideUp.SlideUpAnimation();
|
|
151
|
+
slideUp.SlideUpAnimation(SlideInDuration);
|
|
152
|
+
|
|
153
|
+
if (CanShowPreparedTutorialImmediately())
|
|
154
|
+
{
|
|
155
|
+
ShowPreparedTutorialImmediately();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (tutorialLoaderModal != null)
|
|
160
|
+
{
|
|
161
|
+
tutorialLoaderModal.gameObject.SetActive(true);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (_dataManager != null && _dataManager.HasReferencedTutorialResources)
|
|
165
|
+
{
|
|
166
|
+
ShowTutorialData(_dataManager.TutorialData);
|
|
167
|
+
}
|
|
168
|
+
#if UNITY_EDITOR
|
|
169
|
+
else
|
|
170
|
+
{
|
|
171
|
+
if (!TryStartEditorTutorialLoad())
|
|
172
|
+
{
|
|
173
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
#endif
|
|
177
|
+
#if !UNITY_EDITOR
|
|
178
|
+
else
|
|
179
|
+
{
|
|
180
|
+
HyperRuntime.BackendManager.GetTutorial(() => { });
|
|
181
|
+
}
|
|
182
|
+
#endif
|
|
107
183
|
}
|
|
108
184
|
|
|
109
185
|
private void Hide()
|
|
@@ -111,6 +187,143 @@ namespace Hyper.Internal
|
|
|
111
187
|
tutorialCG.alpha = 0f;
|
|
112
188
|
tutorialCG.interactable = false;
|
|
113
189
|
tutorialCG.blocksRaycasts = false;
|
|
190
|
+
// HideTutorialContent();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private bool CanShowPreparedTutorialImmediately()
|
|
194
|
+
{
|
|
195
|
+
return hasPreparedTutorialContent && tutorialData != null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private void ShowPreparedTutorialImmediately()
|
|
199
|
+
{
|
|
200
|
+
isDirectContentOpen = true;
|
|
201
|
+
|
|
202
|
+
if (tutorialLoaderModal != null)
|
|
203
|
+
{
|
|
204
|
+
tutorialLoaderModal.gameObject.SetActive(false);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
FadeTutorialContentIn(SlideInDuration);
|
|
208
|
+
ShowTutorialData(tutorialData);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private void ShowTutorialContentImmediate()
|
|
212
|
+
{
|
|
213
|
+
if (tutorialContentGroup == null) return;
|
|
214
|
+
|
|
215
|
+
tutorialContentGroup.DOKill();
|
|
216
|
+
tutorialContentGroup.gameObject.SetActive(true);
|
|
217
|
+
tutorialContentGroup.alpha = 1f;
|
|
218
|
+
tutorialContentGroup.interactable = true;
|
|
219
|
+
tutorialContentGroup.blocksRaycasts = true;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private void FadeTutorialContentIn(float delay = 0f)
|
|
223
|
+
{
|
|
224
|
+
if (tutorialContentGroup == null) return;
|
|
225
|
+
|
|
226
|
+
contentRevealScheduled = true;
|
|
227
|
+
contentRevealDelayTween?.Kill();
|
|
228
|
+
contentRevealDelayTween = null;
|
|
229
|
+
tutorialContentGroup.DOKill();
|
|
230
|
+
tutorialContentGroup.gameObject.SetActive(true);
|
|
231
|
+
tutorialContentGroup.alpha = 0f;
|
|
232
|
+
tutorialContentGroup.interactable = false;
|
|
233
|
+
tutorialContentGroup.blocksRaycasts = false;
|
|
234
|
+
|
|
235
|
+
if (delay > 0f)
|
|
236
|
+
{
|
|
237
|
+
contentRevealDelayTween = HyperTween.DelayedCall(delay, StartTutorialContentFade)
|
|
238
|
+
.SetUpdate(true);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
StartTutorialContentFade();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private void StartTutorialContentFade()
|
|
246
|
+
{
|
|
247
|
+
if (tutorialContentGroup == null) return;
|
|
248
|
+
|
|
249
|
+
contentRevealDelayTween = null;
|
|
250
|
+
tutorialContentGroup.DOKill();
|
|
251
|
+
tutorialContentGroup.gameObject.SetActive(true);
|
|
252
|
+
tutorialContentGroup.interactable = true;
|
|
253
|
+
tutorialContentGroup.blocksRaycasts = true;
|
|
254
|
+
var fadeTween = tutorialContentGroup.DOFade(1f, ContentFadeInDuration)
|
|
255
|
+
.SetEase(HyperTween.Ease.OutQuad)
|
|
256
|
+
.SetUpdate(true);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private void HandleTutorialLoadingComplete()
|
|
260
|
+
{
|
|
261
|
+
if (!isActiveAndEnabled || isClosing) return;
|
|
262
|
+
|
|
263
|
+
hasPreparedTutorialContent = true;
|
|
264
|
+
|
|
265
|
+
if (IsHiddenPreload())
|
|
266
|
+
{
|
|
267
|
+
ScheduleHiddenPreloadClose();
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (isDirectContentOpen)
|
|
272
|
+
{
|
|
273
|
+
if (!contentRevealScheduled)
|
|
274
|
+
{
|
|
275
|
+
FadeTutorialContentIn(SlideInDuration);
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (tutorialContentGroup == null) return;
|
|
281
|
+
if (contentRevealScheduled) return;
|
|
282
|
+
|
|
283
|
+
FadeTutorialContentIn(LoaderFadeOutDelay);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private void HideTutorialContent()
|
|
287
|
+
{
|
|
288
|
+
if (tutorialContentGroup == null) return;
|
|
289
|
+
|
|
290
|
+
contentRevealScheduled = false;
|
|
291
|
+
contentRevealDelayTween?.Kill();
|
|
292
|
+
contentRevealDelayTween = null;
|
|
293
|
+
tutorialContentGroup.DOKill();
|
|
294
|
+
tutorialContentGroup.alpha = 0f;
|
|
295
|
+
tutorialContentGroup.interactable = false;
|
|
296
|
+
tutorialContentGroup.blocksRaycasts = false;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private bool IsHiddenPreload()
|
|
300
|
+
{
|
|
301
|
+
return tutorialCG != null && tutorialCG.alpha <= 0.001f && !tutorialCG.blocksRaycasts && !tutorialCG.interactable;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private void ScheduleHiddenPreloadClose()
|
|
305
|
+
{
|
|
306
|
+
CancelHiddenPreloadClose();
|
|
307
|
+
hiddenPreloadCloseCo = StartCoroutine(CloseHiddenPreloadAfterPreview());
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
private IEnumerator CloseHiddenPreloadAfterPreview()
|
|
311
|
+
{
|
|
312
|
+
yield return new WaitForSecondsRealtime(HiddenPreloadPlayDuration);
|
|
313
|
+
|
|
314
|
+
hiddenPreloadCloseCo = null;
|
|
315
|
+
|
|
316
|
+
if (!isActiveAndEnabled || isClosing || !IsHiddenPreload()) yield break;
|
|
317
|
+
|
|
318
|
+
FinishClose();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private void CancelHiddenPreloadClose()
|
|
322
|
+
{
|
|
323
|
+
if (hiddenPreloadCloseCo == null) return;
|
|
324
|
+
|
|
325
|
+
StopCoroutine(hiddenPreloadCloseCo);
|
|
326
|
+
hiddenPreloadCloseCo = null;
|
|
114
327
|
}
|
|
115
328
|
|
|
116
329
|
private void SetupSliderControls()
|
|
@@ -119,19 +332,25 @@ namespace Hyper.Internal
|
|
|
119
332
|
|
|
120
333
|
timeSlider.minValue = 0f;
|
|
121
334
|
timeSlider.wholeNumbers = false;
|
|
122
|
-
timeSlider.
|
|
335
|
+
timeSlider.interactable = VideoSeekingEnabled;
|
|
336
|
+
timeSlider.onValueChanged.RemoveListener(OnTimeSliderChanged);
|
|
123
337
|
|
|
124
|
-
// Add event triggers for drag detection
|
|
125
338
|
var eventTrigger = timeSlider.gameObject.GetComponent<UnityEngine.EventSystems.EventTrigger>();
|
|
339
|
+
if (eventTrigger != null)
|
|
340
|
+
{
|
|
341
|
+
eventTrigger.triggers.Clear();
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!VideoSeekingEnabled) return;
|
|
345
|
+
|
|
346
|
+
timeSlider.onValueChanged.AddListener(OnTimeSliderChanged);
|
|
347
|
+
|
|
348
|
+
eventTrigger = timeSlider.gameObject.GetComponent<UnityEngine.EventSystems.EventTrigger>();
|
|
126
349
|
if (eventTrigger == null)
|
|
127
350
|
{
|
|
128
351
|
eventTrigger = timeSlider.gameObject.AddComponent<UnityEngine.EventSystems.EventTrigger>();
|
|
129
352
|
}
|
|
130
353
|
|
|
131
|
-
// Clear existing triggers
|
|
132
|
-
eventTrigger.triggers.Clear();
|
|
133
|
-
|
|
134
|
-
// Pointer down (begin drag)
|
|
135
354
|
var pointerDownEntry = new UnityEngine.EventSystems.EventTrigger.Entry
|
|
136
355
|
{
|
|
137
356
|
eventID = UnityEngine.EventSystems.EventTriggerType.PointerDown
|
|
@@ -139,7 +358,6 @@ namespace Hyper.Internal
|
|
|
139
358
|
pointerDownEntry.callback.AddListener((_) => OnSliderDragStart());
|
|
140
359
|
eventTrigger.triggers.Add(pointerDownEntry);
|
|
141
360
|
|
|
142
|
-
// Pointer up (end drag)
|
|
143
361
|
var pointerUpEntry = new UnityEngine.EventSystems.EventTrigger.Entry
|
|
144
362
|
{
|
|
145
363
|
eventID = UnityEngine.EventSystems.EventTriggerType.PointerUp
|
|
@@ -150,11 +368,15 @@ namespace Hyper.Internal
|
|
|
150
368
|
|
|
151
369
|
private void OnSliderDragStart()
|
|
152
370
|
{
|
|
371
|
+
if (!VideoSeekingEnabled) return;
|
|
372
|
+
|
|
153
373
|
isDraggingSlider = true;
|
|
154
374
|
}
|
|
155
375
|
|
|
156
376
|
private void OnSliderDragEnd()
|
|
157
377
|
{
|
|
378
|
+
if (!VideoSeekingEnabled) return;
|
|
379
|
+
|
|
158
380
|
isDraggingSlider = false;
|
|
159
381
|
|
|
160
382
|
if (videoPlayer != null && videoPlayer.isPrepared)
|
|
@@ -171,8 +393,24 @@ namespace Hyper.Internal
|
|
|
171
393
|
{
|
|
172
394
|
//Debug.Log("Showing tutorial based on type");
|
|
173
395
|
tutorialData = _dataManager.TutorialData;
|
|
396
|
+
ShowTutorialData(tutorialData);
|
|
397
|
+
}
|
|
174
398
|
|
|
175
|
-
|
|
399
|
+
private void ShowTutorialData(TutorialData data)
|
|
400
|
+
{
|
|
401
|
+
if (data == null)
|
|
402
|
+
{
|
|
403
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
tutorialData = data;
|
|
408
|
+
|
|
409
|
+
if (tutorialData.cards != null && tutorialData.cards.Length > 0)
|
|
410
|
+
{
|
|
411
|
+
ShowMixedTutorial(tutorialData);
|
|
412
|
+
}
|
|
413
|
+
else if (tutorialData.type == TutorialType.video)
|
|
176
414
|
{
|
|
177
415
|
ShowVideoTutorial(tutorialData);
|
|
178
416
|
}
|
|
@@ -187,6 +425,74 @@ namespace Hyper.Internal
|
|
|
187
425
|
}
|
|
188
426
|
}
|
|
189
427
|
|
|
428
|
+
#if UNITY_EDITOR
|
|
429
|
+
internal bool TryStartEditorTutorialLoad()
|
|
430
|
+
{
|
|
431
|
+
TutorialData editorData = CreateEditorTutorialData();
|
|
432
|
+
if (editorData == null) return false;
|
|
433
|
+
|
|
434
|
+
if (editorTutorialLoadCo != null)
|
|
435
|
+
{
|
|
436
|
+
StopCoroutine(editorTutorialLoadCo);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
editorTutorialLoadCo = StartCoroutine(LoadEditorTutorialAfterDelay(editorData));
|
|
440
|
+
return true;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
private TutorialData CreateEditorTutorialData()
|
|
444
|
+
{
|
|
445
|
+
if (editorTutorialCards == null || editorTutorialCards.Length == 0) return null;
|
|
446
|
+
|
|
447
|
+
List<TutorialCardData> editorCards = new List<TutorialCardData>();
|
|
448
|
+
|
|
449
|
+
for (int i = 0; i < editorTutorialCards.Length; i++)
|
|
450
|
+
{
|
|
451
|
+
EditorTutorialCard editorCard = editorTutorialCards[i];
|
|
452
|
+
if (editorCard == null || editorCard.type == TutorialType.None) continue;
|
|
453
|
+
|
|
454
|
+
if (editorCard.type == TutorialType.video && editorCard.videoClip == null) continue;
|
|
455
|
+
if (editorCard.type == TutorialType.images && editorCard.image == null) continue;
|
|
456
|
+
|
|
457
|
+
editorCards.Add(new TutorialCardData
|
|
458
|
+
{
|
|
459
|
+
type = editorCard.type,
|
|
460
|
+
videoClip = editorCard.videoClip,
|
|
461
|
+
image = editorCard.image,
|
|
462
|
+
description = editorCard.description,
|
|
463
|
+
position = editorCard.position > 0 ? editorCard.position : i + 1
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (editorCards.Count == 0) return null;
|
|
468
|
+
|
|
469
|
+
editorCards.Sort((a, b) => a.position.CompareTo(b.position));
|
|
470
|
+
|
|
471
|
+
return new TutorialData
|
|
472
|
+
{
|
|
473
|
+
type = editorCards[0].type,
|
|
474
|
+
cards = editorCards.ToArray()
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
private IEnumerator LoadEditorTutorialAfterDelay(TutorialData editorData)
|
|
479
|
+
{
|
|
480
|
+
tutorialLoaderModal.gameObject.SetActive(true);
|
|
481
|
+
tutorialDisplayP.gameObject.SetActive(false);
|
|
482
|
+
tutorialDisplayL.gameObject.SetActive(false);
|
|
483
|
+
videoPlayer.enabled = false;
|
|
484
|
+
|
|
485
|
+
float minDelay = Mathf.Max(0f, Mathf.Min(editorLoadDelayRange.x, editorLoadDelayRange.y));
|
|
486
|
+
float maxDelay = Mathf.Max(minDelay, Mathf.Max(editorLoadDelayRange.x, editorLoadDelayRange.y));
|
|
487
|
+
float delay = UnityEngine.Random.Range(minDelay, maxDelay);
|
|
488
|
+
|
|
489
|
+
yield return new WaitForSecondsRealtime(delay);
|
|
490
|
+
|
|
491
|
+
editorTutorialLoadCo = null;
|
|
492
|
+
ShowTutorialData(editorData);
|
|
493
|
+
}
|
|
494
|
+
#endif
|
|
495
|
+
|
|
190
496
|
private RawImage SelectOptimalDisplay(float aspectRatio)
|
|
191
497
|
{
|
|
192
498
|
bool isLandscape = aspectRatio > 1.0f;
|
|
@@ -196,20 +502,393 @@ namespace Hyper.Internal
|
|
|
196
502
|
|
|
197
503
|
if (isLandscape)
|
|
198
504
|
{
|
|
199
|
-
//Debug.Log($"Content is landscape (aspect: {aspectRatio:F2}), using landscape display");
|
|
200
505
|
tutorialDisplayL.gameObject.SetActive(true);
|
|
201
506
|
return tutorialDisplayL;
|
|
202
507
|
}
|
|
203
508
|
else
|
|
204
509
|
{
|
|
205
|
-
//Debug.Log($"Content is portrait (aspect: {aspectRatio:F2}), using portrait display");
|
|
206
510
|
tutorialDisplayP.gameObject.SetActive(true);
|
|
207
511
|
return tutorialDisplayP;
|
|
208
512
|
}
|
|
209
513
|
}
|
|
210
514
|
|
|
515
|
+
private void ShowMixedTutorial(TutorialData tutorialData)
|
|
516
|
+
{
|
|
517
|
+
if (tutorialData.cards == null || tutorialData.cards.Length == 0)
|
|
518
|
+
{
|
|
519
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
hasInvokedTutorialLoadComplete = false;
|
|
524
|
+
tutorialCards.Clear();
|
|
525
|
+
tutorialCards.AddRange(tutorialData.cards);
|
|
526
|
+
tutorialCards.Sort((a, b) => a.position.CompareTo(b.position));
|
|
527
|
+
currentCardIndex = 0;
|
|
528
|
+
currentImageIndex = 0;
|
|
529
|
+
|
|
530
|
+
if (tutorialDisplayP != null) tutorialDisplayP.gameObject.SetActive(false);
|
|
531
|
+
if (tutorialDisplayL != null) tutorialDisplayL.gameObject.SetActive(false);
|
|
532
|
+
|
|
533
|
+
if (TryLoadCardImagesFromUrls())
|
|
534
|
+
{
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
SetupMixedTutorialCards();
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
private bool TryLoadCardImagesFromUrls()
|
|
542
|
+
{
|
|
543
|
+
bool hasRemoteImage = false;
|
|
544
|
+
|
|
545
|
+
for (int i = 0; i < tutorialCards.Count; i++)
|
|
546
|
+
{
|
|
547
|
+
TutorialCardData card = tutorialCards[i];
|
|
548
|
+
if (card.type == TutorialType.images && card.image == null && !string.IsNullOrEmpty(card.imageUrl))
|
|
549
|
+
{
|
|
550
|
+
hasRemoteImage = true;
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (!hasRemoteImage) return false;
|
|
556
|
+
|
|
557
|
+
StartCoroutine(LoadCardImagesFromUrlsCoroutine(SetupMixedTutorialCards));
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private IEnumerator LoadCardImagesFromUrlsCoroutine(Action onComplete)
|
|
562
|
+
{
|
|
563
|
+
for (int i = 0; i < tutorialCards.Count; i++)
|
|
564
|
+
{
|
|
565
|
+
TutorialCardData card = tutorialCards[i];
|
|
566
|
+
if (card.type != TutorialType.images || card.image != null || string.IsNullOrEmpty(card.imageUrl)) continue;
|
|
567
|
+
|
|
568
|
+
using (UnityWebRequest webRequest = UnityWebRequestTexture.GetTexture(card.imageUrl))
|
|
569
|
+
{
|
|
570
|
+
yield return webRequest.SendWebRequest();
|
|
571
|
+
|
|
572
|
+
if (webRequest.result == UnityWebRequest.Result.Success)
|
|
573
|
+
{
|
|
574
|
+
Texture2D texture = DownloadHandlerTexture.GetContent(webRequest);
|
|
575
|
+
texture.filterMode = FilterMode.Bilinear;
|
|
576
|
+
texture.Compress(false);
|
|
577
|
+
|
|
578
|
+
card.image = Sprite.Create(
|
|
579
|
+
texture,
|
|
580
|
+
new Rect(0, 0, texture.width, texture.height),
|
|
581
|
+
new Vector2(0.5f, 0.5f),
|
|
582
|
+
100f,
|
|
583
|
+
0,
|
|
584
|
+
SpriteMeshType.Tight
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
onComplete?.Invoke();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private void SetupMixedTutorialCards()
|
|
594
|
+
{
|
|
595
|
+
RemoveInvalidTutorialCards();
|
|
596
|
+
|
|
597
|
+
if (tutorialCards.Count == 0)
|
|
598
|
+
{
|
|
599
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
backButton.gameObject.SetActive(true);
|
|
604
|
+
nextButton.gameObject.SetActive(true);
|
|
605
|
+
indicatorParent.gameObject.SetActive(true);
|
|
606
|
+
|
|
607
|
+
UpdateIndicators(tutorialCards.Count);
|
|
608
|
+
StartCoroutine(ApplyInitialIndicatorHighlightNextFrame());
|
|
609
|
+
DisplayCurrentCard(true);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
private void RemoveInvalidTutorialCards()
|
|
613
|
+
{
|
|
614
|
+
for (int i = tutorialCards.Count - 1; i >= 0; i--)
|
|
615
|
+
{
|
|
616
|
+
TutorialCardData card = tutorialCards[i];
|
|
617
|
+
bool hasVideo = card.type == TutorialType.video && (card.videoClip != null || !string.IsNullOrEmpty(card.videoUrl));
|
|
618
|
+
bool hasImage = card.type == TutorialType.images && card.image != null;
|
|
619
|
+
|
|
620
|
+
if (!hasVideo && !hasImage)
|
|
621
|
+
{
|
|
622
|
+
tutorialCards.RemoveAt(i);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private void ResetCardMediaState()
|
|
628
|
+
{
|
|
629
|
+
StopMediaDisplayFade();
|
|
630
|
+
ResetVideoProgressState();
|
|
631
|
+
HideMediaDisplay(tutorialDisplayP);
|
|
632
|
+
HideMediaDisplay(tutorialDisplayL);
|
|
633
|
+
HideMediaDisplay(tutorialImageDisplay);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
private void ResetVideoProgressState()
|
|
637
|
+
{
|
|
638
|
+
cachedVideoLength = 0f;
|
|
639
|
+
isDraggingSlider = false;
|
|
640
|
+
|
|
641
|
+
if (timeSlider == null) return;
|
|
642
|
+
|
|
643
|
+
timeSlider.minValue = 0f;
|
|
644
|
+
timeSlider.maxValue = 1f;
|
|
645
|
+
timeSlider.value = 0f;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private void HideMediaDisplay(RawImage display)
|
|
649
|
+
{
|
|
650
|
+
if (display == null) return;
|
|
651
|
+
|
|
652
|
+
SetMediaDisplayAlpha(display, 0f);
|
|
653
|
+
display.gameObject.SetActive(false);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private void SetMediaDisplayAlpha(RawImage display, float alpha)
|
|
657
|
+
{
|
|
658
|
+
if (display == null) return;
|
|
659
|
+
|
|
660
|
+
Color color = display.color;
|
|
661
|
+
color.a = alpha;
|
|
662
|
+
display.color = color;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
private void StopMediaDisplayFade()
|
|
666
|
+
{
|
|
667
|
+
if (mediaDisplayFadeCo == null) return;
|
|
668
|
+
|
|
669
|
+
StopCoroutine(mediaDisplayFadeCo);
|
|
670
|
+
mediaDisplayFadeCo = null;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
private void FadeInMediaDisplay(RawImage display, float delay = 0f)
|
|
674
|
+
{
|
|
675
|
+
if (display == null) return;
|
|
676
|
+
|
|
677
|
+
StopMediaDisplayFade();
|
|
678
|
+
mediaDisplayFadeCo = StartCoroutine(FadeInMediaDisplayCoroutine(display, delay));
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
private IEnumerator FadeInMediaDisplayCoroutine(RawImage display, float delay)
|
|
682
|
+
{
|
|
683
|
+
display.gameObject.SetActive(true);
|
|
684
|
+
SetMediaDisplayAlpha(display, 0f);
|
|
685
|
+
|
|
686
|
+
if (delay > 0f)
|
|
687
|
+
{
|
|
688
|
+
yield return new WaitForSecondsRealtime(delay);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
float elapsed = 0f;
|
|
692
|
+
|
|
693
|
+
while (elapsed < MediaFadeInDuration)
|
|
694
|
+
{
|
|
695
|
+
if (display == null) yield break;
|
|
696
|
+
|
|
697
|
+
elapsed += Time.unscaledDeltaTime;
|
|
698
|
+
float t = Mathf.Clamp01(elapsed / MediaFadeInDuration);
|
|
699
|
+
SetMediaDisplayAlpha(display, Mathf.SmoothStep(0f, 1f, t));
|
|
700
|
+
yield return null;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (display != null)
|
|
704
|
+
{
|
|
705
|
+
SetMediaDisplayAlpha(display, 1f);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
mediaDisplayFadeCo = null;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
private void DisplayCurrentCard(bool isInitialDisplay)
|
|
714
|
+
{
|
|
715
|
+
if (currentCardIndex < 0 || currentCardIndex >= tutorialCards.Count) return;
|
|
716
|
+
|
|
717
|
+
StopVideoPlayback();
|
|
718
|
+
ResetCardMediaState();
|
|
719
|
+
TutorialCardData card = tutorialCards[currentCardIndex];
|
|
720
|
+
|
|
721
|
+
if (descriptionText != null)
|
|
722
|
+
{
|
|
723
|
+
descriptionText.text = card.description ?? string.Empty;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (card.type == TutorialType.video)
|
|
727
|
+
{
|
|
728
|
+
DisplayVideoCard(card, isInitialDisplay);
|
|
729
|
+
}
|
|
730
|
+
else
|
|
731
|
+
{
|
|
732
|
+
DisplayImageCard(card, isInitialDisplay);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
HighlightIndicator(currentCardIndex);
|
|
736
|
+
UpdateNavigationButtons(tutorialCards.Count, currentCardIndex);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private void DisplayVideoCard(TutorialCardData card, bool isInitialDisplay)
|
|
740
|
+
{
|
|
741
|
+
if (videoPlayer == null)
|
|
742
|
+
{
|
|
743
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Keep the media surface hidden while VideoPlayer prepares the next source.
|
|
748
|
+
currentDisplay = tutorialDisplayP;
|
|
749
|
+
|
|
750
|
+
videoPlayer.enabled = true;
|
|
751
|
+
videoPlayer.isLooping = true;
|
|
752
|
+
videoPlayer.prepareCompleted -= OnVideoPrepared;
|
|
753
|
+
videoPlayer.prepareCompleted -= OnCardVideoPrepared;
|
|
754
|
+
videoPlayer.loopPointReached -= OnLoopPointReached;
|
|
755
|
+
videoPlayer.loopPointReached += OnLoopPointReached;
|
|
756
|
+
|
|
757
|
+
if (card.videoClip != null)
|
|
758
|
+
{
|
|
759
|
+
videoPlayer.source = VideoSource.VideoClip;
|
|
760
|
+
videoPlayer.clip = card.videoClip;
|
|
761
|
+
}
|
|
762
|
+
else if (!string.IsNullOrEmpty(card.videoUrl))
|
|
763
|
+
{
|
|
764
|
+
videoPlayer.source = VideoSource.Url;
|
|
765
|
+
videoPlayer.url = card.videoUrl;
|
|
766
|
+
}
|
|
767
|
+
else
|
|
768
|
+
{
|
|
769
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
videoPlayer.prepareCompleted += OnCardVideoPrepared;
|
|
774
|
+
videoPlayer.Prepare();
|
|
775
|
+
|
|
776
|
+
timeSlider.gameObject.SetActive(true);
|
|
777
|
+
timeSlider.interactable = VideoSeekingEnabled;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
private void OnCardVideoPrepared(VideoPlayer vp)
|
|
781
|
+
{
|
|
782
|
+
vp.prepareCompleted -= OnCardVideoPrepared;
|
|
783
|
+
PreventIOSFullscreen();
|
|
784
|
+
|
|
785
|
+
if (currentDisplay == null)
|
|
786
|
+
{
|
|
787
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
bool isLandscape = vp.width > vp.height;
|
|
792
|
+
RawImage targetDisplay = isLandscape ? tutorialDisplayL : tutorialDisplayP;
|
|
793
|
+
RenderTexture targetTexture = isLandscape ? tutorialVideoTextureL : tutorialVideoTextureP;
|
|
794
|
+
if (targetTexture == null) targetTexture = isLandscape ? tutorialVideoTextureP : tutorialVideoTextureL;
|
|
795
|
+
|
|
796
|
+
// Switch to the correct display
|
|
797
|
+
tutorialDisplayP.gameObject.SetActive(targetDisplay == tutorialDisplayP);
|
|
798
|
+
tutorialDisplayL.gameObject.SetActive(targetDisplay == tutorialDisplayL);
|
|
799
|
+
currentDisplay = targetDisplay;
|
|
800
|
+
SetMediaDisplayAlpha(currentDisplay, 0f);
|
|
801
|
+
|
|
802
|
+
if (targetTexture != null)
|
|
803
|
+
{
|
|
804
|
+
currentDisplay.texture = targetTexture;
|
|
805
|
+
videoPlayer.targetTexture = targetTexture;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
double len = videoPlayer.length;
|
|
809
|
+
if (len <= 0 && videoPlayer.clip != null) len = videoPlayer.clip.length;
|
|
810
|
+
cachedVideoLength = (float)Mathf.Max(0f, (float)len);
|
|
811
|
+
|
|
812
|
+
timeSlider.minValue = 0f;
|
|
813
|
+
timeSlider.maxValue = cachedVideoLength;
|
|
814
|
+
timeSlider.value = 0f;
|
|
815
|
+
|
|
816
|
+
if (updateVideoTimeCo != null) StopCoroutine(updateVideoTimeCo);
|
|
817
|
+
updateVideoTimeCo = StartCoroutine(UpdateVideoTime());
|
|
818
|
+
|
|
819
|
+
InvokeTutorialLoadingCompleteOnce();
|
|
820
|
+
videoPlayer.time = 0;
|
|
821
|
+
videoPlayer.Play();
|
|
822
|
+
FadeInMediaDisplay(currentDisplay, VideoRevealDelay);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
private void DisplayImageCard(TutorialCardData card, bool isInitialDisplay)
|
|
826
|
+
{
|
|
827
|
+
videoPlayer.enabled = false;
|
|
828
|
+
timeSlider.gameObject.SetActive(false);
|
|
829
|
+
|
|
830
|
+
if (tutorialDisplayP != null)
|
|
831
|
+
tutorialDisplayP.gameObject.SetActive(false);
|
|
832
|
+
if (tutorialDisplayL != null)
|
|
833
|
+
tutorialDisplayL.gameObject.SetActive(false);
|
|
834
|
+
|
|
835
|
+
if (tutorialImageDisplay == null || card.image == null)
|
|
836
|
+
{
|
|
837
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
tutorialImageDisplay.gameObject.SetActive(true);
|
|
842
|
+
tutorialImageDisplay.texture = card.image.texture;
|
|
843
|
+
FadeInMediaDisplay(tutorialImageDisplay);
|
|
844
|
+
|
|
845
|
+
if (isInitialDisplay)
|
|
846
|
+
{
|
|
847
|
+
InvokeTutorialLoadingCompleteOnce();
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
private void InvokeTutorialLoadingCompleteOnce()
|
|
852
|
+
{
|
|
853
|
+
if (hasInvokedTutorialLoadComplete) return;
|
|
854
|
+
|
|
855
|
+
hasPreparedTutorialContent = true;
|
|
856
|
+
hasInvokedTutorialLoadComplete = true;
|
|
857
|
+
_eventManager.InvokeTutorialLoadingCompleteEvent();
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
private void StopVideoPlayback()
|
|
861
|
+
{
|
|
862
|
+
if (updateVideoTimeCo != null)
|
|
863
|
+
{
|
|
864
|
+
StopCoroutine(updateVideoTimeCo);
|
|
865
|
+
updateVideoTimeCo = null;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
if (videoPlayer == null) return;
|
|
869
|
+
|
|
870
|
+
videoPlayer.prepareCompleted -= OnCardVideoPrepared;
|
|
871
|
+
videoPlayer.loopPointReached -= OnLoopPointReached;
|
|
872
|
+
if (videoPlayer.isPrepared)
|
|
873
|
+
{
|
|
874
|
+
videoPlayer.time = 0;
|
|
875
|
+
}
|
|
876
|
+
videoPlayer.Stop();
|
|
877
|
+
videoPlayer.targetTexture = null;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
|
|
211
882
|
private void ShowVideoTutorial(TutorialData tutorialData)
|
|
212
883
|
{
|
|
884
|
+
if (videoPlayer == null)
|
|
885
|
+
{
|
|
886
|
+
_eventManager.InvokeTutorialFetchFailedEvent();
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
videoPlayer.enabled = true;
|
|
891
|
+
|
|
213
892
|
bool videoFound = false;
|
|
214
893
|
|
|
215
894
|
if (tutorialData.videoClip != null)
|
|
@@ -234,6 +913,7 @@ namespace Hyper.Internal
|
|
|
234
913
|
|
|
235
914
|
else
|
|
236
915
|
{
|
|
916
|
+
videoPlayer.prepareCompleted -= OnCardVideoPrepared;
|
|
237
917
|
videoPlayer.prepareCompleted += OnVideoPrepared;
|
|
238
918
|
videoPlayer.loopPointReached += OnLoopPointReached;
|
|
239
919
|
}
|
|
@@ -258,15 +938,6 @@ namespace Hyper.Internal
|
|
|
258
938
|
currentDisplay.texture = targetTexture;
|
|
259
939
|
videoPlayer.targetTexture = targetTexture;
|
|
260
940
|
|
|
261
|
-
AspectRatioFitter fitter = (currentDisplay == tutorialDisplayL) ?
|
|
262
|
-
aspectRatioFitterL : aspectRatioFitterP;
|
|
263
|
-
|
|
264
|
-
if (fitter != null)
|
|
265
|
-
{
|
|
266
|
-
fitter.aspectRatio = Mathf.Max(0.01f, videoAspectRatio);
|
|
267
|
-
fitter.aspectMode = AspectRatioFitter.AspectMode.FitInParent;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
941
|
timeSlider.gameObject.SetActive(true);
|
|
271
942
|
backButton.gameObject.SetActive(false);
|
|
272
943
|
nextButton.gameObject.SetActive(false);
|
|
@@ -278,6 +949,7 @@ namespace Hyper.Internal
|
|
|
278
949
|
timeSlider.minValue = 0f;
|
|
279
950
|
timeSlider.maxValue = cachedVideoLength;
|
|
280
951
|
timeSlider.value = 0f;
|
|
952
|
+
timeSlider.interactable = VideoSeekingEnabled;
|
|
281
953
|
|
|
282
954
|
if (updateVideoTimeCo != null) StopCoroutine(updateVideoTimeCo);
|
|
283
955
|
updateVideoTimeCo = StartCoroutine(UpdateVideoTime());
|
|
@@ -324,6 +996,7 @@ namespace Hyper.Internal
|
|
|
324
996
|
{
|
|
325
997
|
tutorialImages = tutorialData.images;
|
|
326
998
|
SetupImageTutorial();
|
|
999
|
+
_eventManager.InvokeTutorialLoadingCompleteEvent();
|
|
327
1000
|
}
|
|
328
1001
|
else if (tutorialData.imageUrls != null && tutorialData.imageUrls.Length > 0)
|
|
329
1002
|
{
|
|
@@ -377,15 +1050,6 @@ namespace Hyper.Internal
|
|
|
377
1050
|
float aspectRatio = (float)texture.width / texture.height;
|
|
378
1051
|
|
|
379
1052
|
currentDisplay = SelectOptimalDisplay(aspectRatio);
|
|
380
|
-
|
|
381
|
-
AspectRatioFitter fitter = (currentDisplay == tutorialDisplayL) ?
|
|
382
|
-
aspectRatioFitterL : aspectRatioFitterP;
|
|
383
|
-
|
|
384
|
-
if (fitter != null)
|
|
385
|
-
{
|
|
386
|
-
fitter.aspectRatio = Mathf.Max(0.01f, aspectRatio);
|
|
387
|
-
fitter.aspectMode = AspectRatioFitter.AspectMode.FitInParent;
|
|
388
|
-
}
|
|
389
1053
|
}
|
|
390
1054
|
|
|
391
1055
|
private IEnumerator LoadImagesFromUrlsCoroutine(string[] urls, Action<Sprite[]> onComplete)
|
|
@@ -407,7 +1071,7 @@ namespace Hyper.Internal
|
|
|
407
1071
|
if (webRequest.result == UnityWebRequest.Result.Success)
|
|
408
1072
|
{
|
|
409
1073
|
Texture2D texture = DownloadHandlerTexture.GetContent(webRequest);
|
|
410
|
-
texture.filterMode = FilterMode.
|
|
1074
|
+
texture.filterMode = FilterMode.Bilinear;
|
|
411
1075
|
texture.Compress(false);
|
|
412
1076
|
|
|
413
1077
|
Sprite sprite = Sprite.Create(
|
|
@@ -447,6 +1111,21 @@ namespace Hyper.Internal
|
|
|
447
1111
|
|
|
448
1112
|
private void OnDisable()
|
|
449
1113
|
{
|
|
1114
|
+
isDirectContentOpen = false;
|
|
1115
|
+
contentRevealScheduled = false;
|
|
1116
|
+
isClosing = false;
|
|
1117
|
+
CancelHiddenPreloadClose();
|
|
1118
|
+
StopMediaDisplayFade();
|
|
1119
|
+
HideTutorialContent();
|
|
1120
|
+
|
|
1121
|
+
#if UNITY_EDITOR
|
|
1122
|
+
if (editorTutorialLoadCo != null)
|
|
1123
|
+
{
|
|
1124
|
+
StopCoroutine(editorTutorialLoadCo);
|
|
1125
|
+
editorTutorialLoadCo = null;
|
|
1126
|
+
}
|
|
1127
|
+
#endif
|
|
1128
|
+
|
|
450
1129
|
if (updateVideoTimeCo != null)
|
|
451
1130
|
{
|
|
452
1131
|
StopCoroutine(updateVideoTimeCo);
|
|
@@ -454,6 +1133,14 @@ namespace Hyper.Internal
|
|
|
454
1133
|
}
|
|
455
1134
|
}
|
|
456
1135
|
|
|
1136
|
+
private void OnDestroy()
|
|
1137
|
+
{
|
|
1138
|
+
if (_eventManager == null) return;
|
|
1139
|
+
|
|
1140
|
+
_eventManager.OnTutorialResourcesReferenced -= ShowTutorialBasedOnType;
|
|
1141
|
+
_eventManager.OnTutorialLoadingComplete -= HandleTutorialLoadingComplete;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
457
1144
|
private void DisplayImage(int index)
|
|
458
1145
|
{
|
|
459
1146
|
if (tutorialImages == null || index < 0 || index >= tutorialImages.Length)
|
|
@@ -471,6 +1158,12 @@ namespace Hyper.Internal
|
|
|
471
1158
|
|
|
472
1159
|
public void NextImage()
|
|
473
1160
|
{
|
|
1161
|
+
if (tutorialCards.Count > 0)
|
|
1162
|
+
{
|
|
1163
|
+
NextContent();
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
474
1167
|
if (currentImageIndex < tutorialImages.Length - 1)
|
|
475
1168
|
{
|
|
476
1169
|
currentImageIndex++;
|
|
@@ -480,6 +1173,12 @@ namespace Hyper.Internal
|
|
|
480
1173
|
|
|
481
1174
|
public void PreviousImage()
|
|
482
1175
|
{
|
|
1176
|
+
if (tutorialCards.Count > 0)
|
|
1177
|
+
{
|
|
1178
|
+
PreviousContent();
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
|
|
483
1182
|
if (currentImageIndex > 0)
|
|
484
1183
|
{
|
|
485
1184
|
currentImageIndex--;
|
|
@@ -487,19 +1186,77 @@ namespace Hyper.Internal
|
|
|
487
1186
|
}
|
|
488
1187
|
}
|
|
489
1188
|
|
|
1189
|
+
public void NextContent()
|
|
1190
|
+
{
|
|
1191
|
+
if (currentCardIndex >= tutorialCards.Count - 1) return;
|
|
1192
|
+
|
|
1193
|
+
currentCardIndex++;
|
|
1194
|
+
DisplayCurrentCard(false);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
public void PreviousContent()
|
|
1198
|
+
{
|
|
1199
|
+
if (currentCardIndex <= 0) return;
|
|
1200
|
+
|
|
1201
|
+
currentCardIndex--;
|
|
1202
|
+
DisplayCurrentCard(false);
|
|
1203
|
+
}
|
|
1204
|
+
|
|
490
1205
|
public void Close()
|
|
491
1206
|
{
|
|
1207
|
+
if (isClosing) return;
|
|
1208
|
+
|
|
1209
|
+
isClosing = true;
|
|
1210
|
+
CancelHiddenPreloadClose();
|
|
1211
|
+
tutorialCG.interactable = false;
|
|
1212
|
+
tutorialCG.blocksRaycasts = false;
|
|
1213
|
+
|
|
1214
|
+
if (tutorialContentGroup == null || tutorialContentGroup.alpha <= 0.001f)
|
|
1215
|
+
{
|
|
1216
|
+
FinishClose();
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
tutorialContentGroup.DOKill();
|
|
1221
|
+
tutorialContentGroup.interactable = false;
|
|
1222
|
+
tutorialContentGroup.blocksRaycasts = false;
|
|
1223
|
+
tutorialContentGroup.DOFade(0f, ContentFadeOutDuration)
|
|
1224
|
+
.SetEase(HyperTween.Ease.InQuad)
|
|
1225
|
+
.SetUpdate(true)
|
|
1226
|
+
.OnComplete(FinishClose);
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
private void FinishClose()
|
|
1230
|
+
{
|
|
1231
|
+
if (tutorialContentGroup != null)
|
|
1232
|
+
{
|
|
1233
|
+
tutorialContentGroup.alpha = 0f;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
492
1236
|
if (videoPlayer != null) videoPlayer.enabled = false;
|
|
493
1237
|
slideUp.SlideDownAnimation();
|
|
494
1238
|
}
|
|
495
1239
|
|
|
496
1240
|
private void UpdateNavigationButtons()
|
|
497
1241
|
{
|
|
498
|
-
|
|
499
|
-
|
|
1242
|
+
UpdateNavigationButtons(tutorialImages != null ? tutorialImages.Length : 0, currentImageIndex);
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
private void UpdateNavigationButtons(int totalCount, int currentIndex)
|
|
1246
|
+
{
|
|
1247
|
+
bool hasMultipleCards = totalCount > 1;
|
|
1248
|
+
nextButton.gameObject.SetActive(hasMultipleCards);
|
|
1249
|
+
backButton.gameObject.SetActive(hasMultipleCards);
|
|
1250
|
+
nextButton.interactable = currentIndex < totalCount - 1;
|
|
1251
|
+
backButton.interactable = currentIndex > 0;
|
|
500
1252
|
}
|
|
501
1253
|
|
|
502
1254
|
private void UpdateIndicators()
|
|
1255
|
+
{
|
|
1256
|
+
UpdateIndicators(tutorialImages != null ? tutorialImages.Length : 0);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
private void UpdateIndicators(int totalCount)
|
|
503
1260
|
{
|
|
504
1261
|
|
|
505
1262
|
foreach (Transform child in indicatorParent)
|
|
@@ -507,10 +1264,18 @@ namespace Hyper.Internal
|
|
|
507
1264
|
Destroy(child.gameObject);
|
|
508
1265
|
}
|
|
509
1266
|
|
|
510
|
-
for (int i = 0; i <
|
|
1267
|
+
for (int i = 0; i < totalCount; i++)
|
|
511
1268
|
{
|
|
512
1269
|
Instantiate(indicatorPrefab, indicatorParent);
|
|
513
1270
|
}
|
|
1271
|
+
|
|
1272
|
+
indicatorParent.gameObject.SetActive(totalCount > 1);
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
private IEnumerator ApplyInitialIndicatorHighlightNextFrame()
|
|
1276
|
+
{
|
|
1277
|
+
yield return null;
|
|
1278
|
+
HighlightIndicator(currentCardIndex);
|
|
514
1279
|
}
|
|
515
1280
|
|
|
516
1281
|
private void HighlightIndicator(int index)
|
|
@@ -559,6 +1324,8 @@ namespace Hyper.Internal
|
|
|
559
1324
|
|
|
560
1325
|
private void OnTimeSliderChanged(float value)
|
|
561
1326
|
{
|
|
1327
|
+
if (!VideoSeekingEnabled) return;
|
|
1328
|
+
|
|
562
1329
|
// var tutorialData = HyperRuntime.GameManager.IsPortrait()
|
|
563
1330
|
// ? HyperRuntime.DataManager.tutorialData
|
|
564
1331
|
// : HyperRuntime.DataManager.landscapeTutorial;
|