app.hypergames.hypersdk 1.8.21 → 1.8.23
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 +132 -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/Plugins/WebGL/HyperTutorialCachePlugin.jslib +5 -1
- 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/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/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/TutorialAnnouncementModal.prefab.meta +7 -0
- package/Runtime/Resources/UIPrefabs/TutorialLandscapeModal.prefab +2 -2
- package/Runtime/Resources/UIPrefabs/TutorialPortraitModal.prefab +1 -1
- 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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.8.23](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.22...v1.8.23) (2026-07-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* signal tutorial availability before cache completes ([7a8d043](https://github.com/mvmhyper/hyper-sdk/commit/7a8d043e016c36007b088535f06ae5f4bb4ae64d))
|
|
7
|
+
|
|
8
|
+
## [1.8.22](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.21...v1.8.22) (2026-07-08)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* add tutorial announcement prompt ([0b5e009](https://github.com/mvmhyper/hyper-sdk/commit/0b5e0099346d144b56d35a39d613e24a776ef11b))
|
|
14
|
+
|
|
1
15
|
## [1.8.21](https://github.com/mvmhyper/hyper-sdk/compare/v1.8.20...v1.8.21) (2026-07-08)
|
|
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,38 @@ 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
|
{
|
|
1647
|
+
case "available":
|
|
1648
|
+
tutorialExistsButFailed = false;
|
|
1649
|
+
tutorialFetchSuccess = false;
|
|
1650
|
+
TutorialAvailability = TutorialAvailabilityState.Available;
|
|
1651
|
+
break;
|
|
1652
|
+
|
|
1632
1653
|
case "success":
|
|
1633
|
-
_eventManager?.InvokeTutorialFetchCompleteEvent();
|
|
1634
1654
|
_isFetchingTutorial = false;
|
|
1635
1655
|
tutorialExistsButFailed = false;
|
|
1636
1656
|
tutorialFetchSuccess = true;
|
|
1637
|
-
|
|
1657
|
+
TutorialAvailability = TutorialAvailabilityState.Available;
|
|
1658
|
+
_eventManager?.InvokeTutorialFetchCompleteEvent();
|
|
1659
|
+
InvokeAndClearTutorialCallbacks(TutorialFetchStatus.Success);
|
|
1638
1660
|
break;
|
|
1639
1661
|
|
|
1640
1662
|
case "no_tutorial":
|
|
1641
1663
|
_isFetchingTutorial = false;
|
|
1642
1664
|
tutorialExistsButFailed = false;
|
|
1643
1665
|
tutorialFetchSuccess = false;
|
|
1644
|
-
|
|
1666
|
+
TutorialAvailability = TutorialAvailabilityState.Unavailable;
|
|
1667
|
+
InvokeAndClearTutorialCallbacks(TutorialFetchStatus.NoTutorialExists);
|
|
1645
1668
|
_eventManager?.InvokeTutorialNotExistsEvent();
|
|
1646
1669
|
break;
|
|
1647
1670
|
|
|
@@ -1659,16 +1682,106 @@ namespace Hyper.Internal.Managers
|
|
|
1659
1682
|
|
|
1660
1683
|
private void HandleRetryOrFail(TutorialFetchStatus status)
|
|
1661
1684
|
{
|
|
1662
|
-
_onTutorialFailure?.Invoke(status);
|
|
1663
|
-
_eventManager?.InvokeTutorialFetchFailedEvent();
|
|
1664
1685
|
_isFetchingTutorial = false;
|
|
1686
|
+
|
|
1687
|
+
if (status == TutorialFetchStatus.DownloadFailed)
|
|
1688
|
+
{
|
|
1689
|
+
tutorialExistsButFailed = true;
|
|
1690
|
+
tutorialFetchSuccess = false;
|
|
1691
|
+
TutorialAvailability = TutorialAvailabilityState.ExistsButFailed;
|
|
1692
|
+
}
|
|
1693
|
+
else
|
|
1694
|
+
{
|
|
1695
|
+
tutorialExistsButFailed = false;
|
|
1696
|
+
tutorialFetchSuccess = false;
|
|
1697
|
+
TutorialAvailability = TutorialAvailabilityState.Unknown;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
InvokeAndClearTutorialCallbacks(status);
|
|
1701
|
+
_eventManager?.InvokeTutorialFetchFailedEvent();
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
private bool IsTutorialFetchComplete()
|
|
1705
|
+
{
|
|
1706
|
+
return (TutorialAvailability == TutorialAvailabilityState.Available && !_isFetchingTutorial) ||
|
|
1707
|
+
TutorialAvailability == TutorialAvailabilityState.Unavailable;
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
private void AddTutorialCallbacks(Action fetchComplete, Action onSuccess, Action<TutorialFetchStatus> onFailure)
|
|
1711
|
+
{
|
|
1712
|
+
if (fetchComplete != null) _fetchCompleteCallbacks.Add(fetchComplete);
|
|
1713
|
+
if (onSuccess != null) _tutorialSuccessCallbacks.Add(onSuccess);
|
|
1714
|
+
if (onFailure != null) _tutorialFailureCallbacks.Add(onFailure);
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
private void InvokeTutorialCallbacksForCurrentState(Action fetchComplete, Action onSuccess, Action<TutorialFetchStatus> onFailure)
|
|
1718
|
+
{
|
|
1719
|
+
switch (TutorialAvailability)
|
|
1720
|
+
{
|
|
1721
|
+
case TutorialAvailabilityState.Available:
|
|
1722
|
+
onSuccess?.Invoke();
|
|
1723
|
+
break;
|
|
1724
|
+
case TutorialAvailabilityState.Unavailable:
|
|
1725
|
+
onFailure?.Invoke(TutorialFetchStatus.NoTutorialExists);
|
|
1726
|
+
break;
|
|
1727
|
+
case TutorialAvailabilityState.ExistsButFailed:
|
|
1728
|
+
onFailure?.Invoke(TutorialFetchStatus.DownloadFailed);
|
|
1729
|
+
break;
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
fetchComplete?.Invoke();
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
private void InvokeAndClearTutorialCallbacks(TutorialFetchStatus status)
|
|
1736
|
+
{
|
|
1737
|
+
if (status == TutorialFetchStatus.Success)
|
|
1738
|
+
{
|
|
1739
|
+
for (int i = 0; i < _tutorialSuccessCallbacks.Count; i++)
|
|
1740
|
+
{
|
|
1741
|
+
_tutorialSuccessCallbacks[i]?.Invoke();
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
else
|
|
1745
|
+
{
|
|
1746
|
+
for (int i = 0; i < _tutorialFailureCallbacks.Count; i++)
|
|
1747
|
+
{
|
|
1748
|
+
_tutorialFailureCallbacks[i]?.Invoke(status);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
for (int i = 0; i < _fetchCompleteCallbacks.Count; i++)
|
|
1753
|
+
{
|
|
1754
|
+
_fetchCompleteCallbacks[i]?.Invoke();
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
_fetchCompleteCallbacks.Clear();
|
|
1758
|
+
_tutorialSuccessCallbacks.Clear();
|
|
1759
|
+
_tutorialFailureCallbacks.Clear();
|
|
1665
1760
|
}
|
|
1666
1761
|
|
|
1667
1762
|
#if UNITY_EDITOR
|
|
1668
1763
|
private IEnumerator SimulateTutorialFetch()
|
|
1669
1764
|
{
|
|
1670
|
-
|
|
1671
|
-
|
|
1765
|
+
var value = UnityEngine.Random.Range(0, 4);
|
|
1766
|
+
Debug.Log($"[Tutorial] Simulating tutorial fetch result: {value}");
|
|
1767
|
+
|
|
1768
|
+
switch (value)
|
|
1769
|
+
{
|
|
1770
|
+
case 0:
|
|
1771
|
+
OnTutorialCacheComplete("success");
|
|
1772
|
+
break;
|
|
1773
|
+
case 1:
|
|
1774
|
+
OnTutorialCacheComplete("download_failed");
|
|
1775
|
+
break;
|
|
1776
|
+
case 2:
|
|
1777
|
+
OnTutorialCacheComplete("fetch_failed");
|
|
1778
|
+
break;
|
|
1779
|
+
default:
|
|
1780
|
+
OnTutorialCacheComplete("no_tutorial");
|
|
1781
|
+
break;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
yield break;
|
|
1672
1785
|
}
|
|
1673
1786
|
#endif
|
|
1674
1787
|
|
|
@@ -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
|
+
}
|
|
@@ -8,6 +8,7 @@ mergeInto(LibraryManager.library, {
|
|
|
8
8
|
const gameObject = UTF8ToString(gameObjectPtr);
|
|
9
9
|
|
|
10
10
|
const endpoint = `${baseUrl}/games/${gameSlug}/tutorials`;
|
|
11
|
+
let tutorialRecordsFound = false;
|
|
11
12
|
|
|
12
13
|
caches.open("tutorial-cache").then(async (cache) => {
|
|
13
14
|
try {
|
|
@@ -22,6 +23,9 @@ mergeInto(LibraryManager.library, {
|
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
tutorialRecordsFound = true;
|
|
27
|
+
SendMessage(gameObject, "OnTutorialCacheComplete", "available");
|
|
28
|
+
|
|
25
29
|
const cards = records
|
|
26
30
|
.filter(r => r && r.fileUrl)
|
|
27
31
|
.map((record, index) => ({
|
|
@@ -48,7 +52,7 @@ mergeInto(LibraryManager.library, {
|
|
|
48
52
|
SendMessage(gameObject, "OnTutorialCacheComplete", "success");
|
|
49
53
|
} catch (e) {
|
|
50
54
|
console.error("Tutorial caching error:", e);
|
|
51
|
-
const status = e.message.includes("Download failed") ? "download_failed" : "fetch_failed";
|
|
55
|
+
const status = tutorialRecordsFound || e.message.includes("Download failed") ? "download_failed" : "fetch_failed";
|
|
52
56
|
SendMessage(gameObject, "OnTutorialCacheComplete", status);
|
|
53
57
|
}
|
|
54
58
|
});
|
|
Binary file
|
package/Runtime/Resources/Sprites/{TutorialModal/P Container.png.meta → Frame 2147224037.png.meta }
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
fileFormatVersion: 2
|
|
2
|
-
guid:
|
|
2
|
+
guid: 79ea1f5df2417914697380a19c4438fc
|
|
3
3
|
TextureImporter:
|
|
4
4
|
internalIDToNameTable:
|
|
5
5
|
- first:
|
|
6
|
-
213:
|
|
7
|
-
second:
|
|
6
|
+
213: 4044033671516185218
|
|
7
|
+
second: Frame 2147224037_0
|
|
8
8
|
externalObjects: {}
|
|
9
9
|
serializedVersion: 13
|
|
10
10
|
mipmaps:
|
|
@@ -53,7 +53,7 @@ TextureImporter:
|
|
|
53
53
|
spritePivot: {x: 0.5, y: 0.5}
|
|
54
54
|
spritePixelsToUnits: 100
|
|
55
55
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
|
56
|
-
spriteGenerateFallbackPhysicsShape:
|
|
56
|
+
spriteGenerateFallbackPhysicsShape: 0
|
|
57
57
|
alphaUsage: 1
|
|
58
58
|
alphaIsTransparency: 1
|
|
59
59
|
spriteTessellationDetail: -1
|
|
@@ -72,11 +72,11 @@ TextureImporter:
|
|
|
72
72
|
platformSettings:
|
|
73
73
|
- serializedVersion: 4
|
|
74
74
|
buildTarget: DefaultTexturePlatform
|
|
75
|
-
maxTextureSize:
|
|
75
|
+
maxTextureSize: 1024
|
|
76
76
|
resizeAlgorithm: 0
|
|
77
77
|
textureFormat: -1
|
|
78
78
|
textureCompression: 1
|
|
79
|
-
compressionQuality:
|
|
79
|
+
compressionQuality: 50
|
|
80
80
|
crunchedCompression: 1
|
|
81
81
|
allowsAlphaSplitting: 0
|
|
82
82
|
overridden: 0
|
|
@@ -96,19 +96,6 @@ TextureImporter:
|
|
|
96
96
|
ignorePlatformSupport: 0
|
|
97
97
|
androidETC2FallbackOverride: 0
|
|
98
98
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
|
99
|
-
- serializedVersion: 4
|
|
100
|
-
buildTarget: Android
|
|
101
|
-
maxTextureSize: 2048
|
|
102
|
-
resizeAlgorithm: 0
|
|
103
|
-
textureFormat: -1
|
|
104
|
-
textureCompression: 1
|
|
105
|
-
compressionQuality: 50
|
|
106
|
-
crunchedCompression: 0
|
|
107
|
-
allowsAlphaSplitting: 0
|
|
108
|
-
overridden: 0
|
|
109
|
-
ignorePlatformSupport: 0
|
|
110
|
-
androidETC2FallbackOverride: 0
|
|
111
|
-
forceMaximumCompressionQuality_BC6H_BC7: 0
|
|
112
99
|
- serializedVersion: 4
|
|
113
100
|
buildTarget: WebGL
|
|
114
101
|
maxTextureSize: 2048
|
|
@@ -126,13 +113,13 @@ TextureImporter:
|
|
|
126
113
|
serializedVersion: 2
|
|
127
114
|
sprites:
|
|
128
115
|
- serializedVersion: 2
|
|
129
|
-
name:
|
|
116
|
+
name: Frame 2147224037_0
|
|
130
117
|
rect:
|
|
131
118
|
serializedVersion: 2
|
|
132
119
|
x: 0
|
|
133
120
|
y: 0
|
|
134
|
-
width:
|
|
135
|
-
height:
|
|
121
|
+
width: 1180
|
|
122
|
+
height: 808
|
|
136
123
|
alignment: 0
|
|
137
124
|
pivot: {x: 0, y: 0}
|
|
138
125
|
border: {x: 0, y: 0, z: 0, w: 0}
|
|
@@ -141,8 +128,8 @@ TextureImporter:
|
|
|
141
128
|
physicsShape: []
|
|
142
129
|
tessellationDetail: -1
|
|
143
130
|
bones: []
|
|
144
|
-
spriteID:
|
|
145
|
-
internalID:
|
|
131
|
+
spriteID: 282e072b23b4f1830800000000000000
|
|
132
|
+
internalID: 4044033671516185218
|
|
146
133
|
vertices: []
|
|
147
134
|
indices:
|
|
148
135
|
edges: []
|
|
@@ -161,7 +148,7 @@ TextureImporter:
|
|
|
161
148
|
spriteCustomMetadata:
|
|
162
149
|
entries: []
|
|
163
150
|
nameFileIdTable:
|
|
164
|
-
|
|
151
|
+
Frame 2147224037_0: 4044033671516185218
|
|
165
152
|
mipmapLimitGroupName:
|
|
166
153
|
pSDRemoveMatte: 0
|
|
167
154
|
userData:
|
|
@@ -36,7 +36,7 @@ RectTransform:
|
|
|
36
36
|
m_AnchorMin: {x: 0.5, y: 1}
|
|
37
37
|
m_AnchorMax: {x: 0.5, y: 1}
|
|
38
38
|
m_AnchoredPosition: {x: 43.75238, y: -29.683838}
|
|
39
|
-
m_SizeDelta: {x: 536.
|
|
39
|
+
m_SizeDelta: {x: 536.76, y: 28.8}
|
|
40
40
|
m_Pivot: {x: 0.5, y: 0.5}
|
|
41
41
|
--- !u!222 &4167215372404575111
|
|
42
42
|
CanvasRenderer:
|
|
@@ -228,14 +228,14 @@ RectTransform:
|
|
|
228
228
|
m_GameObject: {fileID: 1339429102133054667}
|
|
229
229
|
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
|
230
230
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
231
|
-
m_LocalScale: {x: 1.
|
|
232
|
-
m_ConstrainProportionsScale:
|
|
231
|
+
m_LocalScale: {x: 1.1, y: 1.1, z: 1.1}
|
|
232
|
+
m_ConstrainProportionsScale: 1
|
|
233
233
|
m_Children: []
|
|
234
234
|
m_Father: {fileID: 3144244002372456063}
|
|
235
235
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
236
236
|
m_AnchorMin: {x: 0.5, y: 0.5}
|
|
237
237
|
m_AnchorMax: {x: 0.5, y: 0.5}
|
|
238
|
-
m_AnchoredPosition: {x: -278.74756, y: 0
|
|
238
|
+
m_AnchoredPosition: {x: -278.74756, y: 0}
|
|
239
239
|
m_SizeDelta: {x: 51.5, y: 41}
|
|
240
240
|
m_Pivot: {x: 0.5, y: 0.5}
|
|
241
241
|
--- !u!222 &6646643930977255528
|
|
@@ -416,14 +416,14 @@ RectTransform:
|
|
|
416
416
|
m_GameObject: {fileID: 2742868627260701116}
|
|
417
417
|
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
|
418
418
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
419
|
-
m_LocalScale: {x:
|
|
420
|
-
m_ConstrainProportionsScale:
|
|
419
|
+
m_LocalScale: {x: 0.8, y: 0.8, z: 0.8}
|
|
420
|
+
m_ConstrainProportionsScale: 1
|
|
421
421
|
m_Children: []
|
|
422
422
|
m_Father: {fileID: 6079682807480946086}
|
|
423
423
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
|
424
|
-
m_AnchorMin: {x: 0.5, y:
|
|
425
|
-
m_AnchorMax: {x: 0.5, y:
|
|
426
|
-
m_AnchoredPosition: {x: -222.96002, y:
|
|
424
|
+
m_AnchorMin: {x: 0.5, y: 0.5}
|
|
425
|
+
m_AnchorMax: {x: 0.5, y: 0.5}
|
|
426
|
+
m_AnchoredPosition: {x: -222.96002, y: 0}
|
|
427
427
|
m_SizeDelta: {x: 51.5, y: 41}
|
|
428
428
|
m_Pivot: {x: 0.5, y: 0.5}
|
|
429
429
|
--- !u!222 &6181031279965490734
|
|
@@ -613,7 +613,7 @@ RectTransform:
|
|
|
613
613
|
m_AnchorMin: {x: 0.5, y: 1}
|
|
614
614
|
m_AnchorMax: {x: 0.5, y: 1}
|
|
615
615
|
m_AnchoredPosition: {x: 42.23999, y: -23.5}
|
|
616
|
-
m_SizeDelta: {x: 425.
|
|
616
|
+
m_SizeDelta: {x: 425.1, y: 28.8}
|
|
617
617
|
m_Pivot: {x: 0.5, y: 0.5}
|
|
618
618
|
--- !u!222 &8979309464556769080
|
|
619
619
|
CanvasRenderer:
|
|
@@ -184,7 +184,7 @@ RectTransform:
|
|
|
184
184
|
m_GameObject: {fileID: 1641685960017025922}
|
|
185
185
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
186
186
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
187
|
-
m_LocalScale: {x: 1.
|
|
187
|
+
m_LocalScale: {x: 1.25, y: 1.25, z: 1.25}
|
|
188
188
|
m_ConstrainProportionsScale: 1
|
|
189
189
|
m_Children:
|
|
190
190
|
- {fileID: 6118213081617822483}
|
|
@@ -125,7 +125,7 @@ RectTransform:
|
|
|
125
125
|
m_GameObject: {fileID: 936452418214250672}
|
|
126
126
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
127
127
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
128
|
-
m_LocalScale: {x: 1.
|
|
128
|
+
m_LocalScale: {x: 1.25, y: 1.25, z: 1.25}
|
|
129
129
|
m_ConstrainProportionsScale: 1
|
|
130
130
|
m_Children:
|
|
131
131
|
- {fileID: 943448477169687493}
|
|
@@ -257,7 +257,7 @@ RectTransform:
|
|
|
257
257
|
m_GameObject: {fileID: 936452418214250672}
|
|
258
258
|
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
|
259
259
|
m_LocalPosition: {x: 0, y: 0, z: 0}
|
|
260
|
-
m_LocalScale: {x: 1.
|
|
260
|
+
m_LocalScale: {x: 1.25, y: 1.25, z: 1.25}
|
|
261
261
|
m_ConstrainProportionsScale: 1
|
|
262
262
|
m_Children:
|
|
263
263
|
- {fileID: 943448477169687493}
|