Home > Software engineering >  Action with unknow number of parameters
Action with unknow number of parameters

Time:12-02

I'm creating a flexible dialog system, I have a static IEnumerator that type every char from a string that I pass, after finishes it will run a method:

public static IEnumerator Typewritter(..., Action method)
{
    //My logic...
    method();
}

When I call it in another class, I'm able to pass a void method normally:

public class ExampleClass : MonoBehaviour
{
    void Start()
    {
         EnemyPresentation();
    }

    void FadeEnemy()
    {
         //Logic.
    }

    void EnemyPresentation()
    {
         StartCoroutine(Dialog.Typewritter(..., FadeEnemy));
    }
}

My question is, I have some methods that have parameters, and I cannot pass them in the IEnumerator - is there a way to specify an Action with an unknown amount of parameters?

Examples of my other methods:

void ChangeBoxSize(RectTransform box, Vector2 size, float animDuration){}
void ShowBubble(Image bubble, bool isVisible){}
void EnableAttackometer(bool boolean){}

Tried Action<> with some generics configs, but they didn’t work.

CodePudding user response:

Let me preface this by saying I don't know whether you'd really ever want to do this or not. Once set up, it works, and it does what you were asking (within limits). This is more an exercise in finding a solution to a question. So, with that being said, let's look at how you would implement a solution using generics.

Firstly, you would determine how many arguments you'd like as a maximum. From that number, you would create that many methods, all called the same, but with differing numbers of parameters. Like so:

public IEnumerator TypeWriter<T1> ( string text, Action<T1> a, T1 t1 )
{
    var c = StartCoroutine ( TypeWriterEnumerator ( text ) );
    yield return c;
    a?.Invoke ( t1 );
}

public IEnumerator TypeWriter<T1, T2> ( string text, Action<T1, T2> a, T1 t1, T2 t2 )
{
    var c = StartCoroutine ( TypeWriterEnumerator ( text ) );
    yield return c;
    a?.Invoke ( t1, t2 );
}

public IEnumerator TypeWriter<T1, T2, T3> ( string text, Action<T1, T2, T3> a, T1 t1, T2 t2, T3 t3 )
{
    var c = StartCoroutine ( TypeWriterEnumerator ( text ) );
    yield return c;
    a?.Invoke ( t1, t2, t3 );
}

private IEnumerator TypeWriterEnumerator ( string text )
{
    // Your typerwriter logic ... e.g.:
    Debug.Log ( $"Start TypeWriterEnumerator ('{text}') [{Time.time}]" );
    yield return new WaitForSeconds ( 1f );
    Debug.Log ( $"End TypeWriterEnumerator  [{Time.time}]" );
}

Now, to actually use this, let's look at a full code example:

public class Test : MonoBehaviour
{
    private void Start ( )
    {
        StartCoroutine ( TypeWriter ( "Hello T3", Action3, 1, 2.0f, this.gameObject ) );
    }

    // An example of an action you want called after the type writer has finished. This one has three parameters.
    private void Action3 ( int a, float b, GameObject c )
    {
        Debug.Log ( $"-> Action3({a}, {b}, '{c.name}')" );
    }

    public IEnumerator TypeWriter<T1> ( string text, Action<T1> a, T1 t1 )
    {
        var c = StartCoroutine ( TypeWriterEnumerator ( text ) );
        yield return c;
        a?.Invoke ( t1 );
    }

    public IEnumerator TypeWriter<T1, T2> ( string text, Action<T1, T2> a, T1 t1, T2 t2 )
    {
        var c = StartCoroutine ( TypeWriterEnumerator ( text ) );
        yield return c;
        a?.Invoke ( t1, t2 );
    }

    public IEnumerator TypeWriter<T1, T2, T3> ( string text, Action<T1, T2, T3> a, T1 t1, T2 t2, T3 t3 )
    {
        var c = StartCoroutine ( TypeWriterEnumerator ( text ) );
        yield return c;
        a?.Invoke ( t1, t2, t3 );
    }


    private IEnumerator TypeWriterEnumerator ( string text )
    {
        // Your typerwriter logic ... e.g.:
        Debug.Log ( $"Start TypeWriterEnumerator ('{text}') [{Time.time}]" );
        yield return new WaitForSeconds ( 1f );
        Debug.Log ( $"End TypeWriterEnumerator  [{Time.time}]" );
    }
}

Remember the disclaimer at the beginning? I still stand by that. Now, my aversion to coroutines aside,` it's Unity, and the Unity engineers DO make use of coroutines, and expect developers to use them as well. This functionality was included specifically for situations similar to this, where you might want to chain coroutines.

Now, moving on from that, you'll notice that in many libraries, the engineers will include code very similar to this; methods with a varying number of generic parameters. You need look no further than the Unity documentation to see an example: Unity documentation

  • Related