app.hypergames.hypersdk 1.11.0-beta.9 → 1.11.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.
- package/CHANGELOG.md +188 -0
- package/Editor/Scripts/Inspectors/HyperSDKSettingsEditor.cs +15 -0
- package/HyperLoader.unity +2 -2
- package/Runtime/Internal/Bootstrap/HyperLoader.cs +5 -1
- package/Runtime/Internal/Core/HyperRuntime.cs +63 -1
- package/Runtime/Internal/Managers/BackendManager.cs +26 -3
- package/Runtime/Internal/Managers/DataManager.cs +8 -0
- package/Runtime/Internal/Managers/EventManager.cs +7 -1
- package/Runtime/Internal/Managers/GameManager.cs +21 -13
- package/Runtime/Internal/Managers/ReplayManager.cs +7 -2
- package/Runtime/Internal/Managers/TimeManager.cs +30 -12
- package/Runtime/Internal/Replay/ReplaySessionState.cs +15 -4
- package/Runtime/Internal/ScriptableObjects/HyperSDKSettings.cs +10 -0
- package/Runtime/Internal/UI/Components/ReplayController.cs +205 -27
- package/Runtime/Internal/UI/Components/ReplayPresentationCanvas.cs +1 -1
- package/Runtime/Internal/UI/Components/ReplaySeekOverlay.cs +18 -0
- package/Runtime/Internal/UI/Components/ReplaySeekOverlay.cs.meta +2 -0
- package/Runtime/Internal/UI/Modals/ReplayPausedModal.cs +9 -1
- package/Runtime/Internal/UI/Modals/StartingInstructionModal.cs +1 -1
- package/Runtime/Internal/UI/Modals/TimeUpModal.cs +2 -2
- package/Runtime/Public/Replay/ReplayPlaybackEngine.cs +71 -2
- package/Runtime/Resources/Fonts/Wonder Boys/Wonder Boys.asset +394 -379
- package/Runtime/Resources/HyperSDKSettings.asset +2 -0
- package/Runtime/Resources/Sprites/Circle Loader/Property 1=1.png.meta +7 -7
- package/Runtime/Resources/Sprites/Circle Loader/Property 1=2.png.meta +7 -7
- package/Runtime/Resources/Sprites/Circle Loader/Property 1=3.png.meta +7 -7
- package/Runtime/Resources/Sprites/Circle Loader/Property 1=4.png.meta +7 -7
- package/Runtime/Resources/Sprites/Circle Loader/Property 1=5.png.meta +7 -7
- package/Runtime/Resources/UIPrefabs/ReplayController.prefab +295 -15
- package/Runtime/Resources/UIPrefabs/ReplaySeekOverlay.prefab +291 -6
- package/Samples~/LaneRunner/Prefabs/DoubleScorePickup.prefab +1 -1
- package/Samples~/LaneRunner/Prefabs/Obstacle.prefab +1 -1
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerSpawner.cs +1 -1
- package/Samples~/LaneRunner/Scripts/Gameplay/LaneRunnerTrackItem.cs +9 -2
- package/Samples~/LaneRunner/Scripts/Hyper/LaneRunnerTournamentAdapter.cs +1 -0
- package/package.json +1 -1
- /package/Runtime/{Public → Shared}/PlayerDeviceClassification.cs +0 -0
- /package/Runtime/{Public → Shared}/PlayerDeviceClassification.cs.meta +0 -0
|
@@ -103,14 +103,28 @@ namespace Hyper.Internal.Managers
|
|
|
103
103
|
if (Mathf.Approximately(Time.timeScale, 0f))
|
|
104
104
|
return;
|
|
105
105
|
|
|
106
|
-
float gameTimerDelta =
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
float gameTimerDelta = GetGameTimerDelta(
|
|
107
|
+
_gameManager != null && _gameManager.IsReplayMode,
|
|
108
|
+
Time.fixedDeltaTime,
|
|
109
|
+
Time.timeScale);
|
|
109
110
|
|
|
110
111
|
UpdatePracticeTimer(gameTimerDelta);
|
|
111
112
|
UpdateBattleTimer(gameTimerDelta);
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
internal static float GetGameTimerDelta(
|
|
116
|
+
bool isReplayMode,
|
|
117
|
+
float fixedDeltaTime,
|
|
118
|
+
float timeScale)
|
|
119
|
+
{
|
|
120
|
+
if (timeScale <= 0f) return 0f;
|
|
121
|
+
|
|
122
|
+
// The first FixedUpdate after a pause can report the whole paused
|
|
123
|
+
// interval in fixedUnscaledDeltaTime. Derive one live simulation
|
|
124
|
+
// step instead, keeping real-time duration at custom time scales.
|
|
125
|
+
return isReplayMode ? fixedDeltaTime : fixedDeltaTime / timeScale;
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
public void Reset()
|
|
115
129
|
{
|
|
116
130
|
_practiceTimer = 0;
|
|
@@ -299,9 +313,9 @@ namespace Hyper.Internal.Managers
|
|
|
299
313
|
}
|
|
300
314
|
|
|
301
315
|
_battleTimer -= delta;
|
|
302
|
-
if (_battleTimer <=
|
|
316
|
+
if (_battleTimer <= 0f || Mathf.Approximately(_battleTimer, 0f))
|
|
303
317
|
{
|
|
304
|
-
_battleTimer =
|
|
318
|
+
_battleTimer = 0f;
|
|
305
319
|
if (!_timeUpTriggered)
|
|
306
320
|
{
|
|
307
321
|
QueueTimeUp();
|
|
@@ -314,7 +328,8 @@ namespace Hyper.Internal.Managers
|
|
|
314
328
|
{
|
|
315
329
|
_battleTimer = _sessionTimer;
|
|
316
330
|
|
|
317
|
-
if (_battleTimer <=
|
|
331
|
+
if ((_battleTimer <= 0f || Mathf.Approximately(_battleTimer, 0f)) &&
|
|
332
|
+
!_timeUpTriggered)
|
|
318
333
|
{
|
|
319
334
|
QueueTimeUp();
|
|
320
335
|
}
|
|
@@ -324,6 +339,14 @@ namespace Hyper.Internal.Managers
|
|
|
324
339
|
|
|
325
340
|
private void QueueTimeUp()
|
|
326
341
|
{
|
|
342
|
+
if (_timeUpPending || _timeUpTriggered || _hasGameEnded) return;
|
|
343
|
+
|
|
344
|
+
_battleTimer = 0f;
|
|
345
|
+
_isBattleRunning = false;
|
|
346
|
+
ClearPausedGameplayTimerState();
|
|
347
|
+
// The SDK runs before gameplay FixedUpdate. The expiry step is blocked,
|
|
348
|
+
// even when several fixed steps run before the next rendered frame.
|
|
349
|
+
_gameManager.ReachGameplayCutoff();
|
|
327
350
|
_timeUpPending = true;
|
|
328
351
|
}
|
|
329
352
|
|
|
@@ -372,12 +395,7 @@ namespace Hyper.Internal.Managers
|
|
|
372
395
|
|
|
373
396
|
if (_hasGameStarted && _gameManager.IsBattleMode)
|
|
374
397
|
{
|
|
375
|
-
|
|
376
|
-
if (!_timeUpTriggered)
|
|
377
|
-
{
|
|
378
|
-
_timeUpTriggered = true;
|
|
379
|
-
_eventManager?.InvokeTimeUpEvent();
|
|
380
|
-
}
|
|
398
|
+
QueueTimeUp();
|
|
381
399
|
}
|
|
382
400
|
else
|
|
383
401
|
{
|
|
@@ -14,6 +14,7 @@ namespace Hyper.Internal
|
|
|
14
14
|
public static string AvatarUrl { get; private set; }
|
|
15
15
|
public static string EntryId { get; private set; }
|
|
16
16
|
public static string ReplayNdjson { get; private set; }
|
|
17
|
+
public static int? FinalScore { get; private set; }
|
|
17
18
|
public static Texture2D AvatarTexture { get; private set; }
|
|
18
19
|
public static bool HasReplayData => !string.IsNullOrWhiteSpace(ReplayNdjson);
|
|
19
20
|
public static long OriginalRandomSeed { get; private set; }
|
|
@@ -48,6 +49,7 @@ namespace Hyper.Internal
|
|
|
48
49
|
AvatarUrl = avatarUrl ?? string.Empty;
|
|
49
50
|
EntryId = entryId ?? string.Empty;
|
|
50
51
|
ReplayNdjson = string.Empty;
|
|
52
|
+
FinalScore = null;
|
|
51
53
|
ReleaseAvatarTexture();
|
|
52
54
|
OriginalRandomSeed = originalRandomSeed;
|
|
53
55
|
Progress01 = 0f;
|
|
@@ -61,13 +63,18 @@ namespace Hyper.Internal
|
|
|
61
63
|
GameplayStartProgress01 = 0f;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
public static void SetMetadata(
|
|
66
|
+
public static void SetMetadata(
|
|
67
|
+
string playerName,
|
|
68
|
+
string avatarUrl,
|
|
69
|
+
long originalRandomSeed,
|
|
70
|
+
int finalScore)
|
|
65
71
|
{
|
|
66
72
|
if (!IsActive) return;
|
|
67
73
|
|
|
68
74
|
PlayerName = playerName ?? string.Empty;
|
|
69
75
|
AvatarUrl = avatarUrl ?? string.Empty;
|
|
70
76
|
OriginalRandomSeed = originalRandomSeed;
|
|
77
|
+
FinalScore = finalScore;
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
public static void SetReplayData(string ndjson)
|
|
@@ -83,10 +90,10 @@ namespace Hyper.Internal
|
|
|
83
90
|
AvatarTexture = texture;
|
|
84
91
|
}
|
|
85
92
|
|
|
86
|
-
public static
|
|
93
|
+
public static bool SetProgress(float progress)
|
|
87
94
|
{
|
|
88
|
-
if (!IsActive) return;
|
|
89
|
-
if (IsSeeking && !_seekPlaybackReady) return;
|
|
95
|
+
if (!IsActive) return false;
|
|
96
|
+
if (IsSeeking && !_seekPlaybackReady) return false;
|
|
90
97
|
float rawProgress = Math.Max(0f, Math.Min(1f, progress));
|
|
91
98
|
Progress01 = GameplayStartProgress01 >= 1f
|
|
92
99
|
? 0f
|
|
@@ -101,7 +108,10 @@ namespace Hyper.Internal
|
|
|
101
108
|
PlaybackSpeed = UserPlaybackSpeed;
|
|
102
109
|
RestoreAudioAfterSeek();
|
|
103
110
|
SeekCompleted?.Invoke(PlaybackSpeed);
|
|
111
|
+
return true;
|
|
104
112
|
}
|
|
113
|
+
|
|
114
|
+
return false;
|
|
105
115
|
}
|
|
106
116
|
|
|
107
117
|
public static void SetPlaybackSpeed(float speed)
|
|
@@ -190,6 +200,7 @@ namespace Hyper.Internal
|
|
|
190
200
|
AvatarUrl = string.Empty;
|
|
191
201
|
EntryId = string.Empty;
|
|
192
202
|
ReplayNdjson = string.Empty;
|
|
203
|
+
FinalScore = null;
|
|
193
204
|
ReleaseAvatarTexture();
|
|
194
205
|
OriginalRandomSeed = 0;
|
|
195
206
|
Progress01 = 0f;
|
|
@@ -76,6 +76,16 @@ namespace Hyper.Internal
|
|
|
76
76
|
"instead of using default restart. Listen to HyperSDK.Event.OnCustomRestart in your game manager.")]
|
|
77
77
|
public bool useCustomRestartFlow = false;
|
|
78
78
|
|
|
79
|
+
[Tooltip("Use the game-owned custom restart flow for replay Watch Again and seeking. " +
|
|
80
|
+
"Leave disabled to rebuild the game through HyperLoader for deterministic replay state.")]
|
|
81
|
+
public bool useCustomReplayRestartFlow = false;
|
|
82
|
+
|
|
83
|
+
[Space(20)]
|
|
84
|
+
[Header("Replay Settings")]
|
|
85
|
+
[Tooltip("Allow replay viewers to seek from the replay progress bar. " +
|
|
86
|
+
"Leave disabled until the game has been validated for accelerated replay reconstruction.")]
|
|
87
|
+
public bool allowReplaySeeking = false;
|
|
88
|
+
|
|
79
89
|
#endregion
|
|
80
90
|
|
|
81
91
|
#region Editor Testing
|
|
@@ -12,35 +12,59 @@ namespace Hyper.Internal
|
|
|
12
12
|
{
|
|
13
13
|
internal const int AvatarTextureSize = 128;
|
|
14
14
|
|
|
15
|
-
[SerializeField] private Slider
|
|
15
|
+
[SerializeField] private Slider seekableSlider;
|
|
16
|
+
[SerializeField] private Slider readOnlySlider;
|
|
16
17
|
[SerializeField] private TextMeshProUGUI replayLabel;
|
|
17
18
|
[SerializeField] private RawImage playerAvatar;
|
|
18
19
|
[SerializeField] private Button pauseButton;
|
|
19
20
|
[SerializeField] private Button playbackSpeedButton;
|
|
20
21
|
[SerializeField] private TextMeshProUGUI playbackSpeedLabel;
|
|
21
|
-
[SerializeField] private float[] playbackSpeeds = { 1.5f, 2f,
|
|
22
|
-
[SerializeField, Min(1f)] private float seekPlaybackSpeed =
|
|
22
|
+
[SerializeField] private float[] playbackSpeeds = { 1f, 1.5f, 2f, 0.5f };
|
|
23
|
+
[SerializeField, Min(1f)] private float seekPlaybackSpeed = 15f;
|
|
24
|
+
[SerializeField, Min(0f)] private float readOnlyProgressSmoothTime = 0.08f;
|
|
23
25
|
[SerializeField, Range(0.25f, 1f)] private float seekSnapshotScale = 0.55f;
|
|
26
|
+
[SerializeField, Range(0f, 0.5f)] private float seekPreparationProgress = 0.2f;
|
|
27
|
+
[SerializeField, Range(0f, 0.15f)] private float seekPreparationProgressVariation = 0.05f;
|
|
28
|
+
[SerializeField, Min(0.1f)] private float seekPreparationDuration = 3f;
|
|
24
29
|
[SerializeField] private GameObject seekLoadingOverlay;
|
|
25
30
|
[SerializeField] private RawImage seekSnapshotImage;
|
|
26
31
|
|
|
27
32
|
private int playbackSpeedIndex;
|
|
33
|
+
private Slider progressSlider;
|
|
28
34
|
private ReplaySeekInput seekInput;
|
|
35
|
+
private bool seekingAllowed;
|
|
29
36
|
private bool wasSeeking;
|
|
30
37
|
private bool isCapturingSeek;
|
|
31
38
|
private float pendingSeekTarget;
|
|
39
|
+
private float displayedReadOnlyProgress;
|
|
40
|
+
private float readOnlyProgressVelocity;
|
|
32
41
|
private Texture2D seekSnapshotTexture;
|
|
33
42
|
private Image seekOverlayBackground;
|
|
34
43
|
private Color seekOverlayBackgroundColor;
|
|
44
|
+
private ReplaySeekOverlay seekOverlay;
|
|
45
|
+
private static readonly System.Random SeekProgressRandom = new System.Random();
|
|
46
|
+
private static float displayedSeekProgress;
|
|
47
|
+
private static float firstSeekPreparationTarget;
|
|
48
|
+
private static float secondSeekPreparationTarget;
|
|
49
|
+
private static float seekPreparationCeiling;
|
|
35
50
|
|
|
36
51
|
private void Awake()
|
|
37
52
|
{
|
|
53
|
+
seekingAllowed = HyperRuntime.Settings != null && HyperRuntime.Settings.allowReplaySeeking;
|
|
38
54
|
AutoBindReferences();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
55
|
+
ConfigureProgressSliders();
|
|
56
|
+
if (seekingAllowed)
|
|
57
|
+
{
|
|
58
|
+
ConfigureSeekOverlay();
|
|
59
|
+
ConfigureSeekSnapshot();
|
|
60
|
+
}
|
|
61
|
+
else if (seekLoadingOverlay != null)
|
|
62
|
+
{
|
|
63
|
+
seekLoadingOverlay.SetActive(false);
|
|
64
|
+
}
|
|
65
|
+
|
|
42
66
|
ApplyInitialPlaybackSpeed();
|
|
43
|
-
ReplaySessionState.SeekCompleted += RestorePlaybackAfterSeek;
|
|
67
|
+
if (seekingAllowed) ReplaySessionState.SeekCompleted += RestorePlaybackAfterSeek;
|
|
44
68
|
if (pauseButton != null) pauseButton.onClick.AddListener(PauseReplay);
|
|
45
69
|
if (playbackSpeedButton != null) playbackSpeedButton.onClick.AddListener(CyclePlaybackSpeed);
|
|
46
70
|
}
|
|
@@ -52,13 +76,13 @@ namespace Hyper.Internal
|
|
|
52
76
|
if (replayLabel != null) replayLabel.text = $"Replay of @{playerName}";
|
|
53
77
|
if (progressSlider != null)
|
|
54
78
|
{
|
|
55
|
-
float displayedProgress = ReplaySessionState.IsSeeking
|
|
79
|
+
float displayedProgress = seekingAllowed && ReplaySessionState.IsSeeking
|
|
56
80
|
? ReplaySessionState.SeekTarget01
|
|
57
81
|
: ReplaySessionState.Progress01;
|
|
58
|
-
|
|
82
|
+
SetProgressSliderValue(displayedProgress);
|
|
59
83
|
}
|
|
60
84
|
UpdatePlaybackSpeedLabel(ReplaySessionState.UserPlaybackSpeed);
|
|
61
|
-
SetSeekingPresentation(ReplaySessionState.IsSeeking);
|
|
85
|
+
SetSeekingPresentation(seekingAllowed && ReplaySessionState.IsSeeking);
|
|
62
86
|
|
|
63
87
|
if (playerAvatar != null && ReplaySessionState.AvatarTexture != null)
|
|
64
88
|
{
|
|
@@ -68,7 +92,7 @@ namespace Hyper.Internal
|
|
|
68
92
|
|
|
69
93
|
private void Update()
|
|
70
94
|
{
|
|
71
|
-
bool isSeeking = ReplaySessionState.IsSeeking;
|
|
95
|
+
bool isSeeking = seekingAllowed && ReplaySessionState.IsSeeking;
|
|
72
96
|
if (wasSeeking && !isSeeking && !ReplaySessionState.HasEnded)
|
|
73
97
|
{
|
|
74
98
|
RestorePlaybackAfterSeek(ReplaySessionState.PlaybackSpeed);
|
|
@@ -79,6 +103,11 @@ namespace Hyper.Internal
|
|
|
79
103
|
SetSeekingPresentation(isSeeking);
|
|
80
104
|
}
|
|
81
105
|
|
|
106
|
+
if (isSeeking)
|
|
107
|
+
{
|
|
108
|
+
UpdateSeekProgress(true);
|
|
109
|
+
}
|
|
110
|
+
|
|
82
111
|
if (progressSlider != null && (seekInput == null || !seekInput.IsDragging))
|
|
83
112
|
{
|
|
84
113
|
float displayedProgress = isCapturingSeek
|
|
@@ -86,7 +115,7 @@ namespace Hyper.Internal
|
|
|
86
115
|
: isSeeking
|
|
87
116
|
? ReplaySessionState.SeekTarget01
|
|
88
117
|
: ReplaySessionState.Progress01;
|
|
89
|
-
|
|
118
|
+
SetProgressSliderValue(displayedProgress, !seekingAllowed);
|
|
90
119
|
}
|
|
91
120
|
|
|
92
121
|
wasSeeking = isSeeking;
|
|
@@ -129,7 +158,7 @@ namespace Hyper.Internal
|
|
|
129
158
|
if (progressSlider != null) progressSlider.interactable = false;
|
|
130
159
|
if (seekLoadingOverlay != null) seekLoadingOverlay.SetActive(false);
|
|
131
160
|
ClearSeekSnapshot();
|
|
132
|
-
|
|
161
|
+
SetProgressSliderValue(1f);
|
|
133
162
|
}
|
|
134
163
|
|
|
135
164
|
internal void ResetForReplayRestart()
|
|
@@ -148,10 +177,7 @@ namespace Hyper.Internal
|
|
|
148
177
|
: 0f;
|
|
149
178
|
wasSeeking = ReplaySessionState.IsSeeking;
|
|
150
179
|
|
|
151
|
-
|
|
152
|
-
{
|
|
153
|
-
progressSlider.SetValueWithoutNotify(pendingSeekTarget);
|
|
154
|
-
}
|
|
180
|
+
SetProgressSliderValue(pendingSeekTarget);
|
|
155
181
|
|
|
156
182
|
UpdatePlaybackSpeedLabel(playbackSpeed);
|
|
157
183
|
AlignPlaybackSpeedIndex(playbackSpeed);
|
|
@@ -181,7 +207,7 @@ namespace Hyper.Internal
|
|
|
181
207
|
|
|
182
208
|
private void SeekTo(float targetProgress)
|
|
183
209
|
{
|
|
184
|
-
if (!ReplaySessionState.IsActive || ReplaySessionState.HasEnded ||
|
|
210
|
+
if (!seekingAllowed || !ReplaySessionState.IsActive || ReplaySessionState.HasEnded ||
|
|
185
211
|
ReplaySessionState.IsSeeking || isCapturingSeek)
|
|
186
212
|
{
|
|
187
213
|
return;
|
|
@@ -189,6 +215,7 @@ namespace Hyper.Internal
|
|
|
189
215
|
|
|
190
216
|
float resumeSpeed = SanitizePlaybackSpeed(ReplaySessionState.UserPlaybackSpeed);
|
|
191
217
|
float fastForwardSpeed = SanitizePlaybackSpeed(seekPlaybackSpeed);
|
|
218
|
+
InitializeSeekProgressPresentation();
|
|
192
219
|
StartCoroutine(CaptureAndSeek(targetProgress, resumeSpeed, fastForwardSpeed));
|
|
193
220
|
}
|
|
194
221
|
|
|
@@ -238,7 +265,6 @@ namespace Hyper.Internal
|
|
|
238
265
|
private void RestorePlaybackAfterSeek(float playbackSpeed)
|
|
239
266
|
{
|
|
240
267
|
float speed = SanitizePlaybackSpeed(playbackSpeed);
|
|
241
|
-
Time.timeScale = speed;
|
|
242
268
|
AlignPlaybackSpeedIndex(speed);
|
|
243
269
|
UpdatePlaybackSpeedLabel(speed);
|
|
244
270
|
SetSeekingPresentation(false);
|
|
@@ -259,9 +285,14 @@ namespace Hyper.Internal
|
|
|
259
285
|
|
|
260
286
|
private void SetSeekingPresentation(bool isSeeking)
|
|
261
287
|
{
|
|
288
|
+
isSeeking &= seekingAllowed;
|
|
262
289
|
ReplayPresentationCanvas.SetSeekingLayer(isSeeking);
|
|
290
|
+
UpdateSeekProgress(isSeeking);
|
|
263
291
|
if (seekLoadingOverlay != null) seekLoadingOverlay.SetActive(isSeeking);
|
|
264
|
-
if (progressSlider != null)
|
|
292
|
+
if (progressSlider != null)
|
|
293
|
+
{
|
|
294
|
+
progressSlider.interactable = seekingAllowed && !isSeeking && !ReplaySessionState.HasEnded;
|
|
295
|
+
}
|
|
265
296
|
if (playbackSpeedButton != null)
|
|
266
297
|
{
|
|
267
298
|
playbackSpeedButton.interactable = !isSeeking && !ReplaySessionState.HasEnded;
|
|
@@ -285,7 +316,7 @@ namespace Hyper.Internal
|
|
|
285
316
|
|
|
286
317
|
if (playbackSpeedLabel != null)
|
|
287
318
|
{
|
|
288
|
-
playbackSpeedLabel.text = $"{speed:0
|
|
319
|
+
playbackSpeedLabel.text = $"{speed:0.##}x";
|
|
289
320
|
}
|
|
290
321
|
}
|
|
291
322
|
|
|
@@ -339,8 +370,6 @@ namespace Hyper.Internal
|
|
|
339
370
|
|
|
340
371
|
private void AutoBindReferences()
|
|
341
372
|
{
|
|
342
|
-
if (progressSlider == null) progressSlider = GetComponentInChildren<Slider>(true);
|
|
343
|
-
|
|
344
373
|
Button[] buttons = GetComponentsInChildren<Button>(true);
|
|
345
374
|
for (int i = 0; i < buttons.Length; i++)
|
|
346
375
|
{
|
|
@@ -360,24 +389,69 @@ namespace Hyper.Internal
|
|
|
360
389
|
}
|
|
361
390
|
}
|
|
362
391
|
|
|
363
|
-
private void
|
|
392
|
+
private void ConfigureProgressSliders()
|
|
364
393
|
{
|
|
394
|
+
progressSlider = seekingAllowed ? seekableSlider : readOnlySlider;
|
|
395
|
+
|
|
396
|
+
if (seekableSlider != null)
|
|
397
|
+
{
|
|
398
|
+
seekableSlider.gameObject.SetActive(seekingAllowed);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (readOnlySlider != null)
|
|
402
|
+
{
|
|
403
|
+
readOnlySlider.gameObject.SetActive(!seekingAllowed);
|
|
404
|
+
}
|
|
405
|
+
|
|
365
406
|
if (progressSlider == null) return;
|
|
366
407
|
|
|
367
408
|
progressSlider.minValue = 0f;
|
|
368
409
|
progressSlider.maxValue = 1f;
|
|
369
410
|
progressSlider.wholeNumbers = false;
|
|
370
|
-
progressSlider.interactable =
|
|
371
|
-
float displayedProgress = ReplaySessionState.IsSeeking
|
|
411
|
+
progressSlider.interactable = seekingAllowed;
|
|
412
|
+
float displayedProgress = seekingAllowed && ReplaySessionState.IsSeeking
|
|
372
413
|
? ReplaySessionState.SeekTarget01
|
|
373
414
|
: ReplaySessionState.Progress01;
|
|
374
|
-
|
|
415
|
+
SetProgressSliderValue(displayedProgress);
|
|
416
|
+
|
|
417
|
+
if (!seekingAllowed) return;
|
|
375
418
|
|
|
376
419
|
seekInput = progressSlider.GetComponent<ReplaySeekInput>();
|
|
377
420
|
if (seekInput == null) seekInput = progressSlider.gameObject.AddComponent<ReplaySeekInput>();
|
|
378
421
|
seekInput.Initialize(progressSlider, SeekTo);
|
|
379
422
|
}
|
|
380
423
|
|
|
424
|
+
private void SetProgressSliderValue(float targetProgress, bool smoothReadOnly = false)
|
|
425
|
+
{
|
|
426
|
+
if (progressSlider == null) return;
|
|
427
|
+
|
|
428
|
+
targetProgress = Mathf.Clamp01(targetProgress);
|
|
429
|
+
if (!smoothReadOnly || readOnlyProgressSmoothTime <= 0f ||
|
|
430
|
+
targetProgress < displayedReadOnlyProgress)
|
|
431
|
+
{
|
|
432
|
+
displayedReadOnlyProgress = targetProgress;
|
|
433
|
+
readOnlyProgressVelocity = 0f;
|
|
434
|
+
}
|
|
435
|
+
else
|
|
436
|
+
{
|
|
437
|
+
displayedReadOnlyProgress = Mathf.SmoothDamp(
|
|
438
|
+
displayedReadOnlyProgress,
|
|
439
|
+
targetProgress,
|
|
440
|
+
ref readOnlyProgressVelocity,
|
|
441
|
+
readOnlyProgressSmoothTime,
|
|
442
|
+
Mathf.Infinity,
|
|
443
|
+
Time.unscaledDeltaTime);
|
|
444
|
+
|
|
445
|
+
if (Mathf.Abs(targetProgress - displayedReadOnlyProgress) < 0.0001f)
|
|
446
|
+
{
|
|
447
|
+
displayedReadOnlyProgress = targetProgress;
|
|
448
|
+
readOnlyProgressVelocity = 0f;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
progressSlider.SetValueWithoutNotify(displayedReadOnlyProgress);
|
|
453
|
+
}
|
|
454
|
+
|
|
381
455
|
private void ConfigureSeekOverlay()
|
|
382
456
|
{
|
|
383
457
|
if (seekLoadingOverlay == null)
|
|
@@ -404,10 +478,114 @@ namespace Hyper.Internal
|
|
|
404
478
|
seekLoadingOverlay.transform.SetAsFirstSibling();
|
|
405
479
|
seekOverlayBackground = seekLoadingOverlay.GetComponent<Image>();
|
|
406
480
|
if (seekOverlayBackground != null) seekOverlayBackgroundColor = seekOverlayBackground.color;
|
|
481
|
+
seekOverlay = seekLoadingOverlay.GetComponent<ReplaySeekOverlay>();
|
|
407
482
|
seekLoadingOverlay.SetActive(ReplaySessionState.IsSeeking);
|
|
408
483
|
wasSeeking = ReplaySessionState.IsSeeking;
|
|
409
484
|
}
|
|
410
485
|
|
|
486
|
+
private void UpdateSeekProgress(bool isSeeking)
|
|
487
|
+
{
|
|
488
|
+
if (seekOverlay == null) return;
|
|
489
|
+
|
|
490
|
+
float progress;
|
|
491
|
+
if (!isSeeking)
|
|
492
|
+
{
|
|
493
|
+
progress = 1f;
|
|
494
|
+
}
|
|
495
|
+
else if (isCapturingSeek || ReplaySessionState.IsSeekRestartPending)
|
|
496
|
+
{
|
|
497
|
+
AdvanceSeekPreparationProgress();
|
|
498
|
+
progress = displayedSeekProgress;
|
|
499
|
+
}
|
|
500
|
+
else
|
|
501
|
+
{
|
|
502
|
+
EnsureSeekProgressPresentation();
|
|
503
|
+
float target = ReplaySessionState.SeekTarget01;
|
|
504
|
+
float reconstructionProgress = target <= Mathf.Epsilon
|
|
505
|
+
? 1f
|
|
506
|
+
: ReplaySessionState.Progress01 / target;
|
|
507
|
+
float realProgress = Mathf.Lerp(
|
|
508
|
+
seekPreparationCeiling,
|
|
509
|
+
1f,
|
|
510
|
+
Mathf.Clamp01(reconstructionProgress));
|
|
511
|
+
AdvanceSeekReconstructionProgress(realProgress);
|
|
512
|
+
progress = displayedSeekProgress;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
seekOverlay.SetProgress(progress);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
private void AdvanceSeekPreparationProgress()
|
|
519
|
+
{
|
|
520
|
+
EnsureSeekProgressPresentation();
|
|
521
|
+
float duration = Mathf.Max(0.1f, seekPreparationDuration);
|
|
522
|
+
float deltaTime = Mathf.Min(Time.unscaledDeltaTime, 0.1f);
|
|
523
|
+
|
|
524
|
+
if (displayedSeekProgress < firstSeekPreparationTarget)
|
|
525
|
+
{
|
|
526
|
+
float speed = firstSeekPreparationTarget / (duration * 0.35f);
|
|
527
|
+
displayedSeekProgress = Mathf.MoveTowards(
|
|
528
|
+
displayedSeekProgress,
|
|
529
|
+
firstSeekPreparationTarget,
|
|
530
|
+
speed * deltaTime);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (displayedSeekProgress < secondSeekPreparationTarget)
|
|
535
|
+
{
|
|
536
|
+
float speed = (secondSeekPreparationTarget - firstSeekPreparationTarget) / (duration * 0.2f);
|
|
537
|
+
displayedSeekProgress = Mathf.MoveTowards(
|
|
538
|
+
displayedSeekProgress,
|
|
539
|
+
secondSeekPreparationTarget,
|
|
540
|
+
speed * deltaTime);
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
float crawlResponse = 1f - Mathf.Exp(-deltaTime / (duration * 0.65f));
|
|
545
|
+
displayedSeekProgress = Mathf.Lerp(displayedSeekProgress, seekPreparationCeiling, crawlResponse);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
private void InitializeSeekProgressPresentation()
|
|
549
|
+
{
|
|
550
|
+
float center = Mathf.Clamp01(seekPreparationProgress);
|
|
551
|
+
float variation = Mathf.Max(0f, seekPreparationProgressVariation);
|
|
552
|
+
float minimum = Mathf.Clamp01(center - variation);
|
|
553
|
+
float maximum = Mathf.Clamp01(center + variation);
|
|
554
|
+
|
|
555
|
+
displayedSeekProgress = 0f;
|
|
556
|
+
seekPreparationCeiling = RandomRange(minimum, maximum);
|
|
557
|
+
firstSeekPreparationTarget = seekPreparationCeiling * RandomRange(0.25f, 0.35f);
|
|
558
|
+
secondSeekPreparationTarget = seekPreparationCeiling * RandomRange(0.62f, 0.76f);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private void EnsureSeekProgressPresentation()
|
|
562
|
+
{
|
|
563
|
+
if (seekPreparationCeiling > 0f) return;
|
|
564
|
+
InitializeSeekProgressPresentation();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private static float RandomRange(float minimum, float maximum)
|
|
568
|
+
{
|
|
569
|
+
if (maximum <= minimum) return minimum;
|
|
570
|
+
return minimum + ((float)SeekProgressRandom.NextDouble() * (maximum - minimum));
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
private void AdvanceSeekReconstructionProgress(float realProgress)
|
|
574
|
+
{
|
|
575
|
+
float target = Mathf.Max(displayedSeekProgress, realProgress);
|
|
576
|
+
if (target >= 0.999f)
|
|
577
|
+
{
|
|
578
|
+
displayedSeekProgress = 1f;
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
float deltaTime = Mathf.Min(Time.unscaledDeltaTime, 0.1f);
|
|
583
|
+
float gap = target - displayedSeekProgress;
|
|
584
|
+
float responseSpeed = Mathf.Lerp(5f, 14f, Mathf.Clamp01(gap * 4f));
|
|
585
|
+
float response = 1f - Mathf.Exp(-responseSpeed * deltaTime);
|
|
586
|
+
displayedSeekProgress = Mathf.Lerp(displayedSeekProgress, target, response);
|
|
587
|
+
}
|
|
588
|
+
|
|
411
589
|
private void ConfigureSeekSnapshot()
|
|
412
590
|
{
|
|
413
591
|
if (seekSnapshotImage == null)
|
|
@@ -110,7 +110,7 @@ namespace Hyper.Internal
|
|
|
110
110
|
CanvasScaler scaler = GetComponent<CanvasScaler>();
|
|
111
111
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
112
112
|
scaler.referenceResolution = new Vector2(1080f, 2160f);
|
|
113
|
-
scaler.matchWidthOrHeight = isPortrait ? 1f : 0.593f;
|
|
113
|
+
scaler.matchWidthOrHeight = isPortrait ? 1f : (PlayerDeviceClassification.PlayerDeviceIsPC ? 0.75f : 0.593f);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
private void EnsurePresentationObjects()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
using TMPro;
|
|
2
|
+
using UnityEngine;
|
|
3
|
+
|
|
4
|
+
namespace Hyper.Internal
|
|
5
|
+
{
|
|
6
|
+
internal sealed class ReplaySeekOverlay : MonoBehaviour
|
|
7
|
+
{
|
|
8
|
+
[SerializeField] private TextMeshProUGUI progressText;
|
|
9
|
+
|
|
10
|
+
internal void SetProgress(float progress01)
|
|
11
|
+
{
|
|
12
|
+
if (progressText == null) return;
|
|
13
|
+
|
|
14
|
+
int percentage = Mathf.FloorToInt(Mathf.Clamp01(progress01) * 10f) * 10;
|
|
15
|
+
progressText.text = $"{percentage}%";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
using System.Globalization;
|
|
1
2
|
using TMPro;
|
|
2
3
|
using UnityEngine;
|
|
3
4
|
using UnityEngine.UI;
|
|
@@ -38,7 +39,7 @@ namespace Hyper.Internal
|
|
|
38
39
|
|
|
39
40
|
if (scoreValue != null)
|
|
40
41
|
{
|
|
41
|
-
scoreValue.text = (
|
|
42
|
+
scoreValue.text = FormatFinalScore(ReplaySessionState.FinalScore);
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
if (scoreOwnerName != null)
|
|
@@ -99,6 +100,13 @@ namespace Hyper.Internal
|
|
|
99
100
|
: $"@{normalizedName.ToUpperInvariant()}'S SCORE";
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
internal static string FormatFinalScore(int? finalScore)
|
|
104
|
+
{
|
|
105
|
+
return finalScore.HasValue
|
|
106
|
+
? finalScore.Value.ToString("N0", CultureInfo.InvariantCulture)
|
|
107
|
+
: "--";
|
|
108
|
+
}
|
|
109
|
+
|
|
102
110
|
private void UpdateSoundPresentation()
|
|
103
111
|
{
|
|
104
112
|
bool isSoundOn = HyperRuntime.DataManager == null || HyperRuntime.DataManager.GetSound() == 1;
|
|
@@ -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 = 1.02f;
|
|
18
18
|
private GameManager _gameManager;
|
|
19
19
|
private StartCTA _currentCTA;
|
|
20
20
|
private SwipeDirection _swipeDirection;
|
|
@@ -16,13 +16,13 @@ namespace Hyper.Internal
|
|
|
16
16
|
|
|
17
17
|
private void OnEnable()
|
|
18
18
|
{
|
|
19
|
-
//
|
|
19
|
+
// Show Game Over after one real-time second.
|
|
20
20
|
StartCoroutine(GameOver());
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
IEnumerator GameOver()
|
|
24
24
|
{
|
|
25
|
-
yield return new WaitForSecondsRealtime(
|
|
25
|
+
yield return new WaitForSecondsRealtime(1f);
|
|
26
26
|
gameObject.SetActive(false);
|
|
27
27
|
|
|
28
28
|
_gameManager.EndGame("Hyper_Time_Up");
|