Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

TeaTime: A fast queue for callbacks, to solve common coroutines patterns

Discussion in 'Assets and Asset Store' started by alvivar, Jan 7, 2015.

  1. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    Hi!

    I have been experimenting, trying to improve my workflow with coroutines, looking for a fast and safe shortcut to achieve all those common patterns; locks, timers, sequences, animations, tweens, all of them mixed, etc.

    And after a fun week of coding and some testing, I just finished this dark magic: TeaTime, a fast & simple queue for timed callbacks, fashioned as a MonoBehaviour extension set.

    Just put 'TeaTime.cs' somewhere in your project and call it inside any MonoBehaviour using 'this.tt' (and trust the autocomplete).


    > TeaTime.zip (Current version: v0.7.4 beta)
    http://github.com/alvivar/TeaTime

    > Examples
    http://github.com/alvivar/TeaTime/tree/master/Examples

    > Github
    http://github.com/alvivar/TeaTime


    Classic example:
    Code (CSharp):
    1.  
    2.     // In MonoBehaviours
    3.     TeaTime queue = this.tt().Add(1, () =>
    4.     {
    5.         // The queue autoplays by default
    6.         Debug.Log("Once second later! " + Time.time);
    7.     })
    8.     .Loop(3, (ttHandler loop) =>
    9.     {
    10.         // This callback is repeated per frame during the loop duration
    11.         transform.position =
    12.             Vector3.Lerp(
    13.                 transform.position,
    14.                 transform.position + Random.insideUnitSphere,
    15.                 loop.deltaTime // deltaTime sincronized with the loop duration
    16.             );
    17.     })
    18.     .Add(() =>
    19.     {
    20.         Debug.Log("The loop is done! " + Time.time);
    21.     })
    22.     .Repeat(); // Repeats forever!
    23.  
    24.     // And more!
    25.     queue.Pause();
    26.     queue.Play();
    27.     queue.Stop();
    28.  

    And that's it!

    By Andrés Villalobos
     
    Last edited: Mar 28, 2016
    MikeTon and twobob like this.
  2. SirStompsalot

    SirStompsalot

    Joined:
    Sep 28, 2013
    Posts:
    112
    What wizardry is this? Haven't looked into it too much, but looks like voodoo.

    Will toss it into my projects and see what happens. Thanks!
     
  3. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    Cool! If you have any questions just drop it here and I'll try my best.
     
  4. ZJP

    ZJP

    Joined:
    Jan 22, 2010
    Posts:
    2,649
  5. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    Last edited: Jan 29, 2015
  6. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    New version: TeaTime 0.5.4!

    + Some optimizations!

    + API renamed for faster writing and cleaner chains.

    - ttAppend renamed to ttAdd.
    - ttAppendLoop renamed to ttLoop.
    - ttInvoke renamed to ttNow.
    - ttLock renamed to ttWaitForCompletion.

    + Improved examples!

    The current version was heavily tested during the latest Global Game Jam by me and my friend, I believe it's stable enough for you all to try it!

    I hope you enjoyed it!
     
    Last edited: Jan 31, 2015
  7. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    NEW TeaTime 0.6!
    DOWNLOAD // GITHUB


    Check out Examples.cs to learn with practical patterns! (More to come)

    Featuring .ttRepeat(n)
    + Repeats the current queue n times or infinite (n <= -1).

    Changes
    - .ttAdd( & .ttLoop(, can't create/change the current queue now, use .tt( instead.


    Hope you like it!
     
    Last edited: Mar 17, 2015
  8. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    Is it possible to queue things up without processing them, and then process the queue on command at a later time?
     
  9. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    No, by design, because you can mimic that behavior with a function.

    Example:
    Code (CSharp):
    1. void DebugTextDelay(int delay, string text) {
    2.     this.tt("queueName").ttAdd(delay, delegate()
    3.     {
    4.         Debug.Log(text);
    5.     });
    6. }
    This way, you can contain complex queues with safety, and every time you call the function is like re-calling the queue.

    Tell me if it works for you.
     
    Last edited: Apr 3, 2015
  10. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    Unless I misunderstand, this will immediately start counting down as soon as you call the function DebugTextDelay, and then after `delay`, the Debug.Log(text) will execute?

    This is not the same thing that I'm suggesting. I was thinking that you could add in a simple flag "enabled" or whatever, to control the processing of items in the given queue. In the above case it could be:

    Code (CSharp):
    1. this.tt("queueName").Paused = true;
    2. this.tt("queueName").Add(delay, () => Debug.Log("Hello!"));
    3. ...
    4. //lots of wonderful stuff happening until we're ready to process the queue
    5. ...
    6. this.tt("queueName").Paused = false;
    This would be relevant when, say, users are causing a bunch of required actions that I only want to execute when they are done. So, I add each action to a queue, which gets processed when they click "done", for example.​
     
  11. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    Hm, yes, I can see how that could be useful.

    It's now on my to-do for the next version!

    It will be probably like this:

    Code (CSharp):
    1. this.tt("queueName").ttStop();
    2. this.tt("queueName").ttAdd(delay, () => Debug.Log("Hello!"));
    3. ...
    4. // lots of wonderful stuff happening until we're ready to process the queue
    5. ...
    6. this.tt("queueName").ttPlay();
    Thanks for your suggestion!
     
    twobob likes this.
  12. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    => Debug.Log ("Emit garbage")?
    This looks like a very PC specific tool, given the emissions, is it?

    Excellent work however, just checking the specifics
     
  13. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    In this case "Debug.Log" is just an example, that parameter is an Action used as a callback for whatever code you want to queue on an specific time.

    TeaTime is just a MonoBehaviour extension, it should work in all the platforms supporting coroutines.
     
  14. twobob

    twobob

    Joined:
    Jun 28, 2014
    Posts:
    2,058
    To make that question clearer. Many of the things I see appear to create garbage.
    Would this be appropriate for work on mobile, given this constraint.
     
  15. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    Got it, you are right.

    Right now I cannot assure you TeaTime is optimal for mobile, I have been working mostly on the algorithm, workflow and patterns, and I still have some things to solve there. But of course this kind of optimizations are on my to-do for next versions, I'll try my best handling those delegates.

    Thanks for commenting about it!
     
    twobob likes this.
  16. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    NEW UPDATE TeaTime 0.6.5!
    DOWNLOAD // GITHUB



    Featuring ttPause, ttStop and ttPlay! And they work just like the word says, you can always see the changes log here.

    I have this particular example showing those new features. The rest of the examples here, as usual (I'm starting to rethink the examples, maybe a tutorial, I have lots to do!)

    Feel free to post your doubts here, or you can ask me personally on twitter.


    Hope you like this update!

    BONUS: I did made this game for Ludum Dare 32, Drybreed, play it and tell me what you think, powered by TeaTime v0.6.5 of course :D
     
    Last edited: Apr 29, 2015
  17. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    New version!
    TeaTime v0.7.4 beta!
    http://github.com/alvivar/TeaTime



    There has been lots of changes since the last post here, I did a complete code rewrite, there is a cleaner API, better performance and TeaTime is now fully type-safe object-oriented. I'm so happy with this experiment!

    Here are some examples / patterns! Give it a try!


    Code (CSharp):
    1.  
    2.     // A simple 2 seconds delay.
    3.     TeaTime simpleDelay = this.tt().Add(2, () =>
    4.     {
    5.         Debug.Log("simpleDelay: Two seconds later, just once!" + Time.time);
    6.     });
    7.  
    8.  
    9.     // Something that repeats itself every 3 seconds.
    10.     TeaTime repeatDelay = this.tt().Add(() =>
    11.     {
    12.         Debug.Log("repeatDelay: Every 3 seconds, repeats forever! " + Time.time);
    13.     })
    14.     .Add(3).Repeat();
    15.  
    16.  
    17.     // A controlled frame by frame loop (update-like) with 1.5 seconds duration!
    18.     TeaTime updateLike = this.tt().Loop(1.5f, (ttHandler loop) =>
    19.     {
    20.         Debug.Log("updateLike: Frame by frame during 1.5 seconds, just once! " + Time.time);
    21.     });
    22.  
    23.  
    24.     // A simple delay without autoplay.
    25.     TeaTime somethingForLater = this.tt().Pause().Add(3, () =>
    26.     {
    27.         Debug.Log("somethingForLater: Someone called 'somethingForLater.Play()' 3 second ago! " + Time.time);
    28.     });
    29.  
    30.     somethingForLater.Play();
    31.  
    32.  
    33.     // A tween-like with before and after setup.
    34.     TeaTime tweenLike = this.tt().Add(() =>
    35.     {
    36.         Debug.Log("tweenLike: Just before the 4 seconds loop! " + Time.time);
    37.         transform.position = new Vector3(999, 999, 999);
    38.     })
    39.     .Loop(4, (ttHandler loop) =>
    40.     {
    41.         transform.position = Vector3.Lerp(
    42.             transform.position,
    43.             Vector3.zero,
    44.             loop.deltaTime);
    45.     })
    46.     .Add(() =>
    47.     {
    48.         Debug.Log("tweenLike: Just after the 4 seconds loop! " + Time.time);
    49.     });
    50.  

    More examples here!
    http://github.com/alvivar/TeaTime/tree/master/Examples

    And feel free to ask me about it!
    http://twitter.com/matnesis
     
  18. SirStompsalot

    SirStompsalot

    Joined:
    Sep 28, 2013
    Posts:
    112
    You know, I would LOVE to see some compatibility with something like https://www.assetstore.unity3d.com/en/#!/content/31225

    Have you done any stress testing on this? I'm curious to see how many timer operations it could handle easily.

    And with the new refactor, how do you think it'll perform on mobile now?
     
  19. alvivar

    alvivar

    Joined:
    Aug 7, 2012
    Posts:
    16
    @SirStompsalot

    Probably my worst sin with TeaTime is that I'm using a generic list to store the queue. I should use an Array that automatically grows instead, because Unity doesn't create garbage with arrays.

    Code (CSharp):
    1. List<ttTask> _tasks = new List<ttTask>();

    I haven't made this change yet because it makes the code uglier, but it's something that I'm planning to do! :(

    Right now, by default, a TeaTime queue is non-destructive, so, every time you define a queue, the queue will stay that way, so you can use Play(), Pause(), Stop(), Repeat() or Immutable() to control your queue without worry about garbage.

    But, if you are using Consume() mode (that deletes tasks after execution, a queue without memory) or Reset() that cleans everything, this will generates garbage. This will be fixed once I migrate to arrays.

    Yes, the Chronos support should be possible by creating a TeaTime global manager, something that I'm experimenting with, to improve other stuff.

    Doing a decent stress testing on most platforms and make the change to arrays is on my to-do! Soon I hope! ;)