Search Unity

Feedback Custom Scale Tweening Class Feedback

Discussion in 'Scripting' started by angeldevelopment, May 9, 2023.

  1. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    247
    Here I have written a Tweening class that scales an object over a period of time. The only thing I don't like about it is that it requires a gameobject with the script attached to it to be in a scene to create an instance to run the coroutines. A work around would be to just get the monobehavior component attached the the game object that we want to scale, and having that component run the coroutine:
    obj.GetComponenet<Monobehavior>().StartCoroutine();
    But I figure this will be much more performant because getcomponent is expensive. Just looking for thought and feedback, I plan to implement a way to execute other coroutines on completion, similar to
    SKAction.run {}, completion: {} in sprite kit.

    Could there be any problems if there are 100 coroutines at once? Does it make more sense for the object being scale to run the courtine?

    Code (CSharp):
    1. public class ActionManager : MonoBehaviour
    2. {
    3.  
    4.     public static ActionManager instance;
    5.  
    6.     void Awake() {
    7.         instance = this;
    8.     }
    9.  
    10.     static IEnumerator Execute(Transform transform, Vector3 finalScale, float time) {
    11.  
    12.         float startingX = transform.localScale.x;
    13.         float startingY = transform.localScale.y;
    14.  
    15.         float finalX = finalScale.x;
    16.         float finalY = finalScale.y;
    17.  
    18.         float elaspedTime = 0;
    19.  
    20.         while (elaspedTime < time) {
    21.  
    22.             float newX = Mathf.Lerp(startingX, finalX, elaspedTime / time);
    23.             float newY = Mathf.Lerp(startingY, finalY, elaspedTime / time);
    24.             transform.localScale = new Vector3(newX, newY, 1);
    25.             elaspedTime += Time.deltaTime;
    26.             yield return null;
    27.         }
    28.         yield return null;
    29.     }
    30.  
    31.     public static void ScaleBy(Transform transform, float factor, float time) {
    32.         float startX = transform.localScale.x;
    33.         float startY = transform.localScale.y;
    34.  
    35.         float endX = startX * factor;
    36.         float endY = startY * factor;
    37.  
    38.         Vector3 startingScale = new Vector3(startX, startY, 1);
    39.         Vector3 finalScale = new Vector3(endX, endY, 1);
    40.  
    41.         instance.StartCoroutine(Execute(transform, finalScale, time));
    42.     }
    43. }
     
  2. angeldevelopment

    angeldevelopment

    Joined:
    Sep 28, 2022
    Posts:
    247
    This is for a 2D project btw, thus the z scale is always 1
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,531
    I haven't personally ran into any real issues in regards to having a ton of Coroutines outside of just general update performance. But that's not exactly the coroutine, and more just having a ton of work done per update.

    That depends.

    One of the convenient factors to having the Coroutine on the object being scaled is that if it gets disabled/destroyed, the coroutine also gets disabled/destroyed. If it's on a different object, then you could accidentally set the scale of a destroyed object (which causes a runtime exception).

    ...

    With that said, a lot of tween libraries out there hook into the update loop independently.

    For example DOTween (a popular tween library) has a dedicated component that calls the Update method of the TweenManager:
    https://github.com/Demigiant/dotwee...Assembly/DOTween/Core/DOTweenComponent.cs#L75

    You can even hook directly into the PlayerLoop:
    https://docs.unity3d.com/ScriptReference/LowLevel.PlayerLoop.SetPlayerLoop.html

    For example UniTask (not a tween library, but a async task library for unity) inserts into the update loop this way:
    https://github.com/Cysharp/UniTask/...gins/UniTask/Runtime/PlayerLoopHelper.cs#L394

    I use a component like DOTween does in my own tween library:
    https://github.com/lordofduct/space...spacepuppy.tween/Runtime/src/Tween/SPTween.cs

    I've considered moving over to the PlayerLoop approach (it used to not exist, it was only added in 2019.3), but why fix what ain't broke?

    Heck, I even have a 'GameLoop' class that lets me inject coroutines and callbacks and what not sort of how you describe:
    https://github.com/lordofduct/space...k/com.spacepuppy.core/Runtime/src/GameLoop.cs

    I do this for what I usually refer to as "guaranteed" calls. So like say I want to have some function called after 1 second delay, but the component in question doing that might be disabled in that time. I do what I call an 'InvokeGuaranteed' that uses the GameLoop class I linked as the MonoBehaviour to perform the wait on so that I know it'll occur.

    Which, again, is basically what you're doing. You want a dedicated MonoBehaviour for running coroutines. And you're wondering if having too many on 1 object can be a problem. Well... I do it and never had a problem with it.

    The only consideration is if the behaviour in question is what you expect. That being if you disable the object you're "tweening", do you want the coroutine to cancel or not?
     
    angeldevelopment likes this.