Search code examples
c#unity-game-engine2d

IEnumerator Not Taking Input Value


I'm making a turn based RPG in Unity and I have a slider that pops up when the player attacks. A runs down the slider and the closer the player gets the slider to the center and presses Z, the more damage it deals. However, for some reason, instead of it being a simple input, you either have to smash Z or it won't take the input at all.

Here's the code:

void DamageGauge(int target, float power, bool special, string status, string name)
{
    int t = target;
    float p = power;
    bool s = special;
    string st = status;
    string n = name;

    meterOpen = true;
    slashSlider.gameObject.SetActive(true);
    slashSlider.value = 0;
    StartCoroutine(DamageSliderTick(t, p, s, st, n));
}

// ReSharper disable Unity.PerformanceAnalysis
public IEnumerator DamageSliderTick(int target, float power, bool special, string status, string name)
{
    bool press = false;

    int t = target;
    float p = power;
    bool s = special;
    string st = status;
    string n = name;

    BattleChar sTarget = activeBattlers[t];

    for (int i = 0; i < 51; i++)
    {
        yield return new WaitForSeconds(.01f);
        slashSlider.value += 4;
        
        if (Input.GetKeyDown(KeyCode.Z) || i == 50)
        {
            float v = slashSlider.value;

            if(i != 50)
            {
                switch (v)
                {
                    case < 20:
                        DealDamage(t, p, .5f, s, st, n);
                        break;
                    case < 50:
                        DealDamage(t, p, .8f, s, st, n);
                        break;
                    case < 85:
                        DealDamage(t, p, 1, s, st, n);
                        break;
                    case < 97:
                        DealDamage(t, p, 1.4f, s, st, n);
                        break;
                    case < 132:
                        DealDamage(t, p, 1, s, st, n);
                        break;
                    case < 170:
                        DealDamage(t, p, .8f, s, st, n);
                        break;
                    case < 200:
                        DealDamage(t, p, .5f, s, st, n);
                        break;
                }
            } else
            {
                DealDamage(t, p, .5f, s, st, n);
            }
            

            slashSlider.gameObject.SetActive(false);
            slashSlider.value = 0;
            

            if (activeBattlers[t].currentHP <= 0)
            {
                RewardDialog.instance.xpPool += sTarget.xpWorth;
                RewardDialog.instance.moneyPool += sTarget.dollarWorth;
                if (sTarget.itemDrop[0] != null)
                {
                    RewardDialog.instance.ShuffleItems(sTarget.itemDrop);
                }
            }

            UpdateStatus(t);
            NextTurn();
            break;
        }
    }
}

Solution

  • You could also wait directly in the Coroutine, just instead of using WaitForSeconds have a more manual wait loop like e.g.

    for(var time = 0f; time < 0.01f; time += Time.deltaTime)
    { 
        if(Input.GetKeyDown(KeyCode.Z))
        { 
            break; 
        } 
    
        yield return null;
    }
    

    This allows to track input every frame without missing one and directly interrupts the wait and continues with the rest when Z is pressed.


    Despite that, a waiting of 0.01 seconds barely makes sense. For 60 FPS one frame takes about 1/60 = 0.017 seconds. So you could as well just use a

    yield return null;
    

    so it waits exactly one frame. Then instead of increasing the slider by a hard value per frame you could go by per second and do

    slashSlider.value += 4 * Time.deltaTime;