app.hypergames.hypersdk 1.8.20 → 1.8.22
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 +14 -0
- package/Runtime/Internal/Bootstrap/HyperLoader.cs +4 -1
- package/Runtime/Internal/Managers/BackendManager.cs +126 -19
- package/Runtime/Internal/Managers/GameManager.cs +107 -9
- package/Runtime/Internal/ScriptableObjects/HyperSDKSettings.cs +1 -1
- package/Runtime/Internal/UI/Modals/TutorialAnnouncementModal.cs +57 -0
- package/Runtime/Internal/UI/Modals/TutorialAnnouncementModal.cs.meta +2 -0
- package/Runtime/Internal/UI/Modals/TutorialLoaderModal.cs +4 -3
- package/Runtime/Internal/UI/Modals/TutorialModal.cs +99 -6
- package/Runtime/Resources/Materials/RoundedRawImage.mat +3 -3
- package/Runtime/Resources/Sprites/Frame 2147224037.png +0 -0
- package/Runtime/Resources/Sprites/{TutorialModal/P Container.png.meta → Frame 2147224037.png.meta } +12 -25
- package/Runtime/Resources/UIPrefabs/ConnectionStatusBar.prefab +10 -10
- package/Runtime/Resources/UIPrefabs/GamePausedModal_Battle.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/GamePausedModal_Battle_Landscape.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/GamePausedModal_Practice.prefab +12 -0
- package/Runtime/Resources/UIPrefabs/LoadingFailedModal.prefab +7 -7
- package/Runtime/Resources/UIPrefabs/ScoreSubmissionFailedModal_Portrait.prefab +1 -1
- package/Runtime/Resources/UIPrefabs/TutorialAnnouncementModal.prefab +699 -0
- package/Runtime/Resources/UIPrefabs/{Content.prefab.meta → TutorialAnnouncementModal.prefab.meta} +1 -1
- package/Runtime/Resources/UIPrefabs/TutorialLandscapeModal.prefab +1578 -1218
- package/Runtime/Resources/UIPrefabs/TutorialLandscapeModal.prefab.meta +1 -1
- package/Runtime/Resources/UIPrefabs/TutorialPortraitModal.prefab +2210 -777
- package/package.json +1 -1
- package/Runtime/Resources/Sprites/TutorialModal/L Container.png +0 -0
- package/Runtime/Resources/Sprites/TutorialModal/L Container.png.meta +0 -169
- package/Runtime/Resources/Sprites/TutorialModal/P Container.png +0 -0
- package/Runtime/Resources/Sprites/TutorialModal/Time_Seeker_Bar_Empty.png +0 -0
- package/Runtime/Resources/Sprites/TutorialModal/Time_Seeker_Bar_Empty.png.meta +0 -156
- package/Runtime/Resources/Sprites/TutorialModal/Time_Seeker_Bar_Filled.png +0 -0
- package/Runtime/Resources/Sprites/TutorialModal/Time_Seeker_Bar_Filled.png.meta +0 -156
- package/Runtime/Resources/Sprites/TutorialModal/Tutorial_Background_Landscape.png +0 -0
- package/Runtime/Resources/Sprites/TutorialModal/Tutorial_Background_Landscape.png.meta +0 -169
- package/Runtime/Resources/Sprites/TutorialModal/Tutorial_Background_Portrait.png +0 -0
- package/Runtime/Resources/Sprites/TutorialModal/Tutorial_Background_Portrait.png.meta +0 -169
- package/Runtime/Resources/UIPrefabs/Content.prefab +0 -1672
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.8.22](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.21...v1.8.22) (2026-07-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add tutorial announcement prompt ([0b5e009](https://github.com/mvmhyper/hyper-sdk/commit/0b5e0099346d144b56d35a39d613e24a776ef11b))
|
|
7
|
+
|
|
8
|
+
## [1.8.21](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.20...v1.8.21) (2026-07-08)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* smooth tutorial loader and media switching ([b4cfbef](https://github.com/mvmhyper/hyper-sdk/commit/b4cfbef45d5544e7c5dfa37f702acb9f7be67548))
|
|
14
|
+
|
|
1
15
|
## [1.8.20](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.19...v1.8.20) (2026-07-07)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -263,6 +263,9 @@ namespace Hyper.Internal
|
|
|
263
263
|
_bootstrapProgress = _progressAfterInit;
|
|
264
264
|
_loadingBarSlider.value = _bootstrapProgress;
|
|
265
265
|
|
|
266
|
+
// Start tutorial discovery early, but never wait for it before loading gameplay.
|
|
267
|
+
HyperRuntime.BackendManager.GetTutorial(null);
|
|
268
|
+
|
|
266
269
|
// Step 2: Handle Backend/PVP or Practice mode initialization
|
|
267
270
|
if (_gameManager.Config.IsBackendGame && _gameManager.Config.isPVP)
|
|
268
271
|
{
|
|
@@ -475,4 +478,4 @@ namespace Hyper.Internal
|
|
|
475
478
|
|
|
476
479
|
#endregion
|
|
477
480
|
}
|
|
478
|
-
}
|
|
481
|
+
}
|
|
@@ -49,7 +49,8 @@ namespace Hyper.Internal.Managers
|
|
|
49
49
|
public bool isSocketConnected { get; private set; }
|
|
50
50
|
public bool tutorialFetchSuccess { get; private set; }
|
|
51
51
|
public bool tutorialExistsButFailed { get; private set; }
|
|
52
|
-
public bool tutorialDoesNotExist =>
|
|
52
|
+
public bool tutorialDoesNotExist => TutorialAvailability == TutorialAvailabilityState.Unavailable;
|
|
53
|
+
public TutorialAvailabilityState TutorialAvailability { get; private set; } = TutorialAvailabilityState.Unknown;
|
|
53
54
|
|
|
54
55
|
/// <summary>
|
|
55
56
|
/// Type of error that occurred during score submission.
|
|
@@ -127,7 +128,7 @@ namespace Hyper.Internal.Managers
|
|
|
127
128
|
private void FetchAndCacheTutorials(string serverBaseUrl, string gameSlug, string gameObject)
|
|
128
129
|
{
|
|
129
130
|
_isFetchingTutorial = false;
|
|
130
|
-
|
|
131
|
+
OnTutorialCacheComplete("success");
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
private void InitWebSocket(string serverUrl) { }
|
|
@@ -1587,27 +1588,41 @@ namespace Hyper.Internal.Managers
|
|
|
1587
1588
|
NetworkError
|
|
1588
1589
|
}
|
|
1589
1590
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1591
|
+
public enum TutorialAvailabilityState
|
|
1592
|
+
{
|
|
1593
|
+
Unknown,
|
|
1594
|
+
InProgress,
|
|
1595
|
+
Available,
|
|
1596
|
+
Unavailable,
|
|
1597
|
+
ExistsButFailed
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
private readonly List<Action> _fetchCompleteCallbacks = new List<Action>();
|
|
1601
|
+
private readonly List<Action> _tutorialSuccessCallbacks = new List<Action>();
|
|
1602
|
+
private readonly List<Action<TutorialFetchStatus>> _tutorialFailureCallbacks = new List<Action<TutorialFetchStatus>>();
|
|
1593
1603
|
private bool _isFetchingTutorial;
|
|
1594
1604
|
|
|
1595
1605
|
public void GetTutorial(Action fetchComplete, Action onSuccess = null, Action<TutorialFetchStatus> onFailure = null)
|
|
1596
1606
|
{
|
|
1597
1607
|
if (_gameManager == null) _gameManager = HyperRuntime.GameManager;
|
|
1598
1608
|
|
|
1609
|
+
if (IsTutorialFetchComplete())
|
|
1610
|
+
{
|
|
1611
|
+
InvokeTutorialCallbacksForCurrentState(fetchComplete, onSuccess, onFailure);
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
AddTutorialCallbacks(fetchComplete, onSuccess, onFailure);
|
|
1616
|
+
|
|
1599
1617
|
if (_isFetchingTutorial)
|
|
1600
1618
|
{
|
|
1601
|
-
HyperDebug.LogWarning("Tutorial fetch already in progress");
|
|
1602
1619
|
return;
|
|
1603
1620
|
}
|
|
1604
1621
|
|
|
1605
|
-
_onFetchComplete = fetchComplete;
|
|
1606
|
-
_onTutorialSuccess = onSuccess;
|
|
1607
|
-
_onTutorialFailure = onFailure;
|
|
1608
1622
|
_currentRetryCount = 0;
|
|
1609
1623
|
tutorialExistsButFailed = false;
|
|
1610
1624
|
tutorialFetchSuccess = false;
|
|
1625
|
+
TutorialAvailability = TutorialAvailabilityState.InProgress;
|
|
1611
1626
|
TryFetchTutorial();
|
|
1612
1627
|
}
|
|
1613
1628
|
|
|
@@ -1618,30 +1633,32 @@ namespace Hyper.Internal.Managers
|
|
|
1618
1633
|
|
|
1619
1634
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
1620
1635
|
FetchAndCacheTutorials(GetServerUrl(), gameSlug, gameObject.name);
|
|
1621
|
-
#
|
|
1636
|
+
#elif UNITY_EDITOR
|
|
1622
1637
|
StartCoroutine(SimulateTutorialFetch());
|
|
1638
|
+
#else
|
|
1639
|
+
OnTutorialCacheComplete("fetch_failed");
|
|
1623
1640
|
#endif
|
|
1624
1641
|
}
|
|
1625
1642
|
|
|
1626
1643
|
public void OnTutorialCacheComplete(string result)
|
|
1627
1644
|
{
|
|
1628
|
-
_onFetchComplete?.Invoke();
|
|
1629
|
-
|
|
1630
1645
|
switch (result)
|
|
1631
1646
|
{
|
|
1632
1647
|
case "success":
|
|
1633
|
-
_eventManager?.InvokeTutorialFetchCompleteEvent();
|
|
1634
1648
|
_isFetchingTutorial = false;
|
|
1635
1649
|
tutorialExistsButFailed = false;
|
|
1636
1650
|
tutorialFetchSuccess = true;
|
|
1637
|
-
|
|
1651
|
+
TutorialAvailability = TutorialAvailabilityState.Available;
|
|
1652
|
+
_eventManager?.InvokeTutorialFetchCompleteEvent();
|
|
1653
|
+
InvokeAndClearTutorialCallbacks(TutorialFetchStatus.Success);
|
|
1638
1654
|
break;
|
|
1639
1655
|
|
|
1640
1656
|
case "no_tutorial":
|
|
1641
1657
|
_isFetchingTutorial = false;
|
|
1642
1658
|
tutorialExistsButFailed = false;
|
|
1643
1659
|
tutorialFetchSuccess = false;
|
|
1644
|
-
|
|
1660
|
+
TutorialAvailability = TutorialAvailabilityState.Unavailable;
|
|
1661
|
+
InvokeAndClearTutorialCallbacks(TutorialFetchStatus.NoTutorialExists);
|
|
1645
1662
|
_eventManager?.InvokeTutorialNotExistsEvent();
|
|
1646
1663
|
break;
|
|
1647
1664
|
|
|
@@ -1659,16 +1676,106 @@ namespace Hyper.Internal.Managers
|
|
|
1659
1676
|
|
|
1660
1677
|
private void HandleRetryOrFail(TutorialFetchStatus status)
|
|
1661
1678
|
{
|
|
1662
|
-
_onTutorialFailure?.Invoke(status);
|
|
1663
|
-
_eventManager?.InvokeTutorialFetchFailedEvent();
|
|
1664
1679
|
_isFetchingTutorial = false;
|
|
1680
|
+
|
|
1681
|
+
if (status == TutorialFetchStatus.DownloadFailed)
|
|
1682
|
+
{
|
|
1683
|
+
tutorialExistsButFailed = true;
|
|
1684
|
+
tutorialFetchSuccess = false;
|
|
1685
|
+
TutorialAvailability = TutorialAvailabilityState.ExistsButFailed;
|
|
1686
|
+
}
|
|
1687
|
+
else
|
|
1688
|
+
{
|
|
1689
|
+
tutorialExistsButFailed = false;
|
|
1690
|
+
tutorialFetchSuccess = false;
|
|
1691
|
+
TutorialAvailability = TutorialAvailabilityState.Unknown;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
InvokeAndClearTutorialCallbacks(status);
|
|
1695
|
+
_eventManager?.InvokeTutorialFetchFailedEvent();
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
private bool IsTutorialFetchComplete()
|
|
1699
|
+
{
|
|
1700
|
+
return TutorialAvailability == TutorialAvailabilityState.Available ||
|
|
1701
|
+
TutorialAvailability == TutorialAvailabilityState.Unavailable;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
private void AddTutorialCallbacks(Action fetchComplete, Action onSuccess, Action<TutorialFetchStatus> onFailure)
|
|
1705
|
+
{
|
|
1706
|
+
if (fetchComplete != null) _fetchCompleteCallbacks.Add(fetchComplete);
|
|
1707
|
+
if (onSuccess != null) _tutorialSuccessCallbacks.Add(onSuccess);
|
|
1708
|
+
if (onFailure != null) _tutorialFailureCallbacks.Add(onFailure);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
private void InvokeTutorialCallbacksForCurrentState(Action fetchComplete, Action onSuccess, Action<TutorialFetchStatus> onFailure)
|
|
1712
|
+
{
|
|
1713
|
+
switch (TutorialAvailability)
|
|
1714
|
+
{
|
|
1715
|
+
case TutorialAvailabilityState.Available:
|
|
1716
|
+
onSuccess?.Invoke();
|
|
1717
|
+
break;
|
|
1718
|
+
case TutorialAvailabilityState.Unavailable:
|
|
1719
|
+
onFailure?.Invoke(TutorialFetchStatus.NoTutorialExists);
|
|
1720
|
+
break;
|
|
1721
|
+
case TutorialAvailabilityState.ExistsButFailed:
|
|
1722
|
+
onFailure?.Invoke(TutorialFetchStatus.DownloadFailed);
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
fetchComplete?.Invoke();
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
private void InvokeAndClearTutorialCallbacks(TutorialFetchStatus status)
|
|
1730
|
+
{
|
|
1731
|
+
if (status == TutorialFetchStatus.Success)
|
|
1732
|
+
{
|
|
1733
|
+
for (int i = 0; i < _tutorialSuccessCallbacks.Count; i++)
|
|
1734
|
+
{
|
|
1735
|
+
_tutorialSuccessCallbacks[i]?.Invoke();
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
else
|
|
1739
|
+
{
|
|
1740
|
+
for (int i = 0; i < _tutorialFailureCallbacks.Count; i++)
|
|
1741
|
+
{
|
|
1742
|
+
_tutorialFailureCallbacks[i]?.Invoke(status);
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
for (int i = 0; i < _fetchCompleteCallbacks.Count; i++)
|
|
1747
|
+
{
|
|
1748
|
+
_fetchCompleteCallbacks[i]?.Invoke();
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
_fetchCompleteCallbacks.Clear();
|
|
1752
|
+
_tutorialSuccessCallbacks.Clear();
|
|
1753
|
+
_tutorialFailureCallbacks.Clear();
|
|
1665
1754
|
}
|
|
1666
1755
|
|
|
1667
1756
|
#if UNITY_EDITOR
|
|
1668
1757
|
private IEnumerator SimulateTutorialFetch()
|
|
1669
1758
|
{
|
|
1670
|
-
|
|
1671
|
-
|
|
1759
|
+
var value = UnityEngine.Random.Range(0, 4);
|
|
1760
|
+
Debug.Log($"[Tutorial] Simulating tutorial fetch result: {value}");
|
|
1761
|
+
|
|
1762
|
+
switch (value)
|
|
1763
|
+
{
|
|
1764
|
+
case 0:
|
|
1765
|
+
OnTutorialCacheComplete("success");
|
|
1766
|
+
break;
|
|
1767
|
+
case 1:
|
|
1768
|
+
OnTutorialCacheComplete("download_failed");
|
|
1769
|
+
break;
|
|
1770
|
+
case 2:
|
|
1771
|
+
OnTutorialCacheComplete("fetch_failed");
|
|
1772
|
+
break;
|
|
1773
|
+
default:
|
|
1774
|
+
OnTutorialCacheComplete("no_tutorial");
|
|
1775
|
+
break;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
yield break;
|
|
1672
1779
|
}
|
|
1673
1780
|
#endif
|
|
1674
1781
|
|
|
@@ -805,20 +805,13 @@ namespace Hyper.Internal.Managers
|
|
|
805
805
|
|
|
806
806
|
ShowControlBarElements();
|
|
807
807
|
AddTutorialModal();
|
|
808
|
-
|
|
808
|
+
ShowTutorialAnnouncementOrStartingInstruction();
|
|
809
809
|
}
|
|
810
810
|
|
|
811
811
|
private IEnumerator BootstrapAndStart()
|
|
812
812
|
{
|
|
813
813
|
HyperRuntime.EventManager.OnGameStart += HandleGameStarted;
|
|
814
|
-
|
|
815
|
-
bool tutorialDone = false;
|
|
816
|
-
HyperRuntime.BackendManager.GetTutorial(() => tutorialDone = true);
|
|
817
|
-
|
|
818
|
-
while (!tutorialDone)
|
|
819
|
-
{
|
|
820
|
-
yield return null;
|
|
821
|
-
}
|
|
814
|
+
yield break;
|
|
822
815
|
}
|
|
823
816
|
|
|
824
817
|
private void HandleGameStarted()
|
|
@@ -978,6 +971,7 @@ namespace Hyper.Internal.Managers
|
|
|
978
971
|
private GameObject _battleGameOverModal;
|
|
979
972
|
private GameObject _practiceGameOverModal;
|
|
980
973
|
private GameObject _startingInstructionModal;
|
|
974
|
+
private GameObject _tutorialAnnouncementModal;
|
|
981
975
|
private GameObject _tutorialPortraitModal;
|
|
982
976
|
private GameObject _tutorialLandscapeModal;
|
|
983
977
|
private GameObject _controlBarElements;
|
|
@@ -987,6 +981,7 @@ namespace Hyper.Internal.Managers
|
|
|
987
981
|
private GameOverModal _gameOverModalComponent;
|
|
988
982
|
private ControlBar _controlBarModal;
|
|
989
983
|
private TutorialModal _tutorialModalComponent;
|
|
984
|
+
private TutorialAnnouncementModal _tutorialAnnouncementModalComponent;
|
|
990
985
|
private ScoreSubmissionFailedModal _scoreSubmissionFailedModalComponent;
|
|
991
986
|
private StartingInstructionModal _startingInstructionModalComponent;
|
|
992
987
|
|
|
@@ -1495,6 +1490,109 @@ namespace Hyper.Internal.Managers
|
|
|
1495
1490
|
SetBlockerActive(false);
|
|
1496
1491
|
}
|
|
1497
1492
|
|
|
1493
|
+
private void ShowTutorialAnnouncementOrStartingInstruction()
|
|
1494
|
+
{
|
|
1495
|
+
if (ShouldShowTutorialAnnouncement())
|
|
1496
|
+
{
|
|
1497
|
+
ShowTutorialAnnouncementModal();
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
ShowStartingInstructionModal();
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
private bool ShouldShowTutorialAnnouncement()
|
|
1505
|
+
{
|
|
1506
|
+
var availability = HyperRuntime.BackendManager.TutorialAvailability;
|
|
1507
|
+
bool tutorialKnownToExist =
|
|
1508
|
+
availability == BackendManager.TutorialAvailabilityState.Available ||
|
|
1509
|
+
availability == BackendManager.TutorialAvailabilityState.ExistsButFailed;
|
|
1510
|
+
|
|
1511
|
+
return tutorialKnownToExist && !HasSeenTutorialAnnouncement();
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
private bool HasSeenTutorialAnnouncement()
|
|
1515
|
+
{
|
|
1516
|
+
string key = GetTutorialAnnouncementSeenKey();
|
|
1517
|
+
return !string.IsNullOrEmpty(key) && PlayerPrefs.GetInt(key, 0) == 1;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
private void MarkTutorialAnnouncementSeen()
|
|
1521
|
+
{
|
|
1522
|
+
string key = GetTutorialAnnouncementSeenKey();
|
|
1523
|
+
if (string.IsNullOrEmpty(key)) return;
|
|
1524
|
+
|
|
1525
|
+
PlayerPrefs.SetInt(key, 1);
|
|
1526
|
+
PlayerPrefs.Save();
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
private string GetTutorialAnnouncementSeenKey()
|
|
1530
|
+
{
|
|
1531
|
+
string gameIdentifier = Config != null ? Config.GameSlug : null;
|
|
1532
|
+
|
|
1533
|
+
#if UNITY_EDITOR
|
|
1534
|
+
if (string.IsNullOrWhiteSpace(gameIdentifier))
|
|
1535
|
+
{
|
|
1536
|
+
gameIdentifier = Application.productName;
|
|
1537
|
+
}
|
|
1538
|
+
#endif
|
|
1539
|
+
|
|
1540
|
+
if (string.IsNullOrWhiteSpace(gameIdentifier))
|
|
1541
|
+
{
|
|
1542
|
+
gameIdentifier = HyperRuntime.GameContent != null
|
|
1543
|
+
? HyperRuntime.GameContent.GameScene?.SceneName
|
|
1544
|
+
: null;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
return string.IsNullOrWhiteSpace(gameIdentifier)
|
|
1548
|
+
? null
|
|
1549
|
+
: $"{gameIdentifier}_sdkTutorialPromptSeen";
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
private void ShowTutorialAnnouncementModal()
|
|
1553
|
+
{
|
|
1554
|
+
EnsureTutorialAnnouncementModal();
|
|
1555
|
+
|
|
1556
|
+
if (_tutorialAnnouncementModalComponent == null)
|
|
1557
|
+
{
|
|
1558
|
+
ShowStartingInstructionModal();
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
_tutorialAnnouncementModalComponent.Show(() =>
|
|
1563
|
+
{
|
|
1564
|
+
MarkTutorialAnnouncementSeen();
|
|
1565
|
+
SetBlockerActive(false);
|
|
1566
|
+
ShowStartingInstructionModal();
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1569
|
+
SetBlockerActive(true);
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
private void EnsureTutorialAnnouncementModal()
|
|
1573
|
+
{
|
|
1574
|
+
if (_tutorialAnnouncementModal != null)
|
|
1575
|
+
{
|
|
1576
|
+
_tutorialAnnouncementModal.SetActive(true);
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
if (_canvas == null) CreateCanvas();
|
|
1581
|
+
|
|
1582
|
+
GameObject prefab = Resources.Load<GameObject>("UIPrefabs/TutorialAnnouncementModal");
|
|
1583
|
+
if (prefab == null)
|
|
1584
|
+
{
|
|
1585
|
+
HyperDebug.LogWarning("[GameManager] TutorialAnnouncementModal prefab not found.");
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
GameObject instance = Instantiate(prefab);
|
|
1590
|
+
instance.transform.SetParent(_safeArea.transform, false);
|
|
1591
|
+
|
|
1592
|
+
_tutorialAnnouncementModal = instance;
|
|
1593
|
+
_tutorialAnnouncementModalComponent = instance.GetComponent<TutorialAnnouncementModal>();
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1498
1596
|
private void EnsureStartingInstructionModal()
|
|
1499
1597
|
{
|
|
1500
1598
|
if (_startingInstructionModal != null)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
using UnityEngine;
|
|
2
|
+
using UnityEngine.UI;
|
|
3
|
+
|
|
4
|
+
namespace Hyper.Internal
|
|
5
|
+
{
|
|
6
|
+
internal class TutorialAnnouncementModal : MonoBehaviour
|
|
7
|
+
{
|
|
8
|
+
[SerializeField] private Button closeButton;
|
|
9
|
+
|
|
10
|
+
private System.Action _onClosed;
|
|
11
|
+
private bool _hasClosed;
|
|
12
|
+
|
|
13
|
+
private void Awake()
|
|
14
|
+
{
|
|
15
|
+
if (closeButton == null)
|
|
16
|
+
{
|
|
17
|
+
closeButton = GetComponentInChildren<Button>(true);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (closeButton != null)
|
|
21
|
+
{
|
|
22
|
+
closeButton.onClick.AddListener(HandleCloseClicked);
|
|
23
|
+
}
|
|
24
|
+
else
|
|
25
|
+
{
|
|
26
|
+
HyperDebug.LogWarning("[TutorialAnnouncementModal] Close button is not assigned.");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private void OnDestroy()
|
|
31
|
+
{
|
|
32
|
+
if (closeButton != null)
|
|
33
|
+
{
|
|
34
|
+
closeButton.onClick.RemoveListener(HandleCloseClicked);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
internal void Show(System.Action onClosed)
|
|
39
|
+
{
|
|
40
|
+
_onClosed = onClosed;
|
|
41
|
+
_hasClosed = false;
|
|
42
|
+
gameObject.SetActive(true);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private void HandleCloseClicked()
|
|
46
|
+
{
|
|
47
|
+
if (_hasClosed) return;
|
|
48
|
+
|
|
49
|
+
_hasClosed = true;
|
|
50
|
+
gameObject.SetActive(false);
|
|
51
|
+
|
|
52
|
+
System.Action onClosed = _onClosed;
|
|
53
|
+
_onClosed = null;
|
|
54
|
+
onClosed?.Invoke();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -36,10 +36,10 @@ namespace Hyper.Internal
|
|
|
36
36
|
KillTweens();
|
|
37
37
|
tutorialLoaderModalCG.alpha = 0f;
|
|
38
38
|
|
|
39
|
-
// Fade in and enable
|
|
40
|
-
tutorialLoaderModalCG.DOFade(1, 0.3f).SetUpdate(true);
|
|
41
|
-
|
|
42
39
|
HandleLoadingTutorial();
|
|
40
|
+
|
|
41
|
+
// Fade in after loading state has reset its own tweens.
|
|
42
|
+
tutorialLoaderModalCG.DOFade(1, 0.3f).SetUpdate(true);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
void OnDestroy()
|
|
@@ -124,6 +124,7 @@ namespace Hyper.Internal
|
|
|
124
124
|
void HandleTutorialFailed()
|
|
125
125
|
{
|
|
126
126
|
KillTweens();
|
|
127
|
+
tutorialLoaderModalCG.alpha = 1f;
|
|
127
128
|
|
|
128
129
|
loaderTextTween.SetActive(false);
|
|
129
130
|
loaderText.SetActive(true);
|
|
@@ -21,6 +21,8 @@ namespace Hyper.Internal
|
|
|
21
21
|
private const float SlideInDuration = 0.33f;
|
|
22
22
|
private const float ContentFadeInDuration = 0.28f;
|
|
23
23
|
private const float ContentFadeOutDuration = 0.18f;
|
|
24
|
+
private const float MediaFadeInDuration = 0.18f;
|
|
25
|
+
private const float VideoRevealDelay = 0.05f;
|
|
24
26
|
private const float HiddenPreloadPlayDuration = 0.75f;
|
|
25
27
|
|
|
26
28
|
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
@@ -69,6 +71,7 @@ namespace Hyper.Internal
|
|
|
69
71
|
private Coroutine updateVideoTimeCo = null;
|
|
70
72
|
private Coroutine editorTutorialLoadCo = null;
|
|
71
73
|
private Coroutine hiddenPreloadCloseCo = null;
|
|
74
|
+
private Coroutine mediaDisplayFadeCo = null;
|
|
72
75
|
private TutorialData tutorialData;
|
|
73
76
|
private bool isClosing;
|
|
74
77
|
private bool hasInvokedTutorialLoadComplete;
|
|
@@ -621,6 +624,90 @@ namespace Hyper.Internal
|
|
|
621
624
|
}
|
|
622
625
|
}
|
|
623
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
|
+
|
|
624
711
|
|
|
625
712
|
|
|
626
713
|
private void DisplayCurrentCard(bool isInitialDisplay)
|
|
@@ -628,6 +715,7 @@ namespace Hyper.Internal
|
|
|
628
715
|
if (currentCardIndex < 0 || currentCardIndex >= tutorialCards.Count) return;
|
|
629
716
|
|
|
630
717
|
StopVideoPlayback();
|
|
718
|
+
ResetCardMediaState();
|
|
631
719
|
TutorialCardData card = tutorialCards[currentCardIndex];
|
|
632
720
|
|
|
633
721
|
if (descriptionText != null)
|
|
@@ -656,12 +744,7 @@ namespace Hyper.Internal
|
|
|
656
744
|
return;
|
|
657
745
|
}
|
|
658
746
|
|
|
659
|
-
|
|
660
|
-
tutorialImageDisplay.gameObject.SetActive(false);
|
|
661
|
-
|
|
662
|
-
// Start with portrait display as default; will switch to landscape in OnCardVideoPrepared if needed
|
|
663
|
-
tutorialDisplayP.gameObject.SetActive(true);
|
|
664
|
-
tutorialDisplayL.gameObject.SetActive(false);
|
|
747
|
+
// Keep the media surface hidden while VideoPlayer prepares the next source.
|
|
665
748
|
currentDisplay = tutorialDisplayP;
|
|
666
749
|
|
|
667
750
|
videoPlayer.enabled = true;
|
|
@@ -714,6 +797,7 @@ namespace Hyper.Internal
|
|
|
714
797
|
tutorialDisplayP.gameObject.SetActive(targetDisplay == tutorialDisplayP);
|
|
715
798
|
tutorialDisplayL.gameObject.SetActive(targetDisplay == tutorialDisplayL);
|
|
716
799
|
currentDisplay = targetDisplay;
|
|
800
|
+
SetMediaDisplayAlpha(currentDisplay, 0f);
|
|
717
801
|
|
|
718
802
|
if (targetTexture != null)
|
|
719
803
|
{
|
|
@@ -733,7 +817,9 @@ namespace Hyper.Internal
|
|
|
733
817
|
updateVideoTimeCo = StartCoroutine(UpdateVideoTime());
|
|
734
818
|
|
|
735
819
|
InvokeTutorialLoadingCompleteOnce();
|
|
820
|
+
videoPlayer.time = 0;
|
|
736
821
|
videoPlayer.Play();
|
|
822
|
+
FadeInMediaDisplay(currentDisplay, VideoRevealDelay);
|
|
737
823
|
}
|
|
738
824
|
|
|
739
825
|
private void DisplayImageCard(TutorialCardData card, bool isInitialDisplay)
|
|
@@ -754,6 +840,7 @@ namespace Hyper.Internal
|
|
|
754
840
|
|
|
755
841
|
tutorialImageDisplay.gameObject.SetActive(true);
|
|
756
842
|
tutorialImageDisplay.texture = card.image.texture;
|
|
843
|
+
FadeInMediaDisplay(tutorialImageDisplay);
|
|
757
844
|
|
|
758
845
|
if (isInitialDisplay)
|
|
759
846
|
{
|
|
@@ -781,6 +868,11 @@ namespace Hyper.Internal
|
|
|
781
868
|
if (videoPlayer == null) return;
|
|
782
869
|
|
|
783
870
|
videoPlayer.prepareCompleted -= OnCardVideoPrepared;
|
|
871
|
+
videoPlayer.loopPointReached -= OnLoopPointReached;
|
|
872
|
+
if (videoPlayer.isPrepared)
|
|
873
|
+
{
|
|
874
|
+
videoPlayer.time = 0;
|
|
875
|
+
}
|
|
784
876
|
videoPlayer.Stop();
|
|
785
877
|
videoPlayer.targetTexture = null;
|
|
786
878
|
}
|
|
@@ -1023,6 +1115,7 @@ namespace Hyper.Internal
|
|
|
1023
1115
|
contentRevealScheduled = false;
|
|
1024
1116
|
isClosing = false;
|
|
1025
1117
|
CancelHiddenPreloadClose();
|
|
1118
|
+
StopMediaDisplayFade();
|
|
1026
1119
|
HideTutorialContent();
|
|
1027
1120
|
|
|
1028
1121
|
#if UNITY_EDITOR
|
|
@@ -32,7 +32,7 @@ Material:
|
|
|
32
32
|
m_Floats:
|
|
33
33
|
- _ColorMask: 15
|
|
34
34
|
- _InvertedRadius: 0
|
|
35
|
-
- _Radius:
|
|
35
|
+
- _Radius: 75
|
|
36
36
|
- _Softness: 1.75
|
|
37
37
|
- _Stencil: 0
|
|
38
38
|
- _StencilComp: 8
|
|
@@ -44,10 +44,10 @@ Material:
|
|
|
44
44
|
m_Colors:
|
|
45
45
|
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
|
|
46
46
|
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
|
47
|
-
- _CornerRadii: {r:
|
|
47
|
+
- _CornerRadii: {r: 75, g: 75, b: 0, a: 0}
|
|
48
48
|
- _InvertedCornerRadii: {r: 0, g: 0, b: 962.5, a: 1481.2}
|
|
49
49
|
- _Pivot: {r: 0.5, g: 0.556, b: 0, a: 0}
|
|
50
|
-
- _RectSize: {r:
|
|
50
|
+
- _RectSize: {r: 2532, g: 1216, b: 0, a: 0}
|
|
51
51
|
m_BuildTextureStacks: []
|
|
52
52
|
m_AllowLocking: 1
|
|
53
53
|
--- !u!114 &4644396538228076653
|
|
Binary file
|