Life and Style Media - Tutorials

Version 1.4.x



Learn to create custom units starting with the basics of Inputs and Outputs, Controls and Values. This is the first part of many, where you will learn to create custom code within the Ludiq and Bolt framework.




VARIABLE SETUP


Now that you have created your first unit, we are going to teach you the next step. Coroutines!

If you ever intend a unit to do work over time, this is the perfect tutorial for you. In this tutorial we will be creating something that you can actually use in your projects, instead of a throwaway unit. This unit will be a Type Writer unit. You will be able to display one extra character every X amount of seconds until it is fully written out.

To start, we will setup our variables just like last time. What will we need?


  • A port to start the coroutine. We will use an actual method for this one, instead of a lambda.

  • An exit for right after we start the coroutine.

  • A complete port for when the typing is done.

  • An input value of string for the text we want to write.

  • An input float for the time delay between each character.

  • An output string value port for the current text written.

  • And a storing variable for the current text that we will feed out to the current text port.




  • We won't go over the basics here, so we assume you understand how that can be setup. You should have the following:


    using
    Ludiq.Bolt;
    using
    Ludiq;
    using
    UnityEngine;

    public class
    TypeWriterUnit
    :
    Unit

    {
    [
    DoNotSerialize
    ]
    public
    ControlInput
    start;

    [
    DoNotSerialize
    ]
    public
    ControlOutput
    exit;

    [
    DoNotSerialize
    ]
    public
    ControlOutput
    typed;

    [
    DoNotSerialize
    ]
    public
    ControlOutput
    complete;

    [
    DoNotSerialize
    ]
    public
    ValueInput
    text;

    [
    DoNotSerialize
    ]
    public
    ValueInput
    delay;

    [
    DoNotSerialize
    ]
    public
    ValueOutput
    current;

    private
    string
    storedText =
    string
    .Empty;

    protected override void
    Definition()
    {

    }
    }


    DEFINING PORTS


    Now that we have the setup lets define all the ports before we get to the logic.

    When you have a coroutine, the input that runs one, must use an input coroutine instead of a normal input. There is a similar input method like ControlInput, but called ControlInputCoroutine.

    This is setup in the exact same way as other inputs, except the 2nd parameter.

    Normally we would do:
    Func<Flow, ControlOutput>


    Instead it becomes:
    Func<Flow, IEnumerator>


    Which the IEnumerator is necessary for all Unity Coroutines.


    using
    Ludiq.Bolt;
    using
    Ludiq;

    public class
    TypeWriterUnit
    :
    Unit

    {
    [
    DoNotSerialize
    ]
    public
    ControlInput
    start;

    [
    DoNotSerialize
    ]
    public
    ControlOutput
    exit;

    [
    DoNotSerialize
    ]
    public
    ControlOutput
    typed;

    [
    DoNotSerialize
    ]
    public
    ControlOutput
    complete;

    [
    DoNotSerialize
    ]
    public
    ValueInput
    text;

    [
    DoNotSerialize
    ]
    public
    ValueInput
    delay;

    [
    DoNotSerialize
    ]
    public
    ValueOutput
    current;

    private
    string
    storedText =
    string
    .Empty;

    protected override void
    Definition()
    {
    start = ControlInputCoroutine(
    "start"
    , WriteText);
    text = ValueInput<
    string
    >(
    "text"
    ,
    string
    .Empty);
    delay = ValueInput<
    string
    >(
    "delay"
    , 0.2f);
    current = ValueOutput<
    string
    >(
    "current"
    , (flow) => {
    return
    storedText; });
    typed = ControlOutput(
    "typed"
    );
    complete = ControlOutput(
    "complete"
    );
    exit = ControlOutput(
    "exit"
    );
    }



    private IEnumerator
    WriteText(
    Flow
    flow)
    {
    // Placeholder to prevent errors.

    yield return null
    ;
    }
    }


    Now that we are setup, lets generate the unit. This is what you should have:



    MAKING THE COROUTINE


    This is the last step and you are on your way. We must create the behaviour for the routine to occur. First we should cache our input values when we first enter the unit. So lets get and set our startingText, time, and count. The count will be used to determine what the current length of the string is. A string is also an array of characters, so we can use it to iterate over it, using the current count.

    private IEnumerator
    WriteText(
    Flow
    flow)
    {
    var
    startingText = flow.GetValue<
    string
    >(text);
    var
    _delay = flow.GetValue<
    float
    >(delay);
    var
    count = 1;
    }


    Next, we know we will need to exit immediately before we ever start looping. A Flow itself, is yieldable as an enumerator, so yield the exit port in order to keep the output as a coroutine itself, allowing nestable routines.



    private IEnumerator
    WriteText(
    Flow
    flow)
    {
    var
    startingText = flow.GetValue<
    string
    >(text);
    var
    _delay = flow.GetValue<
    float
    >(delay);
    var
    count = 1;

    yield return
    exit;
    }


    We're now going to create the most important step, the loop!

    Each loop count, we will assign the storedText class variable using SubString. This allows us to grab a section of the full text, based on the current count. From '0', the first character, to 'count', which is the last character. This variable is what we are using to send to the output. That is important.

    After that we want to yield our output 'typed' port, wait X amount of seconds, then add another count, and repeat. All this will occur until the storedText is equal or greater then the length of the startingText value. Afterward, we will yield the complete port, and then we are done coding!

    private IEnumerator
    WriteText(
    Flow
    flow)
    {
    var
    startingText = flow.GetValue<
    string
    >(text);
    var
    _delay = flow.GetValue<
    float
    >(delay);
    var
    count = 1;

    yield return
    exit;

    while
    (storedText.Length < startingText.Length)
    {
    storedText = startingText.Substring(0, count);

    yield return
    typed;

    yield return new
    WaitForSeconds
    (time);

    count++;
    }

    yield return
    complete;
    }


    LETS TEST IT!


    We are all set. Last thing to do is set it up in the graph. We will debug the text to make sure it is working properly. Don't forget, in 1.4 your start event should be checkmarked 'Coroutine' in the graph inspector. Here is what your final result should look like: