My approach is setting Time.timeScale to 0 in the Trigger handler. This will pause the game so no input can be entered and no additional collisions can take place. Like so:
// <In CrashDetector>
void OnCollisionEnter2D(Collision2D other) {
if (other.gameObject.tag == "Obstacle") {
Crash();
}
}
private void Crash() {
....
// Play the crash effect
crashEffect.Play();
StartCoroutine(PauseAfterEffect());
// Reload the scene after a delay
StartCoroutine(ReloadSceneAfterDelay());
}
IEnumerator PauseAfterEffect() {
yield return new WaitForSeconds(Mathf.Max(crashEffect.main.duration, crashSound.clip.length));
Time.timeScale = 0f;
}
IEnumerator ReloadSceneAfterDelay() {
// Use unscaledTime since the scene is paused
yield return new WaitForSecondsRealtime(reloadDelay);
// Reset timeScale before loading new scene
Time.timeScale = 1f;
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
Some notes:
- I make sure to only pause once the crash effect and crash sound have finished, for better experience. Technically the player can still move during this short interval
- In order to reload the scene properly, we have to use
WaitForSecondsRealtime
once the scene is paused sinceWaitForSeconds
uses “game time” and once we settimeScale = 0
game time does not advance. I think we also need to reset the time scale back to 1 in order for the scene to reload. I haven’t tested whether this approach would work withInvoke
instead of a coroutine but I suspect it wouldn’t.