Been working on this Coroutine and wondering if it's suitable or a better way?

I’ve been working on a coroutine that will allow two UI objects to move to a certain location and then back again, based on a click. I’ve tried a few different methods but the only one that has really “worked” is the code below.

I’m relatively new to this so I am wondering if this is a pretty efficient way or if there is a better way.

Thanks.

 IEnumerator RemoveDetail()
    {
        F_active = F_start.position;
        F_inactive = F_target.position;
        int F_current = (int)Flag.position.x;
        int F_targetValue = (int)F_target.position.x;
      
        B_active = B_start.position;
        B_inactive = B_target.position;
        Vector2 B_current = Border.position;

        float rate = 200f;
        float startTime = Time.time;
        float F_distance = Vector2.Distance(F_active, F_inactive);
        float B_distance = Vector2.Distance(B_active, B_inactive);

        while (F_current != F_targetValue)
        {
            float movement = (Time.time - startTime) * rate;
            float F_moved = (movement / F_distance);
            float B_moved = (movement / B_distance);

            Flag.position = Vector2.Lerp(F_active, F_inactive, F_moved);
            Border.position = Vector2.Lerp(B_active, B_inactive, B_moved);
            F_current = (int)Flag.position.x;

            if(F_current == F_targetValue)
            {
                F_current = F_targetValue;
            }

            Debug.Log("target position" + F_targetValue);
            Debug.Log("flag position" + F_current);
    
            yield return null;
          
        }

    }

    IEnumerator ReturnDetail()
    {
        F_active = F_start.position;
        F_inactive = F_target.position;
        int F_current = (int)Flag.position.x;
        int F_targetValue = (int)F_start.position.x;

        B_active = B_start.position;
        B_inactive = B_target.position;
        Vector2 B_current = Border.position;

        float rate = 200f;
        float startTime = Time.time;
        float F_distance = Vector2.Distance(F_inactive, F_active);
        float B_distance = Vector2.Distance(B_inactive, B_active);
      
        while (F_current != F_targetValue)
        {
            float movement = (Time.time - startTime) * rate;
            float F_moved = (movement / F_distance);
            float B_moved = (movement / B_distance);

            Flag.position = Vector2.Lerp(F_inactive, F_active, F_moved);
            Border.position = Vector2.Lerp(B_inactive, B_active, B_moved);
            F_current = (int)Flag.position.x;

            if (F_current == F_targetValue)
            {
                F_current = F_targetValue;
            }

            Debug.Log("target position" + F_targetValue);
            Debug.Log("flag position" + F_current);

            yield return null;

        }
    }

You’ve got two methods there which are basically doing the same thing so code is duplicated. You could just add a bool to the coroutine and set the start and end positions for your lerp based on that and save duplicating code. Something like this should do the job;

[SerializeField]
private Transform Flag, Border;

[SerializeField]
private Vector3 _flagTargetPosOffset = new Vector3(0, 2, 0),
    _borderTargetPosOffset = new Vector3(0, 4, 0);

private Vector3 _flagStartPos,
    _borderStartPos;

private void Start()
{
    _flagStartPos = Flag.position;
    _borderStartPos = Border.position;

    StartCoroutine(MoveTransforms(false, 2f));
}

private IEnumerator MoveTransforms(bool moveToStart, float moveTime)
{
    var flagStart = Flag.position;
    var flagEnd = moveToStart ? _flagStartPos : _flagStartPos + _flagTargetPosOffset;

    var borderStart = Border.position;
    var borderEnd = moveToStart ? _borderStartPos : _borderStartPos + _borderTargetPosOffset;

    var t = 0f;

    while (t <= 1f)
    {
        Flag.position = Vector3.Lerp(flagStart, flagEnd, t);
        Border.position = Vector3.Lerp(borderStart, borderEnd, t);
        t += Time.deltaTime / moveTime;
        yield return null;
    }

    Flag.position = flagEnd;
    Border.position = borderEnd;
}

If you wanted to send them back to their start position, just call the coroutine and set the move to start parameter true instead.

1 Like

Yeah this works pretty good.

If you don’t mind can you clarify what some areas do? I don’t know to much about C# yet which is why my previous code was that way. I understand all of the code except this…

in line 23:

var flagEnd = moveToStart ? _flagStartPos : _flagStartPos + _flagTargetPosOffset;

what is the “?” and the “:” doing here?

and which part of this is actually returning “that it’s made it to it’s point” to end the coroutine? is it variable “t”? so that when the position is changed completely it’s setting “t” to 1?

the rest is pretty straight forward I just wanna fully understand a code instead of just “copy” and “paste”.

Thanks.

? is a conditional operator which means condition ? consequent : alternative or ? : ;

In the line you posted there, it is the same as doing this;

Vector3 flagEnd;

if(moveToStart == true)
{
    flagEnd = _flagStartPos;
}
else
{
    flagEnd = _flagStartPos + _flagTargetPosOffset;
}

just with less code of course.

When the float t has reached a value of one, the objects have been moved to the endpoint via the lerp method. That is when the code will exit from the while statement and the coroutine will complete that frame,

Ah okay, thank you for that. I’ve never seen or used that method so it was a bit confusing but it sort of makes sense.
I appreciate the reply and the knowledge shared!

Just curious though, was the method I originally started with a “decent” way of accomplishing that for the most part? Like I said I am relatively new to more advanced C# and went and pieced that together lol.

It seems fine. yield return null; is the exact unity way to wait for the next frame. It’s funny-looking, but it’s short. ?: is a basic trick in many languages before C#. The real advantage is it _returns _ a value, so can be used in expressions: print("You have "+n+" "+(age>2?"cats":"kittens"));. By itself, it’s mostly used for very short things: health=hardMode?85:100;.

while (F_current != F_targetValue) is fine. In all languages, including C#, float equality is tricky, but Unity’s Lerp’s will put the final value exactly, perfectly on the end point.

War*Mints used var everywhere/ That’s just a new C# option and is exactly the same as declaring them normally. Funny thing, Unity’s first language, UnityScript, also used var to declare. It was because game designers didn’t understand int vs. float (“why do you have to tell the computer a number has no decimals? Just don’t use decimals”). You can also change the editor settings to put open curly-braces at the end of the line instead of on a new one. It depends whether you consider yourself a C#'er, or a general programmer. C#'ers also hate underscores in the middle of identifiers. They camel case everything.

2 Likes