Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How can I add a few seconds before my script loads a new level?

Discussion in '2D' started by Cobble, Nov 8, 2016.

  1. Cobble

    Cobble

    Joined:
    May 9, 2015
    Posts:
    33
    Hi! I'm a bit new to coding. I made this script in which a new level is loaded once the player talks to an NPC, but the level is loaded right away, so the player can't read the dialogue. Is there any way I can put a few seconds between the time the dialogue loads and the new level loads? I appreciate any help. :)

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.SceneManagement;
    4.  
    5. public class DialogueLoad : MonoBehaviour {
    6.  
    7.  public string dialogue;
    8.  private DialogueMan dMan;
    9.  
    10.  public string[] dialogueLines;
    11.  
    12.  public string loadLevel;
    13.  
    14.  //Usethisfor initialization
    15.  void Start () {
    16.  dMan = FindObjectOfType<DialogueMan> ();
    17.  }
    18.  
    19.  //Updateiscalledonceper frame
    20.  void Update () {
    21.  
    22.  }
    23.  
    24.  void OnTriggerStay2D(Collider2D other)
    25.  {
    26.  if (other.gameObject.name == "Scarlet")
    27.  {
    28.  if (Input.GetKeyUp (KeyCode.Space))
    29.  {
    30.  //dMan.ShowBox(dialogue);
    31.  
    32.  if (!dMan.dialogueActive)
    33.  {
    34.  dMan.dialogueLines = dialogueLines;
    35.  dMan.currentLine = 0;
    36.  dMan.ShowDialogue();
    37.  SceneManager.LoadScene(loadLevel);
    38.  }
    39.  }
    40.  }
    41.  }
    42. }
     
  2. Hyblademin

    Hyblademin

    Joined:
    Oct 14, 2013
    Posts:
    725
    There are a couple ways you can add a delay. One popular one is to use a coroutine, which would be very simple to do in your case.
     
  3. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    the other way is to have a timer(with an event connected to it) ish, not really a timer in the .net sense.
    The following class it what I use to pause before I load my next level.

    Code (CSharp):
    1. public class TimerComponent : MonoBehaviour
    2. {
    3. private DateTime mTime;
    4. public delegate void TimeEventHandler(object sender, TimeEventArgs e);
    5. public event TimeEventHandler TimeElapsed;
    6.  
    7. protected virtual void OnTimeElapsed(TimeEventArgs e)
    8. {
    9.   if (TimeElapsed != null)
    10.    TimeElapsed(this, e);
    11. }
    12.  
    13. public void Awake()
    14. {
    15.   mTime = DateTime.MinValue;
    16. }
    17.  
    18. void Update()
    19. {
    20.   if (mTime != DateTime.MinValue)
    21.    if (DateTime.Now > mTime)
    22.     OnTimeElapsed(new TimeEventArgs(DateTime.Now));
    23. }
    24.  
    25. public DateTime Time
    26. {
    27.   get { return mTime; }
    28.   set { mTime = value; }
    29. }
    30. }
    31.  
    You will need to hock up the TimeElapsed event for the callback.

    The event will not fire until you set Time with the datetime you want the event to fire usually something like

    DateTime.Now.Addseconds(5);
     
  4. RiotDX

    RiotDX

    Joined:
    May 3, 2015
    Posts:
    4
    I would definitely recommend using a coroutine for this, as it is much more efficient to keep as much code out of the Update function as possible. Something as simple as this should cover it:
    Code (CSharp):
    1.     IEnumerator LoadNextScene(){
    2.         yield return new WaitForSeconds (5);
    3.         SceneManager.LoadScene(loadLevel);
    4.         yield return null;
    5.     }
    And then call it by replacing your current SceneManager call with this:
    Code (CSharp):
    1. StartCoroutine("LoadNextScene");
     
  5. piggybank1974

    piggybank1974

    Joined:
    Dec 15, 2015
    Posts:
    621
    I only use one scene for game play, and it's not in the update function anyways, it depends on how your code is written, coroutines are not the answer to every solution, but in this case they both work.

    It's what best fits your needs, having an event system as I do, you need to know how to use it properly.
     
  6. Hyblademin

    Hyblademin

    Joined:
    Oct 14, 2013
    Posts:
    725
    While I concur with your solution, I really don't think this statement is so straightforward. It depends on what kind of efficiency we're talking about, though.

    In terms of overhead, the player still has to run the code in the coroutine before the frame can end, and when there is a waitForSeconds, some timer loop process is still being run to check whether or not to resume the coroutine. In addition, using coroutines often necessitates additional logic in Update() (or whatever calling function) to prevent more instances of the coroutine from being triggered.

    In this specific case, though, since it's so simple to write the coroutine, the simplicity of the script and the typing time saved are definitely worth it.
     
  7. Cobble

    Cobble

    Joined:
    May 9, 2015
    Posts:
    33
    I found out how to do it on my own, but thanks for the help!

    Here's how I did it.

    I made a separate script for dialogue that loads a new area after you talk to the NPC, and I changed the code a bit.

    I changed the last "void" to "IEnumerator"

    I also added yield return new WaitForSeconds(10); under it, between the lines of code where I wanted it to wait.

    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.SceneManagement;
    4.  
    5. public class DialogueLoad : MonoBehaviour {
    6.  
    7.  public string dialogue;
    8.  private DialogueMan dMan;
    9.  
    10.  public string[] dialogueLines;
    11.  
    12.  public string loadLevel;
    13.  
    14.  
    15.  //Usethisfor initialization
    16.  void Start () {
    17.  dMan = FindObjectOfType<DialogueMan> ();
    18.  }
    19.  
    20.  //Updateiscalledonceper frame
    21.  void Update () {
    22.  
    23.  }
    24.  
    25.  IEnumerator OnTriggerStay2D(Collider2D other)
    26.  {
    27.  if (other.gameObject.name == "Scarlet")
    28.  {
    29.  if (Input.GetKeyUp (KeyCode.Space))
    30.  {
    31.  //dMan.ShowBox(dialogue);
    32.  
    33.  if (!dMan.dialogueActive)
    34.  {
    35.  dMan.dialogueLines = dialogueLines;
    36.  dMan.currentLine = 0;
    37.  dMan.ShowDialogue();
    38.  yield return new WaitForSeconds(10);
    39.  SceneManager.LoadScene(loadLevel);
    40.  }
    41.  }
    42.  }
    43.  }
    44. }
     
  8. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,802
    You don't want to be using OnTriggerStay as an IEnumerator. Every single physics step it will try and start that coroutine while the colliders overlap.

    Could that instead be OnTriggerEnter?

    I'd also like to point out that it isn't reliable that Input.GetKeyUp will be true at the time of the physics step. You should get those inputs in Update and store it in a variable, and then check that in the event.

    You may want to do something like this instead:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.SceneManagement;
    4.  
    5. public class DialogueLoad : MonoBehaviour {
    6.  
    7.     public string dialogue;
    8.     private DialogueMan dMan;
    9.  
    10.     public string[] dialogueLines;
    11.  
    12.     public string loadLevel;
    13.  
    14.     private bool dialogTriggered;
    15.  
    16.  
    17.     //Usethisfor initialization
    18.     void Start() {
    19.         dMan = FindObjectOfType<DialogueMan>();
    20.     }
    21.  
    22.     //Updateiscalledonceper frame
    23.     void Update() {
    24.         if(dialogTriggered) {
    25.             dialogTriggered = false;
    26.             StartCoroutine(startDialog());
    27.         }
    28.     }
    29.  
    30.     private IEnumerator startDialog() {
    31.         //dMan.ShowBox(dialogue);
    32.  
    33.         if(!dMan.dialogueActive) {
    34.             dMan.dialogueLines = dialogueLines;
    35.             dMan.currentLine = 0;
    36.             dMan.ShowDialogue();
    37.             yield return new WaitForSeconds(10);
    38.             SceneManager.LoadScene(loadLevel);
    39.         }
    40.     }
    41.  
    42.     void OnTriggerEnter2D(Collider2D other) {
    43.         if(other.gameObject.name == "Scarlet") {
    44.             dialogTriggered = true;
    45.         }
    46.     }
    47. }
     
    Last edited: Nov 10, 2016