app.hypergames.hypersdk 1.11.0-beta.1 → 1.11.0-beta.3
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 +17 -0
- package/Runtime/Internal/Core/HyperRuntime.cs +46 -5
- package/Runtime/Internal/Managers/GameManager.cs +18 -3
- package/Runtime/Internal/Managers/ReplayManager.cs +6 -0
- package/Runtime/Internal/Managers/TimeManager.cs +32 -32
- package/Runtime/Internal/UI/Components/ReplayController.cs +26 -0
- package/Runtime/Internal/UI/Modals/ReplayOverModal.cs +7 -0
- package/Runtime/Internal/UI/Modals/StartingInstructionModal.cs +1 -1
- package/Samples~/LaneRunner/LaneRunnerSample.unity +4 -3
- package/Samples~/LaneRunner/Prefabs/DoubleScorePickup.prefab +1 -1
- package/Samples~/LaneRunner/Prefabs/Obstacle.prefab +1 -1
- package/Samples~/LaneRunner/README.md +8 -1
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerPlayer.cs +18 -4
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerSpawner.cs +3 -1
- package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerTournamentAdapter.cs +7 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
# [1.11.0-beta.3](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.2...v1.11.0-beta.3) (2026-08-28)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **replay:** reset random state before playback restart ([526a4d9](https://github.com/mvmhyper/hyper-sdk/commit/526a4d98c9b22241e3c52bb6837145bad999fb2b))
|
|
7
|
+
* **sample:** support lane runner custom restart ([49da385](https://github.com/mvmhyper/hyper-sdk/commit/49da385aaa917de438672a8630d4fe7927741b2a))
|
|
8
|
+
|
|
9
|
+
# [1.11.0-beta.2](https://github.com/mvmhyper/hyper-sdk/compare/v1.11.0-beta.1...v1.11.0-beta.2) (2026-08-27)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **replay:** respect custom restart flows ([7470a17](https://github.com/mvmhyper/hyper-sdk/commit/7470a17c60534465500a202c24b0e360f61850fc))
|
|
15
|
+
* **replay:** shorten automatic replay start ([d05dc92](https://github.com/mvmhyper/hyper-sdk/commit/d05dc9267fbf05d392135eac3bb7c4f69b786eff))
|
|
16
|
+
* **timer:** align game time with fixed simulation ([b690d86](https://github.com/mvmhyper/hyper-sdk/commit/b690d861e178f05b21f1a7fbd87e255d97008a8b))
|
|
17
|
+
|
|
1
18
|
# [1.11.0-beta.1](https://github.com/mvmhyper/hyper-sdk/compare/v1.10.0...v1.11.0-beta.1) (2026-08-26)
|
|
2
19
|
|
|
3
20
|
|
|
@@ -20,6 +20,7 @@ namespace Hyper.Internal
|
|
|
20
20
|
|
|
21
21
|
private static bool _isInitialized;
|
|
22
22
|
private static bool _isSoftRestarting;
|
|
23
|
+
private static bool _replayRandomResetPending;
|
|
23
24
|
private static bool _customLoadingComplete;
|
|
24
25
|
private static bool _customLoadingActive;
|
|
25
26
|
private static float _customLoadingProgress;
|
|
@@ -118,13 +119,13 @@ namespace Hyper.Internal
|
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
/// <summary>
|
|
121
|
-
/// Restarts
|
|
122
|
-
///
|
|
122
|
+
/// Restarts replay playback while preserving its downloaded data and original seed.
|
|
123
|
+
/// Uses the configured custom restart flow when available, otherwise reloads HyperLoader.
|
|
123
124
|
/// </summary>
|
|
124
125
|
internal static void RestartReplaySession()
|
|
125
126
|
{
|
|
126
127
|
ReplaySessionState.ResetForWatchAgain();
|
|
127
|
-
|
|
128
|
+
RestartReplay();
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
internal static void SeekReplaySession(float targetProgress, float resumeSpeed, float seekSpeed)
|
|
@@ -133,14 +134,26 @@ namespace Hyper.Internal
|
|
|
133
134
|
|
|
134
135
|
ReplaySessionState.RequestSeek(targetProgress, resumeSpeed, seekSpeed);
|
|
135
136
|
ReplaySessionState.ResetForSeekRestart();
|
|
136
|
-
|
|
137
|
+
RestartReplay();
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
private static void
|
|
140
|
+
private static void RestartReplay()
|
|
140
141
|
{
|
|
141
142
|
_isSoftRestarting = true;
|
|
142
143
|
Time.timeScale = 1f;
|
|
143
144
|
|
|
145
|
+
if (_settings != null && _settings.useCustomRestartFlow &&
|
|
146
|
+
_gameManager != null && _eventManager != null)
|
|
147
|
+
{
|
|
148
|
+
_replayManager?.PrepareForRestart();
|
|
149
|
+
// Custom cleanup may use random streams. Rewind after cleanup, immediately before OnGameStart.
|
|
150
|
+
DeferReplayRandomReset();
|
|
151
|
+
_gameManager.ResetUIStateForCustomRestart();
|
|
152
|
+
_eventManager.InvokeCustomRestartEvent();
|
|
153
|
+
_isSoftRestarting = false;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
144
157
|
if (_host != null)
|
|
145
158
|
{
|
|
146
159
|
_host.StartCoroutine(RestartSessionCoroutine());
|
|
@@ -152,6 +165,32 @@ namespace Hyper.Internal
|
|
|
152
165
|
}
|
|
153
166
|
}
|
|
154
167
|
|
|
168
|
+
internal static void ResetReplayRandomState()
|
|
169
|
+
{
|
|
170
|
+
int originalSeed = unchecked((int)ReplaySessionState.OriginalRandomSeed);
|
|
171
|
+
if (HyperRandom.CurrentSeed == originalSeed)
|
|
172
|
+
{
|
|
173
|
+
// Preserve references held by game code while rewinding every stream.
|
|
174
|
+
HyperRandom.ResetAll();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
HyperRandom.Init(ReplaySessionState.OriginalRandomSeed);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
internal static void ApplyPendingReplayRandomReset()
|
|
182
|
+
{
|
|
183
|
+
if (!_replayRandomResetPending) return;
|
|
184
|
+
|
|
185
|
+
_replayRandomResetPending = false;
|
|
186
|
+
ResetReplayRandomState();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
internal static void DeferReplayRandomReset()
|
|
190
|
+
{
|
|
191
|
+
_replayRandomResetPending = true;
|
|
192
|
+
}
|
|
193
|
+
|
|
155
194
|
/// <summary>
|
|
156
195
|
/// Coroutine-based restart that properly unloads all scenes before reloading.
|
|
157
196
|
/// Handles async scene operations and ensures clean state before restart.
|
|
@@ -390,6 +429,7 @@ namespace Hyper.Internal
|
|
|
390
429
|
_customLoadingComplete = false;
|
|
391
430
|
_customLoadingActive = false;
|
|
392
431
|
_customLoadingProgress = 0f;
|
|
432
|
+
_replayRandomResetPending = false;
|
|
393
433
|
|
|
394
434
|
if (_singletonObject != null)
|
|
395
435
|
{
|
|
@@ -448,6 +488,7 @@ namespace Hyper.Internal
|
|
|
448
488
|
_customLoadingComplete = false;
|
|
449
489
|
_customLoadingActive = false;
|
|
450
490
|
_customLoadingProgress = 0f;
|
|
491
|
+
_replayRandomResetPending = false;
|
|
451
492
|
}
|
|
452
493
|
}
|
|
453
494
|
}
|
|
@@ -1025,6 +1025,8 @@ namespace Hyper.Internal.Managers
|
|
|
1025
1025
|
if (_gameStartRequested || _hasGameStarted || _hasGameEnded || !IsInGameScene) return;
|
|
1026
1026
|
_gameStartRequested = true;
|
|
1027
1027
|
|
|
1028
|
+
HyperRuntime.ApplyPendingReplayRandomReset();
|
|
1029
|
+
|
|
1028
1030
|
HideStartGameDeadlineSnackBar();
|
|
1029
1031
|
_dataManager.UpdateScore(0);
|
|
1030
1032
|
HyperRuntime.Timer.StartActiveGameTimer();
|
|
@@ -1062,26 +1064,39 @@ namespace Hyper.Internal.Managers
|
|
|
1062
1064
|
|
|
1063
1065
|
/// <summary>
|
|
1064
1066
|
/// Resets SDK UI state for custom restart flow.
|
|
1065
|
-
/// Called
|
|
1067
|
+
/// Called before OnCustomRestart so the developer's cleanup starts from reset SDK state.
|
|
1066
1068
|
/// </summary>
|
|
1067
1069
|
internal void ResetUIStateForCustomRestart()
|
|
1068
1070
|
{
|
|
1069
1071
|
HideGameOverModal();
|
|
1070
1072
|
HidePauseModals();
|
|
1073
|
+
if (_replayOverModal != null) _replayOverModal.SetActive(false);
|
|
1071
1074
|
|
|
1072
1075
|
// Reset game state flags
|
|
1073
1076
|
_hasGameStarted = false;
|
|
1074
1077
|
_gameStartRequested = false;
|
|
1075
1078
|
_hasGameEnded = false;
|
|
1079
|
+
_replayEndHandled = false;
|
|
1080
|
+
_replayFocusSuspended = false;
|
|
1076
1081
|
_isPaused = false;
|
|
1077
1082
|
_isFrozenBeforePause = false;
|
|
1078
1083
|
|
|
1079
|
-
HyperRuntime.Timer.
|
|
1084
|
+
HyperRuntime.Timer.ResetForGameRestart();
|
|
1080
1085
|
HyperRuntime.DataManager.UpdateScore(0);
|
|
1081
1086
|
HyperRuntime.DataManager.RestoreAudioAfterGameSessionEnd();
|
|
1082
1087
|
|
|
1083
1088
|
ShowControlBarElements();
|
|
1084
|
-
|
|
1089
|
+
_replayControllerComponent?.ResetForReplayRestart();
|
|
1090
|
+
|
|
1091
|
+
if (IsReplayMode && ReplaySessionState.IsSeeking)
|
|
1092
|
+
{
|
|
1093
|
+
HideStartingInstructionModal();
|
|
1094
|
+
StartCoroutine(StartSeekingReplayWhenReady());
|
|
1095
|
+
}
|
|
1096
|
+
else
|
|
1097
|
+
{
|
|
1098
|
+
ShowStartingInstructionModal();
|
|
1099
|
+
}
|
|
1085
1100
|
}
|
|
1086
1101
|
|
|
1087
1102
|
#endregion
|
|
@@ -39,6 +39,12 @@ namespace Hyper.Internal.Managers
|
|
|
39
39
|
ReplaySessionState.SetProgress(_playback.Progress01);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
internal void PrepareForRestart()
|
|
43
|
+
{
|
|
44
|
+
_endingGame = false;
|
|
45
|
+
_playback?.Stop();
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
internal void Cleanup()
|
|
43
49
|
{
|
|
44
50
|
if (_eventManager != null)
|
|
@@ -43,7 +43,7 @@ namespace Hyper.Internal.Managers
|
|
|
43
43
|
private bool _isSessionRunning;
|
|
44
44
|
private bool _sessionWarningTriggered;
|
|
45
45
|
private bool _timeUpTriggered;
|
|
46
|
-
private bool
|
|
46
|
+
private bool _timeUpPending;
|
|
47
47
|
|
|
48
48
|
private float _lastUnscaledTime;
|
|
49
49
|
private bool _hasGameEnded;
|
|
@@ -65,7 +65,7 @@ namespace Hyper.Internal.Managers
|
|
|
65
65
|
_hasBackendSessionDuration = false;
|
|
66
66
|
_lastUnscaledTime = Time.unscaledTime;
|
|
67
67
|
_timeUpTriggered = false;
|
|
68
|
-
|
|
68
|
+
_timeUpPending = false;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
#endregion
|
|
@@ -91,27 +91,21 @@ namespace Hyper.Internal.Managers
|
|
|
91
91
|
float unscaledDelta = Time.unscaledTime - _lastUnscaledTime;
|
|
92
92
|
_lastUnscaledTime = Time.unscaledTime;
|
|
93
93
|
|
|
94
|
-
if (!_gameManager.IsReplayMode)
|
|
95
|
-
{
|
|
96
|
-
float gameTimerDelta = Mathf.Approximately(Time.timeScale, 0f)
|
|
97
|
-
? 0f
|
|
98
|
-
: unscaledDelta;
|
|
99
|
-
|
|
100
|
-
UpdatePracticeTimer(gameTimerDelta);
|
|
101
|
-
UpdateBattleTimer(gameTimerDelta);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
94
|
UpdateSessionTimer(unscaledDelta);
|
|
105
|
-
|
|
95
|
+
DispatchPendingTimeUp();
|
|
106
96
|
}
|
|
107
97
|
|
|
108
98
|
internal void FixedTick()
|
|
109
99
|
{
|
|
110
|
-
if (
|
|
100
|
+
if (Mathf.Approximately(Time.timeScale, 0f))
|
|
111
101
|
return;
|
|
112
102
|
|
|
113
|
-
|
|
114
|
-
|
|
103
|
+
float gameTimerDelta = _gameManager != null && _gameManager.IsReplayMode
|
|
104
|
+
? Time.fixedDeltaTime
|
|
105
|
+
: Time.fixedUnscaledDeltaTime;
|
|
106
|
+
|
|
107
|
+
UpdatePracticeTimer(gameTimerDelta);
|
|
108
|
+
UpdateBattleTimer(gameTimerDelta);
|
|
115
109
|
}
|
|
116
110
|
|
|
117
111
|
public void Reset()
|
|
@@ -120,6 +114,19 @@ namespace Hyper.Internal.Managers
|
|
|
120
114
|
_lastUnscaledTime = Time.unscaledTime;
|
|
121
115
|
}
|
|
122
116
|
|
|
117
|
+
internal void ResetForGameRestart()
|
|
118
|
+
{
|
|
119
|
+
_practiceTimer = 0f;
|
|
120
|
+
_battleTimer = _gameManager != null ? _gameManager.Config.TimeLimit : 0f;
|
|
121
|
+
_isPracticeRunning = false;
|
|
122
|
+
_isBattleRunning = false;
|
|
123
|
+
_timeUpTriggered = false;
|
|
124
|
+
_timeUpPending = false;
|
|
125
|
+
_hasGameEnded = false;
|
|
126
|
+
_hasGameStarted = false;
|
|
127
|
+
_lastUnscaledTime = Time.unscaledTime;
|
|
128
|
+
}
|
|
129
|
+
|
|
123
130
|
public void ResumeGameTimersAfterPause()
|
|
124
131
|
{
|
|
125
132
|
if (_gameManager.IsBattleMode)
|
|
@@ -150,7 +157,7 @@ namespace Hyper.Internal.Managers
|
|
|
150
157
|
public void StopAllTimers()
|
|
151
158
|
{
|
|
152
159
|
_hasGameEnded = true;
|
|
153
|
-
|
|
160
|
+
_timeUpPending = false;
|
|
154
161
|
StopBattle();
|
|
155
162
|
StopPractice();
|
|
156
163
|
}
|
|
@@ -262,7 +269,7 @@ namespace Hyper.Internal.Managers
|
|
|
262
269
|
}
|
|
263
270
|
}
|
|
264
271
|
|
|
265
|
-
private void UpdateBattleTimer(float delta
|
|
272
|
+
private void UpdateBattleTimer(float delta)
|
|
266
273
|
{
|
|
267
274
|
if (_isBattleRunning)
|
|
268
275
|
{
|
|
@@ -280,7 +287,7 @@ namespace Hyper.Internal.Managers
|
|
|
280
287
|
_battleTimer = 0;
|
|
281
288
|
if (!_timeUpTriggered)
|
|
282
289
|
{
|
|
283
|
-
|
|
290
|
+
QueueTimeUp();
|
|
284
291
|
}
|
|
285
292
|
}
|
|
286
293
|
}
|
|
@@ -292,30 +299,23 @@ namespace Hyper.Internal.Managers
|
|
|
292
299
|
|
|
293
300
|
if (_battleTimer <= 0 && !_timeUpTriggered)
|
|
294
301
|
{
|
|
295
|
-
|
|
302
|
+
QueueTimeUp();
|
|
296
303
|
}
|
|
297
304
|
}
|
|
298
305
|
}
|
|
299
306
|
}
|
|
300
307
|
|
|
301
|
-
private void
|
|
308
|
+
private void QueueTimeUp()
|
|
302
309
|
{
|
|
303
|
-
|
|
304
|
-
{
|
|
305
|
-
_replayTimeUpPending = true;
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
_timeUpTriggered = true;
|
|
310
|
-
_eventManager?.InvokeTimeUpEvent();
|
|
310
|
+
_timeUpPending = true;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
|
-
private void
|
|
313
|
+
private void DispatchPendingTimeUp()
|
|
314
314
|
{
|
|
315
|
-
if (!
|
|
315
|
+
if (!_timeUpPending)
|
|
316
316
|
return;
|
|
317
317
|
|
|
318
|
-
|
|
318
|
+
_timeUpPending = false;
|
|
319
319
|
if (_hasGameEnded || _timeUpTriggered)
|
|
320
320
|
return;
|
|
321
321
|
|
|
@@ -132,6 +132,32 @@ namespace Hyper.Internal
|
|
|
132
132
|
if (progressSlider != null) progressSlider.SetValueWithoutNotify(1f);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
internal void ResetForReplayRestart()
|
|
136
|
+
{
|
|
137
|
+
float playbackSpeed = ReplaySessionState.UserPlaybackSpeed;
|
|
138
|
+
if (!ReplaySessionState.IsSeeking && playbackSpeeds != null && playbackSpeeds.Length > 0)
|
|
139
|
+
{
|
|
140
|
+
playbackSpeed = SanitizePlaybackSpeed(playbackSpeeds[0]);
|
|
141
|
+
ReplaySessionState.SetPlaybackSpeed(playbackSpeed);
|
|
142
|
+
Time.timeScale = playbackSpeed;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
isCapturingSeek = false;
|
|
146
|
+
pendingSeekTarget = ReplaySessionState.IsSeeking
|
|
147
|
+
? ReplaySessionState.SeekTarget01
|
|
148
|
+
: 0f;
|
|
149
|
+
wasSeeking = ReplaySessionState.IsSeeking;
|
|
150
|
+
|
|
151
|
+
if (progressSlider != null)
|
|
152
|
+
{
|
|
153
|
+
progressSlider.SetValueWithoutNotify(pendingSeekTarget);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
UpdatePlaybackSpeedLabel(playbackSpeed);
|
|
157
|
+
AlignPlaybackSpeedIndex(playbackSpeed);
|
|
158
|
+
SetSeekingPresentation(ReplaySessionState.IsSeeking);
|
|
159
|
+
}
|
|
160
|
+
|
|
135
161
|
private void ExitReplay()
|
|
136
162
|
{
|
|
137
163
|
if (exitButton != null) exitButton.interactable = false;
|
|
@@ -23,6 +23,13 @@ namespace Hyper.Internal
|
|
|
23
23
|
if (exitButton != null) exitButton.onClick.AddListener(Exit);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
private void OnEnable()
|
|
27
|
+
{
|
|
28
|
+
_handled = false;
|
|
29
|
+
if (watchAgainButton != null) watchAgainButton.interactable = true;
|
|
30
|
+
if (exitButton != null) exitButton.interactable = true;
|
|
31
|
+
}
|
|
32
|
+
|
|
26
33
|
private void OnDestroy()
|
|
27
34
|
{
|
|
28
35
|
if (watchAgainButton != null) watchAgainButton.onClick.RemoveListener(WatchAgain);
|
|
@@ -14,7 +14,7 @@ namespace Hyper.Internal
|
|
|
14
14
|
[SerializeField] private BlinkingText blinkingText;
|
|
15
15
|
[SerializeField] private RectTransform swipeGif;
|
|
16
16
|
[SerializeField] private RectTransform handSpriteInGif;
|
|
17
|
-
[SerializeField] private float replayAutoStartDelay =
|
|
17
|
+
[SerializeField] private float replayAutoStartDelay = 1f;
|
|
18
18
|
private GameManager _gameManager;
|
|
19
19
|
private StartCTA _currentCTA;
|
|
20
20
|
private SwipeDirection _swipeDirection;
|
|
@@ -491,7 +491,8 @@ MonoBehaviour:
|
|
|
491
491
|
laneChangeSpeed: 14
|
|
492
492
|
jumpVelocity: 7.5
|
|
493
493
|
swipeThreshold: 45
|
|
494
|
-
startPosition: {x: 3.62, y: 0.
|
|
494
|
+
startPosition: {x: 3.62, y: 0.5, z: 0}
|
|
495
|
+
groundHeight: 0.5
|
|
495
496
|
--- !u!54 &554141008
|
|
496
497
|
Rigidbody:
|
|
497
498
|
m_ObjectHideFlags: 0
|
|
@@ -574,7 +575,7 @@ BoxCollider:
|
|
|
574
575
|
m_Material: {fileID: 0}
|
|
575
576
|
m_IncludeLayers:
|
|
576
577
|
serializedVersion: 2
|
|
577
|
-
m_Bits:
|
|
578
|
+
m_Bits: 1
|
|
578
579
|
m_ExcludeLayers:
|
|
579
580
|
serializedVersion: 2
|
|
580
581
|
m_Bits: 0
|
|
@@ -1051,7 +1052,7 @@ BoxCollider:
|
|
|
1051
1052
|
m_Material: {fileID: 0}
|
|
1052
1053
|
m_IncludeLayers:
|
|
1053
1054
|
serializedVersion: 2
|
|
1054
|
-
m_Bits:
|
|
1055
|
+
m_Bits: 1
|
|
1055
1056
|
m_ExcludeLayers:
|
|
1056
1057
|
serializedVersion: 2
|
|
1057
1058
|
m_Bits: 0
|
|
@@ -13,13 +13,20 @@ deterministic spawning, a timed score multiplier, and replay integration.
|
|
|
13
13
|
Assign `LaneRunnerSample` as the Game and Practice scene in Hyper Game Content
|
|
14
14
|
Config, then use Hyper's Editor game-mode menu to run Practice, Battle, or Replay.
|
|
15
15
|
Battle sessions record to `hyper-replay.ndjson` in `Application.persistentDataPath`.
|
|
16
|
+
Enable Custom Restart Flow in Hyper SDK Settings to test in-place Watch Again and
|
|
17
|
+
replay seeking without reloading the scene.
|
|
18
|
+
|
|
19
|
+
The sample uses only Unity's built-in `Default` layer, `Untagged`, and
|
|
20
|
+
`MainCamera`. Its collider overrides and ground safety do not require changes to
|
|
21
|
+
the importing project's tags, layers, or physics collision matrix.
|
|
16
22
|
|
|
17
23
|
## Integration structure
|
|
18
24
|
|
|
19
25
|
- `LaneRunnerGame` owns rules, score, speed, and power-up state.
|
|
20
26
|
- `LaneRunnerPlayer` owns physical input and applies commands in `FixedUpdate`.
|
|
21
27
|
- `LaneRunnerSpawner` creates deterministic rows using `HyperRandom`.
|
|
22
|
-
- `LaneRunnerTournamentAdapter` connects score, power-ups,
|
|
28
|
+
- `LaneRunnerTournamentAdapter` connects score, power-ups, lifecycle, and custom
|
|
29
|
+
restart handling to HyperSDK.
|
|
23
30
|
- `LaneRunnerReplayAdapter` records accepted commands and injects replay commands
|
|
24
31
|
through the same player path.
|
|
25
32
|
|
|
@@ -11,7 +11,8 @@ namespace HyperSamples.LaneRunner
|
|
|
11
11
|
[SerializeField] private float laneChangeSpeed = 14f;
|
|
12
12
|
[SerializeField] private float jumpVelocity = 7.5f;
|
|
13
13
|
[SerializeField] private float swipeThreshold = 45f;
|
|
14
|
-
[SerializeField] private Vector3 startPosition = new Vector3(0f, 0.
|
|
14
|
+
[SerializeField] private Vector3 startPosition = new Vector3(0f, 0.5f, 0f);
|
|
15
|
+
[SerializeField] private float groundHeight = 0.5f;
|
|
15
16
|
|
|
16
17
|
public event Action<LaneRunnerCommand> CommandApplied;
|
|
17
18
|
|
|
@@ -48,18 +49,18 @@ namespace HyperSamples.LaneRunner
|
|
|
48
49
|
|
|
49
50
|
private void FixedUpdate()
|
|
50
51
|
{
|
|
52
|
+
StabilizeGround();
|
|
53
|
+
|
|
51
54
|
if (pendingLaneDelta != 0)
|
|
52
55
|
{
|
|
53
56
|
int nextLane = Mathf.Clamp(targetLane + pendingLaneDelta, -1, 1);
|
|
54
57
|
pendingLaneDelta = 0;
|
|
55
|
-
Debug.Log($"LaneRunnerPlayer: Moving from lane {targetLane} to {nextLane}");
|
|
56
58
|
if (nextLane != targetLane)
|
|
57
59
|
{
|
|
58
60
|
LaneRunnerCommand applied = nextLane < targetLane
|
|
59
61
|
? LaneRunnerCommand.MoveLeft
|
|
60
62
|
: LaneRunnerCommand.MoveRight;
|
|
61
63
|
targetLane = nextLane;
|
|
62
|
-
Debug.Log($"targetLane updated to {targetLane}");
|
|
63
64
|
CommandApplied?.Invoke(applied);
|
|
64
65
|
}
|
|
65
66
|
}
|
|
@@ -120,7 +121,20 @@ namespace HyperSamples.LaneRunner
|
|
|
120
121
|
|
|
121
122
|
private bool IsGrounded()
|
|
122
123
|
{
|
|
123
|
-
return body.position.y <= 0.
|
|
124
|
+
return body.position.y <= groundHeight + 0.18f && body.linearVelocity.y <= 0.05f;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private void StabilizeGround()
|
|
128
|
+
{
|
|
129
|
+
if (body.position.y >= groundHeight || body.linearVelocity.y > 0f) return;
|
|
130
|
+
|
|
131
|
+
Vector3 position = body.position;
|
|
132
|
+
position.y = groundHeight;
|
|
133
|
+
body.position = position;
|
|
134
|
+
|
|
135
|
+
Vector3 velocity = body.linearVelocity;
|
|
136
|
+
velocity.y = 0f;
|
|
137
|
+
body.linearVelocity = velocity;
|
|
124
138
|
}
|
|
125
139
|
|
|
126
140
|
private void ReadTouchInput()
|
|
@@ -61,7 +61,9 @@ namespace HyperSamples.LaneRunner
|
|
|
61
61
|
{
|
|
62
62
|
for (int i = activeItems.Count - 1; i >= 0; i--)
|
|
63
63
|
{
|
|
64
|
-
if (activeItems[i]
|
|
64
|
+
if (activeItems[i] == null) continue;
|
|
65
|
+
activeItems[i].gameObject.SetActive(false);
|
|
66
|
+
Destroy(activeItems[i].gameObject);
|
|
65
67
|
}
|
|
66
68
|
activeItems.Clear();
|
|
67
69
|
}
|
|
@@ -16,6 +16,7 @@ namespace HyperSamples.LaneRunner
|
|
|
16
16
|
{
|
|
17
17
|
HyperSDK.Event.OnGameStart += HandleGameStart;
|
|
18
18
|
HyperSDK.Event.OnGameEnded += HandleGameEnded;
|
|
19
|
+
HyperSDK.Event.OnCustomRestart += HandleCustomRestart;
|
|
19
20
|
game.ScoreChanged += HandleScoreChanged;
|
|
20
21
|
game.DoubleScoreChanged += HandleDoubleScoreChanged;
|
|
21
22
|
game.GameOverRequested += HandleGameOverRequested;
|
|
@@ -25,6 +26,7 @@ namespace HyperSamples.LaneRunner
|
|
|
25
26
|
{
|
|
26
27
|
HyperSDK.Event.OnGameStart -= HandleGameStart;
|
|
27
28
|
HyperSDK.Event.OnGameEnded -= HandleGameEnded;
|
|
29
|
+
HyperSDK.Event.OnCustomRestart -= HandleCustomRestart;
|
|
28
30
|
game.ScoreChanged -= HandleScoreChanged;
|
|
29
31
|
game.DoubleScoreChanged -= HandleDoubleScoreChanged;
|
|
30
32
|
game.GameOverRequested -= HandleGameOverRequested;
|
|
@@ -35,6 +37,11 @@ namespace HyperSamples.LaneRunner
|
|
|
35
37
|
game.Begin(!HyperSDK.Game.IsReplayMode);
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
private void HandleCustomRestart()
|
|
41
|
+
{
|
|
42
|
+
game.Stop();
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
private void HandleGameEnded()
|
|
39
46
|
{
|
|
40
47
|
game.Stop();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "app.hypergames.hypersdk",
|
|
3
|
-
"version": "1.11.0-beta.
|
|
3
|
+
"version": "1.11.0-beta.3",
|
|
4
4
|
"displayName": "HyperSDK",
|
|
5
5
|
"description": "Official Unity SDK for Hyper: multiplayer battles, leaderboards, deterministic RNG, session management, tutorials, and WebGL optimizations for production-ready games.",
|
|
6
6
|
"unity": "6000.0",
|