0
\$\begingroup\$

I seem to be misunderstanding part of Unity. I'm generating a maze similar to the one here: http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/

I put the whole of the generation in Update() and used a bool to ensure it only runs once. However, right now the whole thing runs before the editor becomes available - it behaves exactly as if I had the whole thing in a Start() section. Do I need to use coroutines to watch it build out? In the link above, if you click the first demo box, you can watch the maze place itself and build. I'd like to be able to see that in the scene view in Unity. Thoughts?

Edit:

This is my Update() method currently:

void Update()
{
    while (_continue)
    {
      _continue = false;
      Dictionary<String, Tile> _dict = FetchTileList();

      Tile _wall = _dict["Wall"];

      for (int x = 0; x < width; x++)
      {
        for (int y = 0; y < height; y++)
        {
            var _go = Instantiate(_wall.gameObject, new Vector3(x, y, 0), Quaternion.identity);
        }
      }

      _addRooms();

      for (var y = 1; y < height; y += 2) {
        for (var x = 1; x < width; x += 2) {
            Vector2 pos = new Vector2(x, y);
            Rect _rect = new Rect(x, y, 1, 1);
            bool _add = true;

            foreach (var other in _rooms)
            {
                if (_rect.Overlaps(other))
                {
                    _add = false;
                }
            }

            if (_add)
            {
                _growMaze(pos);
            }
        }
      }
   }
}
\$\endgroup\$
  • \$\begingroup\$ Please post your Update() method. \$\endgroup\$ – SurvivalMachine Sep 8 '16 at 12:06
  • 1
    \$\begingroup\$ You are looping and placing everything in one shot in an Update() frame. What result would you expect to happen here? Your Draw frame is just going to draw all the items in one shot, since you already placed them all in your first iteration. \$\endgroup\$ – jgallant Sep 8 '16 at 12:58
  • \$\begingroup\$ I didn't realize that rendering would only take place after a complete Update(), regardless of timing. I expected that rendering would occur at a fixed time, regardless of update. But that still isn't a solution. Would calling coroutines that performed the work resolve the issue, or will update not continue until the coroutine is finished? Should I call each loop once per update until the loop has completed? \$\endgroup\$ – Jesse Williams Sep 8 '16 at 13:15
3
\$\begingroup\$

A MonoBehaviour's Update() method runs once, to completion, before each game frame is rendered. So if you change things in the middle of your Update method, you won't see that change until the Update method finishes (by reaching the closing brace at the end or a return statement), giving the engine a chance to continue with the rest of its work for the frame, rendering, and displaying the results.

In this case, that means the first frame isn't rendered until your Update method has finished all of its work and the maze is already complete.

To spread execution across multiple frames, the usual method is, as you say, a Coroutine. Then we can use yield statements to pass control back to the engine periodically to render a new frame, and resume generation exactly where we left off sometime later.

Here's how we might rewrite your maze generation to use this feature:

(I've omitted the while(_continue) section since I couldn't see anywhere in your code that _continue is set back to true to make it loop a second time. Please include that code if it's needed)

void Start() {
    StartCoroutine(GenerateMaze());
}

IEnumerator GenerateMaze() {
    Dictionary<String, Tile> _dict = FetchTileList();

    Tile _wall = _dict["Wall"];

    for (int x = 0; x < width; x++)
    {
        for (int y = 0; y < height; y++)
        {
            // Wait one frame before adding each wall 
            // (so we see an empty maze on the very first frame)
            yield return null;

            var _go = Instantiate(_wall.gameObject, new Vector3(x, y, 0), Quaternion.identity);
        }
    }

    // Wait one frame before adding rooms.
    // You may also want to make _addRooms a coroutine if you want to see it
    // perform its steps one by one. ie. yield return StartCoroutine(_addRooms());
    yield return null;
    _addRooms();

    for (var y = 1; y < height; y += 2) {
        for (var x = 1; x < width; x += 2) {
            Vector2 pos = new Vector2(x, y);
            Rect _rect = new Rect(x, y, 1, 1);
            bool _add = true;

            foreach (var other in _rooms)
            {
                if (_rect.Overlaps(other))
                {
                    _add = false;
                }
            }

            if (_add)
            {
                // Wait one frame before growing the maze.
                // Again, if this involves multiple steps, you might want to
                // make _growMaze a coroutine itself.
                yield return null;

                _growMaze(pos);
            }
        }
    }
}

Instead of yield return null which waits exactly one frame, you can also use yield return new WaitForSeconds(0.1f); if you want the generation speed to run slower, or be more consistent across devices running at different framerates.

See this answer - especially the list of Coroutine return values at the bottom - for more on ways you can control when your code runs.

\$\endgroup\$
  • \$\begingroup\$ The _continue flag was simply to prevent it from running more than once. I would have to move to something similar, I think, to prevent the individual coroutines from executing more than once. Thank you! \$\endgroup\$ – Jesse Williams Sep 8 '16 at 15:16
  • \$\begingroup\$ No, when you StartCoroutine() in Start() as shown here, it will run only once. You don't need the while (and you shouldn't use while where you really mean if — it makes the intent of the code less legible, suggesting there's a loop where there really isn't one) \$\endgroup\$ – DMGregory Sep 8 '16 at 15:29
  • \$\begingroup\$ Ah well, yeah of course Start() only runs once. As for while versus if, that's fair, though all of this is proof of concept. The while loop was installed when other things were occurring. It had just been repurposed temporarily. :) \$\endgroup\$ – Jesse Williams Sep 8 '16 at 16:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.