Wednesday, July 6, 2022

[FIXED] How to have by-reference locals in coroutines in Unity?

Issue

I have some functions that take in an Enemy instance and change one of its fields. The Enemy class has some basic fields like speed, damage, attack range. Each function just stores one of the normal value of the enemy and then change the current field to some value for an amount of time and then change it back to the normal state (I code in Unity and use Coroutine for this). The difference is which field to store (speed or damage or attack range, etc). So I want to make a single function that takes in a delegate that returns the field that it wants to change:

delegate ref float Stat();

private IEnumerator EnemyStatModify(Enemies enemy, float duration, float percent, Stat stat)
{
    ref float enemyStat = ref stat();
    float normalStat = enemyStat;
    enemyStat *= (1 - percent);
    yield return new WaitForSeconds(duration);
    enemyStat = normalStat;
}

and then use it like this

EnemyStatModify(enemy, state.duration, state.timeBtwHits, () => ref enemy.speed);

EnemyStatModify(enemy, state.duration, state.timeBtwHits, () => ref enemy.damage);

But Iterators can't have by-reference locals. How to bypass this?


Solution

You actually don't need the return value of your delegate. You also don't need the Enemies since you don't use it in the routine anyway.

This is of course still not as beautiful as ref parameters would be but you could simply use a callback like

delegate void Stat(float newValue);


private IEnumerator EnemyStatModify(float duration, float percent, float currentValue, Stat onUpdated)
//or
//private IEnumerator EnemyStatModify(float duration, float percent, float stat, Action<float> onUpdated)
{
    onUpdated?.Invoke(currentValue * (1 - percent));

    yield return new WaitForSeconds(duration);

    onUpdated?.Invoke(currentValue);
}

and call it like

StartCoroutine(EnemyStatModify(state.duration, state.timeBtwHits, enemy.speed, (newValue) => 
{ 
    enemy.speed = newValue; 
}));

StartCoroutine(EnemyStatModify(state.duration, state.timeBtwHits, enemy.damage, (newValue) => 
{ 
    enemy.damage = newValue; 
});

Instead of the Stat you could also just use a Action<float>



Answered By - derHugo
Answer Checked By - Cary Denson (PHPFixing Admin)

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.