1
\$\begingroup\$

My player is able to dive for a limited amount of seconds (let's say 10 seconds) while swimming. When he's not diving his breath gauge fills up again to a max of 10 seconds:

private bool isFreediving;
private float breath = 10f;

void Update()
{
    if (isFreediving)
    {
        if (breath > 0)
        {
            breath -= Time.deltaTime;
            Debug.Log(breath);
        }
        else
        {
            Debug.Log("Out of breath");
            breath = 0; // optional?
            isFreediving = false;
            // Do staff...
        }
    }
    else
    {
        if (breath == 0)
        {
            // TODO: Make the player stop moving for 1 second
            // TODO: Stop breath += Time.deltaTime for 3 seconds
        }
        else if (breath > 0 && breath < 10)
        {
            breath += Time.deltaTime; // TODO: Make breath fill up slower/faster
            Debug.Log(breath);
        }
    }
}

If the player runs out of breath completely, I want him to stop moving for a second and I also want the breath gauge to not start to fill up for 3 seconds. I tried with coroutines, but my code is inside the Update() method, so it didn't work. I realize that the above If-else statements need to be rearranged properly.

Lastly, I want the breath gauge to be filled up at a different pace, for example:

breath += Time.deltaTime * 0.5f;

I wonder if there is a better way?

EDIT: After @Pow's solution:

float swimSpeed = 0.03f;
private bool isFreediving;
private float breath = 10f;
private bool canRefillBreath;

void Update()
{
    float swimAmount = Input.GetAxis("Vertical") * swimSpeed;
    transform.Translate(0, swimAmount, 0);

    if (isFreediving)
    {
        if (breath > 0) breath -= Time.deltaTime;
        else
        {
            breath = 0;
            isFreediving = false;
            // Do staff...    
            canRefillBreath = false;
        }
    }
    else
    {
        if (breath == 0)
        {
            swimSpeed = 0;
            Invoke(nameof(EnableSwim), 1f);
            Invoke(nameof(EnableRefill), 3f);
        }
        if (canRefillBreath && breath < 10) breath += Time.deltaTime;
    }
}    

private void EnableSwim() => swimSpeed = 0.03f;
private void EnableRefill() => canRefillBreath = true;

The only weird thing is that after 1 second the player starts moving at a slower rate until the 3 seconds Invoke(nameof(EnableRefill), 3f);, then he goes full speed.

\$\endgroup\$
7
  • \$\begingroup\$ Isn't that because you set swimSpeed to 0.03f after one second? Invoke(nameof(EnableSwim), 1f); \$\endgroup\$
    – Pow
    Jan 21 at 12:48
  • \$\begingroup\$ 0.03f is the default speed, but the player doesn't move at that speed after 1 second. He's slower for the first two seconds until the 3 seconds of the EnableRefill run out. EnableSwim and EnableRefill shouldn't affect each other, but they do. Perhaps the contemporarity slows the cpu, although the two Invokes don't consume much memory. \$\endgroup\$
    – Tomo
    Jan 21 at 14:00
  • \$\begingroup\$ the Invokes wouldn't effect the CPU at all, you've probably just missed something somewhere in your code. \$\endgroup\$
    – Pow
    Jan 21 at 17:05
  • \$\begingroup\$ I agree. All the relevant code is in the EDIT above. Everything seems ok unless I don't properly understand the Unity life cycle. \$\endgroup\$
    – Tomo
    Jan 21 at 17:18
  • \$\begingroup\$ I would highly reccomend you dont use Transform.Translate to move, as it completely ignores physics, that could also be the reason why. \$\endgroup\$
    – Pow
    Jan 21 at 20:41

1 Answer 1

1
\$\begingroup\$

A good way to make something happen only once/after a delay in the Update function is to use booleans, simple yet effective. You can use the Invoke() method to enable/disable booleans when needed.

Heres an example using your script:

private bool runOutOfBreath = false;
private bool canRefillBreath = false;
void Update()
{
    if (isFreediving)
    {
        if (breath > 0)
        {
            runOutOfBreath = false;
            breath -= Time.deltaTime;
            Debug.Log(breath);
        }
        else
        {
            runOutOfBreath = true;
            Debug.Log("Out of breath");
            breath = 0; // optional?
            isFreediving = false;
            // Do staff...
        }
    }
    else {
        if (runOutOfBreath) {
            // your function/coroutine.
            runOutOfBreath = false;
            canRefillBreath = false;
        }
        if (canRefillBreath && breath < 10) {
            breath += Time.deltaTime; // TODO: Make breath fill up slower/faster
            Debug.Log(breath);
        }
        if (breath == 0) {
            Invoke(nameof(EnableRefill), 3f);
        }
    }
}
private void EnableRefill() {
    canRefillBreath = true;
}

NOTE: you may want to move the if (runOutOfBreath) somewhere else depending on your use case. Right now it only checks if you've run out and your not diving. Hope this fixes your issue and let me know if you have any problems :)

\$\endgroup\$
3
  • \$\begingroup\$ The last condition can be simplified since it double checks if breath is below 10 and equals 0 \$\endgroup\$
    – Zibelas
    Jan 20 at 13:39
  • \$\begingroup\$ oh I missed a section, ill edit it in \$\endgroup\$
    – Pow
    Jan 20 at 15:01
  • \$\begingroup\$ Nice solution. Tried Invoke before, but this time works. I don't really need the runOutOfBreath part. I'm doing the prevent player from moving part with transform.Translate(0, 0, 0);. \$\endgroup\$
    – Tomo
    Jan 21 at 12:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .