I don't get coroutines.

I have businesses which can be bought that create revenue and then purchasable upgrades (revenue multiplier) for them.
Here is the coroutine BusinessRevenue in GameManager class:

public IEnumerator BusinessRevenue(double revenue, int perDay)
{
    yield return new WaitForSeconds(5);
    while (true)
    {
        yield return new WaitForSeconds(perDay * 5);
        AdjustBalance(revenue);
        Debug.Log("Income: " + revenue);
    }
}

Here is where it’s started first in BusinessManager class:

public Coroutine coroutine;
coroutine = StartCoroutine(gameManager.BusinessRevenue(businessSO[buttonNumber].revenue, businessSO[buttonNumber].perDay));

Here is where it’s stopped fine in UpgradeManager class:

private Coroutine coroutine;
coroutine = businessManager.coroutine;
if (coroutine != null)
   StopCoroutine(coroutine);

Then revenue is changed:

double rev = business.revenue * upgradeSO[buttonNumber].multiplier;
double reve = Math.Round(rev * 100) / 100;
business.revenue = reve;

Then I want to restart coroutine with the new revenue:

StartCoroutine(gameManager.BusinessRevenue(business.revenue, business.perDay));

But it won’t work. I am a stupid newbie, please help.

“It doesn’t work” isn’t helpful to us. Which aspect(s) aren’t working?

The Coroutine is not running because there is no income debug.log

If you don’t understand coroutines then why not start experimenting with a basic and super simple one in a new project. Trying to understand something in a more complex environment of a larger project with all the things that could go wrong there isn’t the best way.

There’s lots of online videos about coroutines but there’s also one on the Unity Learn site here that might help a little.

Also, it’s easier on the eyes if you post code using code-tags .

I went through the Unity Learn and there wasn’t anything useful for me that I didn’t know yet. I’m trying my best to apply this knowledge on a real project as I know the basics of it. There’s an error in my logic however when it comes to this issue.

Coroutines in a nutshell:

Splitting up larger tasks in coroutines:

Coroutines are NOT always an appropriate solution: know when to use them!

Perhaps you’re stopping the coroutine and restarting it before the first 5 second delay has finished? Perhaps your perDay is exceptionally high, so your second yield is waiting hundreds of seconds?

Add a Debug.Log() to the top of your coroutine function and see if that logs, then you at least have a starting point to begin debugging.

1 Like

Coroutines can be weird to understand at first. First it’s good to understand C# iterator methods. These look like normal functions, but what they actually do under the hood is split the function body into smaller functions (at each yield statement), and create an object which manages a state machine to call those methods in sequence each time you call GetNext() on the returned IEnumerator object.

Unity basically takes the returned IEnumerator and puts it into a list, and then calls GetNext() on each one each frame. Each coroutine will then execute until it hits the next yield statement, then it stops and waits for the next frame. If the yield returns something like a WaitForSeconds, Unity will check how much time has passed since before trying to continue that coroutine.

So, coroutine don’t really process things in parallel like threads. What it does is split your code into chunks which execute then wait until the next frame, or a number of seconds, until the next chunk can continue.

1 Like

Thanks for the log tip. Sometimes it works and sometimes it doesn’t, but I am not sure what the key is. There seems to be something wrong with the timing, but I don’t understand it enough.

And edit: I had to include the

yield return new WaitForSeconds(5);

because at the Start, I have to wait 5 seconds before purchasing the business, otherwise it won’t make revenue.

What you need first is an understanding of what your code is doing. Add a unique Debug.Log() right before every StartCoroutine() and StopCoroutine() in your code. Add a Debug.Log before every yield statement in your code. Add a timestamp (Time.time) to the log too. Then observe.

You can not reason about something you have no knowledge about. Get that info.

1 Like

So my coroutine looks like this now

    public IEnumerator BusinessRevenue(double revenue, int perDay)
    {
        Debug.Log("Coroutine started");
        while (true)
        {
            Debug.Log("Before perDay wait");
            yield return new WaitForSeconds(perDay * 5);
            AdjustBalance(revenue);
            Debug.Log("Income: " + revenue);
        }
    }

And I buy businesses immediately on start and it works but when I upgrade them there are no more logs.
All works perfectly with certain timing but with certain timing it breaks. I need to find out why that is.

Add a Time.time to your logs. That might be helpful in understanding the timing. Have you added logs to StartCoroutine and StopCoroutine? What’s the last log you get, start or stop?

The code you gave in the first post has you assign the return value coming from StartCoroutine in one snippet but not the other, is that the issue?

// first snippet:
coroutine = StartCoroutine(gameManager.BusinessRevenue(businessSO[buttonNumber].revenue, businessSO[buttonNumber].perDay));

// second:
StartCoroutine(gameManager.BusinessRevenue(business.revenue, business.perDay));
1 Like

@Halpaviitta
Coroutines can be hard to debug, particularly when they are chained together (calling coroutines from coroutines).

It makes the execution flow of the program hard to read and follow. I try to avoid them as much as possible when implementing complex logic… actually, I just try to avoid them, full stop =).

As an alternative to coroutine, I strongly advice to have a look at the async/await built-in C# feature. The beauty of async/await is that it makes your code looks like synchronous code, whereas the executions is asynchronous. That makes your code easier to read and to debug.

It seems you are using coroutines as a mean to delay the execution of the program by using WaitForSeconds. You can use Task.Delay instead and obtain the same result. Furthermore, async method can return a value that has meaning in your application (not a useless IEnumerator). That means you can use an async method as if it was a regular method.

I don’t know the details about your business simulation implementation, therefore I am not able to fix your code (and I won’t fix it anyway), but here is a simple business simulation example implemented using async/await:

using UnityEngine;
using System.Threading.Tasks;

public class BusinessSimulation : MonoBehaviour
{
    public float revenuePerDay = 500;
    public int daysToSimulate = 5;

    async void Start()
    {
        float totalRevenue = await RunBusinessFor(daysToSimulate);
        Debug.Log($"TOTAL REVENUE AFTER {daysToSimulate} DAYS: {totalRevenue}");
    }

    async Task<float> RunBusinessFor(int days)
    {
        int elapsedDays = 0;
        float revenue = 0.0f;

        while(elapsedDays < days)
        {
            elapsedDays += 1;
            revenue += await SimulateRevenueForOneDay();
            Debug.Log($"Revenue after {elapsedDays} days: {revenue} ");
        }

        return revenue;
    }

    async Task<float> SimulateRevenueForOneDay()
    {
        await Task.Delay(1000); // Wait 1 second per day
        return revenuePerDay;
    }
}

If you are going to use more asynchronous programming in Unity, have a look UniTask. With this tool, you can turn any coroutine into and async/await method (I have no affiliation with UniTask, it’s just a tool that I use daily and like a lot).

2 Likes