app.hypergames.hypersdk 1.9.1 → 1.11.0-beta.1

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.
Files changed (124) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/Editor/Scripts/Setup/HyperSDKInstaller.cs +42 -0
  3. package/Editor/Scripts/Tools/HyperGameModeMenu.cs +27 -7
  4. package/Runtime/Internal/AssemblyInfo.cs +1 -0
  5. package/Runtime/Internal/Bootstrap/HyperLoader.cs +186 -8
  6. package/Runtime/Internal/Core/HyperRuntime.cs +58 -0
  7. package/Runtime/Internal/Core/HyperRuntimeHost.cs +6 -0
  8. package/Runtime/Internal/Data/URLData.cs +18 -1
  9. package/Runtime/Internal/Managers/BackendManager.cs +281 -24
  10. package/Runtime/Internal/Managers/DataManager.cs +207 -21
  11. package/Runtime/Internal/Managers/GameManager.cs +350 -15
  12. package/Runtime/Internal/Managers/ReplayManager.cs +111 -0
  13. package/Runtime/Internal/Managers/ReplayManager.cs.meta +11 -0
  14. package/Runtime/Internal/Managers/TimeManager.cs +56 -14
  15. package/Runtime/Internal/Replay/ReplayActivityRecorder.cs +182 -0
  16. package/Runtime/Internal/Replay/ReplayActivityRecorder.cs.meta +2 -0
  17. package/Runtime/Internal/Replay/ReplaySessionState.cs +181 -0
  18. package/Runtime/Internal/Replay/ReplaySessionState.cs.meta +2 -0
  19. package/Runtime/Internal/Replay.meta +8 -0
  20. package/Runtime/Internal/ScriptableObjects/HyperSDKSettings.cs +5 -0
  21. package/Runtime/Internal/UI/Animation/LoadingTextAnimation.cs +15 -1
  22. package/Runtime/Internal/UI/Components/ConcaveImage.cs +147 -0
  23. package/Runtime/Internal/UI/Components/ConcaveImage.cs.meta +11 -0
  24. package/Runtime/Internal/UI/Components/ControlBar.cs +86 -6
  25. package/Runtime/Internal/UI/Components/PauseButton.cs +4 -1
  26. package/Runtime/Internal/UI/Components/ReplayController.cs +501 -0
  27. package/Runtime/Internal/UI/Components/ReplayController.cs.meta +2 -0
  28. package/Runtime/Internal/UI/Components/ReplayMarkerPulse.cs +89 -0
  29. package/Runtime/Internal/UI/Components/ReplayMarkerPulse.cs.meta +2 -0
  30. package/Runtime/Internal/UI/Components/ReplayPresentationCanvas.cs +183 -0
  31. package/Runtime/Internal/UI/Components/ReplayPresentationCanvas.cs.meta +2 -0
  32. package/Runtime/Internal/UI/Components/SnackBar.cs +2 -9
  33. package/Runtime/Internal/UI/Modals/LoadingFailedModal.cs +12 -0
  34. package/Runtime/Internal/UI/Modals/ReplayOverModal.cs +54 -0
  35. package/Runtime/Internal/UI/Modals/ReplayOverModal.cs.meta +2 -0
  36. package/Runtime/Internal/UI/Modals/StartingInstructionModal.cs +38 -29
  37. package/Runtime/Public/AssemblyInfo.cs +3 -0
  38. package/Runtime/Public/AssemblyInfo.cs.meta +2 -0
  39. package/Runtime/Public/Core/HyperSDK.cs +305 -109
  40. package/Runtime/Public/Hyper.asmdef +2 -1
  41. package/Runtime/Public/Replay/ReplayPlaybackEngine.cs +555 -0
  42. package/Runtime/Public/Replay/ReplayPlaybackEngine.cs.meta +2 -0
  43. package/Runtime/Public/Replay.meta +8 -0
  44. package/Runtime/Resources/Fonts/Nunito/Nunito-Light SDF.asset +528 -521
  45. package/Runtime/Resources/Materials/HyperReplayScanlines.preset +340 -0
  46. package/Runtime/Resources/Materials/HyperReplayScanlines.preset.meta +8 -0
  47. package/Runtime/Resources/Materials/ReplayScanlineOverlay.mat +55 -0
  48. package/Runtime/Resources/Materials/ReplayScanlineOverlay.mat.meta +8 -0
  49. package/Runtime/Resources/Shaders/ConcaveImage.shader +155 -0
  50. package/Runtime/Resources/Shaders/ConcaveImage.shader.meta +10 -0
  51. package/Runtime/Resources/Shaders/ReplayScanlineOverlay.shader +150 -0
  52. package/Runtime/Resources/Shaders/ReplayScanlineOverlay.shader.meta +10 -0
  53. package/Runtime/Resources/Sprites/Replays/Fallback avatar.png +0 -0
  54. package/Runtime/Resources/Sprites/Replays/Fallback avatar.png.meta +156 -0
  55. package/Runtime/Resources/Sprites/Replays/Recording.png +0 -0
  56. package/Runtime/Resources/Sprites/{Control Bar/Top_Black_Rounded_Bar_Portrait_IV.png.meta → Replays/Recording.png.meta} +16 -29
  57. package/Runtime/Resources/Sprites/Replays/Replay frame.png +0 -0
  58. package/Runtime/Resources/Sprites/Replays/Replay frame.png.meta +156 -0
  59. package/Runtime/Resources/Sprites/Replays/Replay modal icon.png +0 -0
  60. package/Runtime/Resources/Sprites/Replays/Replay modal icon.png.meta +156 -0
  61. package/Runtime/Resources/Sprites/Replays.meta +8 -0
  62. package/Runtime/Resources/UIPrefabs/ControlBar.prefab +389 -42
  63. package/Runtime/Resources/UIPrefabs/GamePausedModal_Battle.prefab +1 -1
  64. package/Runtime/Resources/UIPrefabs/ReplayController.prefab +1269 -0
  65. package/Runtime/Resources/UIPrefabs/ReplayController.prefab.meta +7 -0
  66. package/Runtime/Resources/UIPrefabs/ReplayInputBlocker.prefab +90 -0
  67. package/Runtime/Resources/UIPrefabs/ReplayInputBlocker.prefab.meta +7 -0
  68. package/Runtime/Resources/UIPrefabs/ReplayOverModal.prefab +958 -0
  69. package/Runtime/Resources/UIPrefabs/ReplayOverModal.prefab.meta +7 -0
  70. package/Runtime/Resources/UIPrefabs/ReplaySeekOverlay.prefab +189 -0
  71. package/Runtime/Resources/UIPrefabs/ReplaySeekOverlay.prefab.meta +7 -0
  72. package/Runtime/Resources/UIPrefabs/Rounded Cutout.prefab +96 -0
  73. package/Runtime/Resources/UIPrefabs/Rounded Cutout.prefab.meta +7 -0
  74. package/Runtime/Resources/UIPrefabs/SnackBar.prefab +23 -6
  75. package/Runtime/Shared/AssemblyInfo.cs +5 -0
  76. package/Runtime/Shared/AssemblyInfo.cs.meta +11 -0
  77. package/Runtime/Shared/DTOs/Enums.cs +8 -3
  78. package/Runtime/Shared/DTOs/Score/ScoreSnapshot.cs +13 -1
  79. package/Runtime/Shared/Replay/IReplayPlaybackEngine.cs +37 -0
  80. package/Runtime/Shared/Replay/IReplayPlaybackEngine.cs.meta +11 -0
  81. package/Runtime/Shared/Replay.meta +8 -0
  82. package/Runtime/Shared/Utils/HyperRandom.cs +25 -5
  83. package/Samples~/LaneRunner/LaneRunnerSample.unity +1603 -0
  84. package/Samples~/LaneRunner/LaneRunnerSample.unity.meta +7 -0
  85. package/Samples~/LaneRunner/Materials/DoubleScore.mat +86 -0
  86. package/Samples~/LaneRunner/Materials/DoubleScore.mat.meta +8 -0
  87. package/Samples~/LaneRunner/Materials/Ground.mat +84 -0
  88. package/Samples~/LaneRunner/Materials/Ground.mat.meta +8 -0
  89. package/Samples~/LaneRunner/Materials/Lane.mat +84 -0
  90. package/Samples~/LaneRunner/Materials/Lane.mat.meta +8 -0
  91. package/Samples~/LaneRunner/Materials/LaneMarker.mat +84 -0
  92. package/Samples~/LaneRunner/Materials/LaneMarker.mat.meta +8 -0
  93. package/Samples~/LaneRunner/Materials/Obstacle.mat +84 -0
  94. package/Samples~/LaneRunner/Materials/Obstacle.mat.meta +8 -0
  95. package/Samples~/LaneRunner/Materials/Player.mat +84 -0
  96. package/Samples~/LaneRunner/Materials/Player.mat.meta +8 -0
  97. package/Samples~/LaneRunner/Materials.meta +8 -0
  98. package/Samples~/LaneRunner/Prefabs/DoubleScorePickup.prefab +152 -0
  99. package/Samples~/LaneRunner/Prefabs/DoubleScorePickup.prefab.meta +7 -0
  100. package/Samples~/LaneRunner/Prefabs/Obstacle.prefab +152 -0
  101. package/Samples~/LaneRunner/Prefabs/Obstacle.prefab.meta +7 -0
  102. package/Samples~/LaneRunner/Prefabs.meta +8 -0
  103. package/Samples~/LaneRunner/README.md +35 -0
  104. package/Samples~/LaneRunner/README.md.meta +7 -0
  105. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerCommand.cs +9 -0
  106. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerCommand.cs.meta +2 -0
  107. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerGame.cs +93 -0
  108. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerGame.cs.meta +2 -0
  109. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerPlayer.cs +155 -0
  110. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerPlayer.cs.meta +2 -0
  111. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerSpawner.cs +110 -0
  112. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerSpawner.cs.meta +2 -0
  113. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerTrackItem.cs +41 -0
  114. package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerTrackItem.cs.meta +2 -0
  115. package/Samples~/LaneRunner/Scripts/Gameplay.meta +8 -0
  116. package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerReplayAdapter.cs +62 -0
  117. package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerReplayAdapter.cs.meta +2 -0
  118. package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerTournamentAdapter.cs +64 -0
  119. package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerTournamentAdapter.cs.meta +2 -0
  120. package/Samples~/LaneRunner/Scripts/Hyper.meta +8 -0
  121. package/Samples~/LaneRunner/Scripts.meta +8 -0
  122. package/Tests.meta +8 -0
  123. package/package.json +9 -3
  124. package/Runtime/Resources/Sprites/Control Bar/Top_Black_Rounded_Bar_Portrait_IV.png +0 -0
@@ -0,0 +1,183 @@
1
+ using Hyper.Shared;
2
+ using UnityEngine;
3
+ using UnityEngine.UI;
4
+
5
+ namespace Hyper.Internal
6
+ {
7
+ /// <summary>
8
+ /// Owns replay-only UI independently from the session-scoped HyperCanvas.
9
+ /// The root survives only while a seek restart is in progress.
10
+ /// </summary>
11
+ internal sealed class ReplayPresentationCanvas : MonoBehaviour
12
+ {
13
+ private const int NormalSortingOrder = 9999;
14
+ private const int SeekingSortingOrder = 10001;
15
+
16
+ private static ReplayPresentationCanvas instance;
17
+
18
+ internal GameObject InputBlocker { get; private set; }
19
+ internal GameObject ControllerObject { get; private set; }
20
+ internal ReplayController Controller { get; private set; }
21
+
22
+ internal static ReplayPresentationCanvas GetOrCreate(bool isPortrait)
23
+ {
24
+ if (instance == null)
25
+ {
26
+ instance = FindFirstObjectByType<ReplayPresentationCanvas>();
27
+ }
28
+
29
+ if (instance == null)
30
+ {
31
+ var root = new GameObject(
32
+ "ReplayPresentationCanvas",
33
+ typeof(RectTransform),
34
+ typeof(Canvas),
35
+ typeof(CanvasScaler),
36
+ typeof(GraphicRaycaster));
37
+ instance = root.AddComponent<ReplayPresentationCanvas>();
38
+ DontDestroyOnLoad(root);
39
+ instance.Build(isPortrait);
40
+ }
41
+ else
42
+ {
43
+ instance.ConfigureCanvas(isPortrait);
44
+ instance.EnsurePresentationObjects();
45
+ }
46
+
47
+ instance.gameObject.SetActive(true);
48
+ instance.ControllerObject?.transform.SetAsLastSibling();
49
+ return instance;
50
+ }
51
+
52
+ internal static bool IsPresentationRoot(GameObject root)
53
+ {
54
+ return instance != null && root == instance.gameObject;
55
+ }
56
+
57
+ internal static void SetSeekingLayer(bool isSeeking)
58
+ {
59
+ if (instance == null) return;
60
+ Canvas canvas = instance.GetComponent<Canvas>();
61
+ if (canvas != null) canvas.sortingOrder = isSeeking ? SeekingSortingOrder : NormalSortingOrder;
62
+ }
63
+
64
+ internal static void DestroyCurrent()
65
+ {
66
+ if (instance == null) return;
67
+ Destroy(instance.gameObject);
68
+ instance = null;
69
+ }
70
+
71
+ private void Awake()
72
+ {
73
+ if (instance != null && instance != this)
74
+ {
75
+ Destroy(gameObject);
76
+ return;
77
+ }
78
+
79
+ instance = this;
80
+ }
81
+
82
+ private void OnDestroy()
83
+ {
84
+ if (instance == this) instance = null;
85
+ }
86
+
87
+ private void Build(bool isPortrait)
88
+ {
89
+ ConfigureCanvas(isPortrait);
90
+
91
+ var safeArea = new GameObject("SafeArea", typeof(RectTransform));
92
+ RectTransform safeAreaRect = safeArea.GetComponent<RectTransform>();
93
+ safeAreaRect.SetParent(transform, false);
94
+ StretchToParent(safeAreaRect);
95
+ if (isPortrait) safeArea.AddComponent<SafeArea>();
96
+
97
+ CreateInputBlocker(safeAreaRect);
98
+ CreateController(safeAreaRect);
99
+ }
100
+
101
+ private void ConfigureCanvas(bool isPortrait)
102
+ {
103
+ Canvas canvas = GetComponent<Canvas>();
104
+ canvas.renderMode = RenderMode.ScreenSpaceOverlay;
105
+ canvas.overrideSorting = true;
106
+ canvas.sortingOrder = ReplaySessionState.IsSeeking
107
+ ? SeekingSortingOrder
108
+ : NormalSortingOrder;
109
+
110
+ CanvasScaler scaler = GetComponent<CanvasScaler>();
111
+ scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
112
+ scaler.referenceResolution = new Vector2(1080f, 2160f);
113
+ scaler.matchWidthOrHeight = isPortrait ? 1f : 0.593f;
114
+ }
115
+
116
+ private void EnsurePresentationObjects()
117
+ {
118
+ Transform safeArea = transform.Find("SafeArea");
119
+ if (safeArea == null)
120
+ {
121
+ Build(Screen.height > Screen.width);
122
+ return;
123
+ }
124
+
125
+ if (InputBlocker == null)
126
+ {
127
+ Transform blocker = safeArea.Find("ReplayInputBlocker");
128
+ InputBlocker = blocker != null ? blocker.gameObject : null;
129
+ if (InputBlocker == null) CreateInputBlocker((RectTransform)safeArea);
130
+ }
131
+
132
+ if (ControllerObject == null)
133
+ {
134
+ Transform controller = safeArea.Find("ReplayController");
135
+ ControllerObject = controller != null ? controller.gameObject : null;
136
+ Controller = ControllerObject != null ? ControllerObject.GetComponent<ReplayController>() : null;
137
+ if (ControllerObject == null) CreateController((RectTransform)safeArea);
138
+ }
139
+ }
140
+
141
+ private void CreateInputBlocker(RectTransform parent)
142
+ {
143
+ GameObject prefab = Resources.Load<GameObject>("UIPrefabs/ReplayInputBlocker");
144
+ InputBlocker = prefab != null
145
+ ? Instantiate(prefab, parent, false)
146
+ : new GameObject("ReplayInputBlocker", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
147
+
148
+ RectTransform rect = InputBlocker.GetComponent<RectTransform>();
149
+ if (rect.parent == null) rect.SetParent(parent, false);
150
+ StretchToParent(rect);
151
+
152
+ Image image = InputBlocker.GetComponent<Image>();
153
+ if (image != null)
154
+ {
155
+ image.raycastTarget = true;
156
+ if (prefab == null) image.color = Color.clear;
157
+ }
158
+ }
159
+
160
+ private void CreateController(RectTransform parent)
161
+ {
162
+ GameObject prefab = Resources.Load<GameObject>("UIPrefabs/ReplayController");
163
+ if (prefab == null)
164
+ {
165
+ Debug.LogWarning("[ReplayPresentationCanvas] ReplayController prefab not found.");
166
+ return;
167
+ }
168
+
169
+ ControllerObject = Instantiate(prefab, parent, false);
170
+ ControllerObject.transform.SetAsLastSibling();
171
+ Controller = ControllerObject.GetComponent<ReplayController>();
172
+ if (Controller == null) Controller = ControllerObject.AddComponent<ReplayController>();
173
+ }
174
+
175
+ private static void StretchToParent(RectTransform rect)
176
+ {
177
+ rect.anchorMin = Vector2.zero;
178
+ rect.anchorMax = Vector2.one;
179
+ rect.offsetMin = Vector2.zero;
180
+ rect.offsetMax = Vector2.zero;
181
+ }
182
+ }
183
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 01a4ad28a491451587a73a62982beef7
@@ -74,14 +74,7 @@ namespace Hyper.Internal
74
74
 
75
75
  private void ConfigureOrientation(RectTransform rect)
76
76
  {
77
- if (HyperRuntime.GameManager.IsPortrait())
78
- {
79
- rect.sizeDelta = new Vector2(rect.sizeDelta.x, 150);
80
- }
81
- else
82
- {
83
- rect.sizeDelta = new Vector2(rect.sizeDelta.x, 180);
84
- }
77
+ rect.sizeDelta = new Vector2(rect.sizeDelta.x, 163.909f);
85
78
  }
86
79
 
87
80
  void OnDisable()
@@ -140,7 +133,7 @@ namespace Hyper.Internal
140
133
 
141
134
  private void SlideInAndOutAnimation(bool pingPong = true)
142
135
  {
143
- int yOffset = HyperRuntime.GameManager.IsPortrait() ? 150 : 180;
136
+ int yOffset = 165;
144
137
  rectTransform.DOAnchorPosY(-yOffset, 0.45f).SetUpdate(true).SetEase(HyperTween.Ease.InQuad).OnComplete(() =>
145
138
  {
146
139
  if (pingPong)
@@ -90,6 +90,12 @@ namespace Hyper.Internal
90
90
  retryTimerText.text = $"00:0{5 - HyperRuntime.BackendManager.CurrentRetryCount}";
91
91
  }
92
92
 
93
+ internal void ShowRetryCountdown(int secondsRemaining)
94
+ {
95
+ DisableAllCTAButtons();
96
+ retryTimerText.text = $"00:{Mathf.Max(0, secondsRemaining):00}";
97
+ }
98
+
93
99
  private void EnableRetryLaterButton()
94
100
  {
95
101
  ctaButtonsHolder.interactable = true;
@@ -115,6 +121,12 @@ namespace Hyper.Internal
115
121
 
116
122
  public void Retry() // On Retry Button Clicked
117
123
  {
124
+ if (_gameManager.IsReplayMode)
125
+ {
126
+ _gameManager.RetryReplayLoading();
127
+ return;
128
+ }
129
+
118
130
  StartReconnectionCoroutine();
119
131
  }
120
132
 
@@ -0,0 +1,54 @@
1
+ using Hyper.Shared;
2
+ using UnityEngine;
3
+ using UnityEngine.UI;
4
+
5
+ namespace Hyper.Internal
6
+ {
7
+ internal sealed class ReplayOverModal : MonoBehaviour
8
+ {
9
+ [SerializeField] private Button watchAgainButton;
10
+ [SerializeField] private Button exitButton;
11
+ private bool _handled;
12
+
13
+ private void Awake()
14
+ {
15
+ Button[] buttons = GetComponentsInChildren<Button>(true);
16
+ for (int i = 0; i < buttons.Length; i++)
17
+ {
18
+ if (buttons[i].name == "Watch Again") watchAgainButton = buttons[i];
19
+ else if (buttons[i].name == "Exit") exitButton = buttons[i];
20
+ }
21
+
22
+ if (watchAgainButton != null) watchAgainButton.onClick.AddListener(WatchAgain);
23
+ if (exitButton != null) exitButton.onClick.AddListener(Exit);
24
+ }
25
+
26
+ private void OnDestroy()
27
+ {
28
+ if (watchAgainButton != null) watchAgainButton.onClick.RemoveListener(WatchAgain);
29
+ if (exitButton != null) exitButton.onClick.RemoveListener(Exit);
30
+ }
31
+
32
+ private void WatchAgain()
33
+ {
34
+ if (_handled) return;
35
+ _handled = true;
36
+ DisableButtons();
37
+ HyperRuntime.RestartReplaySession();
38
+ }
39
+
40
+ private void Exit()
41
+ {
42
+ if (_handled) return;
43
+ _handled = true;
44
+ DisableButtons();
45
+ HyperRuntime.GameManager?.ExitCompletedReplay();
46
+ }
47
+
48
+ private void DisableButtons()
49
+ {
50
+ if (watchAgainButton != null) watchAgainButton.interactable = false;
51
+ if (exitButton != null) exitButton.interactable = false;
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 8841f748a9924814cbb010c54be7916e
@@ -1,10 +1,8 @@
1
1
  using System.Collections;
2
- using System.Runtime.InteropServices;
3
2
  using Hyper.Internal.Managers;
4
3
  using Hyper.Shared;
5
4
  using TMPro;
6
5
  using UnityEngine;
7
- using UnityEngine.SceneManagement;
8
6
  using UnityEngine.UI;
9
7
 
10
8
  namespace Hyper.Internal
@@ -12,38 +10,61 @@ namespace Hyper.Internal
12
10
  internal class StartingInstructionModal : MonoBehaviour
13
11
  {
14
12
 
15
- #if UNITY_WEBGL
16
- [DllImport("__Internal")]
17
- private static extern void MarkGameStartedOnHTML();
18
- #endif
19
13
  [SerializeField] private TextMeshProUGUI startCtaLabel;
20
14
  [SerializeField] private BlinkingText blinkingText;
21
15
  [SerializeField] private RectTransform swipeGif;
22
16
  [SerializeField] private RectTransform handSpriteInGif;
23
- private EventManager _eventManager;
24
- private DataManager _dataManager;
17
+ [SerializeField] private float replayAutoStartDelay = 2f;
25
18
  private GameManager _gameManager;
26
- private BackendManager _backendManager;
27
19
  private StartCTA _currentCTA;
28
20
  private SwipeDirection _swipeDirection;
29
21
 
30
22
  private bool _canAcceptInput = false;
31
23
  private Vector2 _swipeStartPosition;
32
24
  private bool _isTrackingSwipe;
25
+ private bool _hasStarted;
26
+ private Coroutine _replayAutoStartCoroutine;
33
27
  private const float SWIPE_THRESHOLD = 50f;
34
28
 
35
29
  void Awake()
36
30
  {
37
- _dataManager = HyperRuntime.DataManager;
38
- _eventManager = HyperRuntime.EventManager;
39
31
  _gameManager = HyperRuntime.GameManager;
40
- _backendManager = HyperRuntime.BackendManager;
41
32
  }
42
33
 
43
34
  void OnEnable()
44
35
  {
45
36
  _canAcceptInput = false;
46
- StartCoroutine(EnableInputAfterDelay());
37
+ _hasStarted = false;
38
+
39
+ if (HyperRuntime.GameManager != null && HyperRuntime.GameManager.IsReplayMode)
40
+ {
41
+ _replayAutoStartCoroutine = StartCoroutine(AutoStartReplay());
42
+ }
43
+ else
44
+ {
45
+ StartCoroutine(EnableInputAfterDelay());
46
+ }
47
+ }
48
+
49
+ private void OnDisable()
50
+ {
51
+ if (_replayAutoStartCoroutine != null)
52
+ {
53
+ StopCoroutine(_replayAutoStartCoroutine);
54
+ _replayAutoStartCoroutine = null;
55
+ }
56
+ }
57
+
58
+ private IEnumerator AutoStartReplay()
59
+ {
60
+ float elapsed = 0f;
61
+ float delay = Mathf.Max(0f, replayAutoStartDelay);
62
+ while (elapsed < delay)
63
+ {
64
+ if (Application.isFocused) elapsed += Time.unscaledDeltaTime;
65
+ yield return null;
66
+ }
67
+ StartGame();
47
68
  }
48
69
 
49
70
  private IEnumerator EnableInputAfterDelay()
@@ -59,6 +80,7 @@ namespace Hyper.Internal
59
80
 
60
81
  void Update()
61
82
  {
83
+ if (HyperRuntime.GameManager != null && HyperRuntime.GameManager.IsReplayMode) return;
62
84
  if (!gameObject.activeSelf || !_canAcceptInput) return;
63
85
 
64
86
  if (_currentCTA == StartCTA.TapToPlay)
@@ -279,23 +301,10 @@ namespace Hyper.Internal
279
301
  /// </summary>
280
302
  private void StartGame()
281
303
  {
282
- _gameManager.HideStartGameDeadlineSnackBar();
283
- _dataManager.UpdateScore(0);
284
- HyperRuntime.Timer.StartActiveGameTimer();
304
+ if (_hasStarted) return;
305
+ _hasStarted = true;
285
306
 
286
- _dataManager.StartCapturingScores(); // Start capture/sender immediately so offline starts still buffer score samples.
287
- _backendManager.StartSendingScores(); // Sender loop already pauses actual submissions while disconnected or token-gated.
288
-
289
- // startGame handshake can only be emitted when socket is connected.
290
- if (_backendManager.isSocketConnected)
291
- {
292
- _backendManager.SendGameStarted();
293
- }
294
-
295
- #if UNITY_WEBGL && !UNITY_EDITOR
296
- MarkGameStartedOnHTML();
297
- #endif
298
- _eventManager.InvokeGameStartEvent();
307
+ _gameManager.StartGameplay();
299
308
  gameObject.SetActive(false);
300
309
  }
301
310
 
@@ -0,0 +1,3 @@
1
+ using System.Runtime.CompilerServices;
2
+
3
+ [assembly: InternalsVisibleTo("Hyper.Tests.EditMode")]
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 2fefc67a1c18b054a84657d9260361b9