Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

[FREE] More Effective Coroutines

Discussion in 'Assets and Asset Store' started by Trinary, Feb 23, 2016.

  1. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    228
    I wasn't asking for help. I was asking the author to explain how their code interfaced to the official code, or to confirm that it does / doesn't - rather than me spend days trying to debug something that may or may not work in the first place. I expected the author to know what their code does, and how it does it, and what the official API requirements are (since they decided to interface with it in the first place).

    When I couldn't get an answer, I asked for any tips/advice on reproducing this asset without breaking compatibility.

    This thread seems to be an echo chamber. To re-iterate: I've written complete co-routines executors for Unity before. We didn't care about performance, but we did care about correctness, so I have extensive experience delving into what a Unity C-R is, how it works, why it works, what the C# compiler is doing. I didn't expect to get abused and insulted just for asking questions about an asset that plays in the same area. I don't have any experience optimizing C-R executors/containers, but if no-one here wants to share, I'm sure I can figure it out with time. At the end of the day, this thread has left a bad taste in the mouth.

    PS: ...and you can bet I won't be sharing anything back here afterwards. Passive aggression drives people away.
     
  2. guzzo

    guzzo

    Joined:
    Feb 20, 2014
    Posts:
    79
    @a436t4ataf dude, read the thread again and have some self-criticism. You are wrong and you are the one with bad attitude.
     
  3. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    181
    ^ agree with above poster.

    If this guy is not trolling... he is definitely self deluded or something. It's like he missed the class on basic communication skills in preschool.
     
    guzzo likes this.
  4. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    MEC does not interface Unity coroutines in any way, it has it's own implementation. The asset is free and the source is available, you can easily go through it, it's just a single file...
     
  5. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    v1.7.1 just came out today.

    This version includes a couple of minor features and a bugfix for a problem that I found in v1.7.0 where coroutines would be dropped from the list if you defined tags. If you're currently using 1.7.0 you should update to 1.7.1 ASAP so you don't end up tracking down bugs that you don't need to.

    I'm still not sure what random number dude was referring to, but I went ahead and added an overload to WaitUntilDone to accept a CustomYieldInstruction, which might be what he was talking about.
     
  6. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    v1.7.2 should be coming out this Monday. It contains another bugfix for a problem in which using WaitForSeconds in the first frame of a coroutine will occasionally wait much longer than intended. I submitted the bugfix last Wednesday and had hoped it would be approved before the end of the week, but it looks like it wasn't.

    If anyone needs v1.7.2 before Monday then feel free to PM me your email address and I will send it to you. All the testers so far have found v1.7.2 to be stable.
     
    AlteredPlanet likes this.
  7. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    v1.7.2 is now live. In addition to the bugfix mentioned in the last post it also reduces the initial size of the buffers, especially the less commonly used ones, which saves about 1kb in memory if you're only using a few coroutines at a time.

    Also, I have some big news: I've been working on a MEC Pro package. The free version will keep all the core functionality that this version of MEC has, MEC Pro just adds new features to the existing infrastructure.

    Right now it has singleton coroutines (for when you only want a single instance of a coroutine), editor coroutine segments, a realtime update segment, and extension methods for corotines that allow you to append coroutines to each other, prepend, hijack (swap out return values), superimpose, etc. This allows you to do things like slow down one instance of a coroutine without rewriting any code, add a delegate on the fly that gets called when the coroutine finishes, and stuff like that.
     
    Last edited: Aug 24, 2016
    Alverik, EMGPY and AlteredPlanet like this.
  8. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Hi, I'm new to the asset (and to unity and programming...), and I was wondering if it's possible to have an integration with Thread ninja (free asset) or to add similar features to MEC? Theoretically this would result in not only faster coroutines but also the availability to use the background or foreground thread. So, it sounds like a golden combination.
     
  9. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    @Alverik There are any number of ways to paste multithreading tools into coroutines. That asset looks like it is accomplishing that goal in a relatively logical way. However, any attempt to do this sort of thing is, in my experience, just asking for trouble.

    As soon as you start multithreading you are asking for trouble. I'm not talking about those friendly little null ref exceptions that you're used to dealing with. I'm talking about the kind of bugs where your application randomly crashes for no reason in the middle of what you are doing and gives you no indication of the cause. Or the kind of bugs where you set a variable in your script and then a completely unrelated variable changes to a nonsense value on half of your target platforms.

    I've spent over a year of my life cleaning up after a group who implemented multithreading in an app without appreciating its downsides. The funny thing is that it's not even faster in most real world cases. Unity already multithreads it's render pipeline, it just tries to keep your scripting logic single threaded. It's being done for your own good, and I highly suggest you don't fight Unity on this point.

    I, for one, don't intend to.
     
  10. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    MEC Pro was approved today! I'll update the documentation this weekend and hopefully have time to make a video or two.

    So far I've found that Coroutines + extension functions are really powerful.
     
  11. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    New video, I'll be adding it to the asset page soon.
     
    Alverik likes this.
  12. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Hi, I had a question. Is it possible to verify if a tagged MEC coroutine is running or not? This has to do with the question I asked you in the other thread. I was using InvokeReapeating to call some functions used to deplete a meter. I was also using IsInvoking to verify if a specific function was running or not (to know if it was safe to kill it). Now I was trying to convert that into MEC coroutines, but I'm stuck cause I don't know how I'd know if a specific instance of a coroutine is running or not.

    the original code for was along the lines of this:
    (note: code is kinda old though, can make it cleaner now, I guess)

    Code (CSharp):
    1. if (!IsInvoking("HPLOSS"))
    2.                     {
    3.                         int currentHp = ... // check current hp here
    4.                         if (currentHp > 0)
    5.                         {
    6.                             _timeFrequecncy = ...// recheck the time frequency
    7.                             InvokeRepeating("HPLOSS", _ttdHp, _ttdHp);
    8.                         }
    9.                     }
    10.  
    11.                 }
    12.                 else
    13.                 {
    14.                     if (IsInvoking("HPLOSS"))
    15.                     {
    16.                         CancelInvoke("HPLOSS");
    17.                     }
    18.                 }

    Is there a way to do this or is there an alternative I can use? Also, if I pause a coroutine instead of killing it, what do I use to restart it? I didn't seem to see that in the manual.

    Anyway, any advice would be greatly appreciated.
     
    Last edited: Aug 15, 2016
  13. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    Hi Alverik. I don't know about whether it's necessary in invoke, but I can tell you that with MEC it is not necessary to check whether there are existing processes before attempting to KillCoroutines. If there are no matches then Kill already does that check and returns without doing any work.

    If you call PauseCoroutines that will pause all coroutines running on that Timing instance. I haven't built in a function to pause and resume a specific coroutine from the outside, although I think I'll put that on the todo list. If you're inside the coroutine there are several functions for "yield return Timing.WaitUntil" For instance, Timing.WaitUntilFalse will hold off your coroutine until a delegate that you pass in returns false.
     
    Alverik likes this.
  14. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Alright, cool to know. But about verifying the coroutine state, It's not so much that is needed, as much as I don't want to keep running the same code over and over again if it's not necessary (but I'll work around it with bools, I guess).

    Also is there any downside from using Kill compared to just Pausing a coroutine? Is it safe to kill several coroutines, short time later, restart them, and repeat.

    I.e: imagine I use the code to decrease the bars I mentioned, by default they would decrease the meters but if the player steps on a savepoint I want the behavior to get inverted, to slowly heal all the bars. But as usual, there's a chance that the player might step on it, move away, the come back, several times, which would kill and restart the coroutines several times... that's just an example though, I'll probably be needing to kill/pause the coroutines during cut scenes and at the beginning of battle... but I still wonder, will there be any impact?

    Edit: by the way, I just tried to use some of the example code in the manual but the "Timing.KillAllCoroutines("shout2")" throws me an error that there's no such parameter for it, instead it takes "Timing.KillCoroutines("tag");" as correct (which is different to the KillCoroutine without S which appears in the manual). Is the manual outdated or is it my visual studio freaking out?
     
    Last edited: Aug 15, 2016
  15. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    The manual needs to be updated. KillCoroutines(string) is right.

    KillCoroutines is quite fast, but memory is allocated whenever you create a new instance of a coroutine (which is slow and eventually triggers the garbage collector), so killing and recreating does have an overhead. However, it's not so huge that it would matter much unless you did it thousands of times per second. If you're triggering something in human time, like what you're describing, then it's perfectly fine to kill the coroutine and recreate it.

    Btw, Movement/Time is another asset I have in the asset store. It has a really clean way of performing that functionality you want for the health bar. You can define an effect that moves the health bar from point a to point b, and then if you store a handle to the effect you can make that effect run backwards by setting it's timescale to a negative number, and then make it run forwards again by setting the timescale positive.
     
    Last edited: Aug 16, 2016
    Alverik likes this.
  16. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Thanks, that's a load off my mind. About movement/Time it looks really cool, but I'm sort of dirt poor at the moment (and in the country I'm at right now, dirt poor really is dirt poor...).

    Anyway, hopefully my first game will start making me some income and I'll give it a try.

    Ah, I had another question. I have some code which I use to verify if the player is rubbing a pet. It uses a raycast to know if the player is rubbing the pet or not, it relies on a bool changed on OnMouseDrag and on OnMouseUp to know if the player is holding the pointer, it also checks if there's movement in one of the axis to know if it's moving (if the player stops moving it resets the counter). The player is awarded points one time per rub.

    Now, I have this running in the Update() and I was thinking of turning the whole process into a MEC coroutine(s), but I was wondering, would it be alright to use Slow Update for this? (which, by what I understand, is more performant.). I'll probably give it a try to see how it works, but I still would like to know if you think there might be problems in the long run.

    Anyways, as usual, any advice would be greatly appreciated.
     
    Trinary likes this.
  17. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    That will certainly run faster in a MEC coroutine since MEC has no context switching cost.

    SlowUpdate probably won't work well for that situation, though, because OnMouseDrag and OnMouseUp are events that are only fired on the specific frame that they happen. SlowUpdate skips frames, so it will miss most of those events.

    SlowUpdate is very much like if you always did "yield return Timing.WaitForSeconds(1f/7f);" except that all SlowUpdate coroutines are synchronized to fire together. This is mostly useful for text boxes, since text boxes look perfectly fine if they only change seven times a second so long as they all change together.
     
    Alverik likes this.
  18. Vegetables

    Vegetables

    Joined:
    Aug 15, 2016
    Posts:
    10
    Thanks for making this asset and giving it away free! 5 stars!
     
    Trinary and Alverik like this.
  19. ephemeral-life

    ephemeral-life

    Joined:
    Sep 12, 2015
    Posts:
    488
    I updated to the latest version today and I'd like to ask why you replaced the KillCoroutine method with KillCoroutines. To always be able to kill a coroutine?
     
  20. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    I changed the structure so that it's not searching through a list to find matches, it's now just looking up the matches in a dictionary, so it's a lot more efficient to just get all matches. The "first match" is just the one with the lowest hash so it would basically just be killing a random match. My thought is that I can't really see the scenario where a user would actually want to only kill a single random match but not kill other matches.

    Even if you're using an instance pointer, I think you want to be assured that if you accidentally started an instance more than once then all versions of that instance would be killed. But if you're using instance pointers I suggest you switch to tags. Tags are just better in almost every way.
     
    Alverik and ephemeral-life like this.
  21. ephemeral-life

    ephemeral-life

    Joined:
    Sep 12, 2015
    Posts:
    488
    Cheers. I remember reading about not using strings unless necessary so I'm trying to stay away from using any. I don't mind doing it like I do now, I was just curious why you removed the option to only kill one coroutine instead of continuing to look for more of the same "kind" when one is killed.

    Uploading a pic of an unexpected behaviour, if it's been explained before I apologize.

    Capture.JPG
    As you can see from the console WaitForSeconds doesn't work, but if I 'yield return 0f;' before using WaitForSeconds it works fine. Unwanted behaviour from first frame? Dunno if it's fixable.
     
  22. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Hey, I finally got to profile my game's alpha stages in an android smartphone! And It runs like crap, lol. But I'm not surprised, I haven't done any optimizations to the graphics yet (no atlasing or anything). Anyways, even so I'm happy :D, most of my slowdowns are from 79% CPU usage on the camera (from a random bloom I picked, and from a vignetting effect), and from like 900drawcalls occurring when the camera is close to the character and about 250 when he's far (haven't done any LODing yet anyways). Still, all code I made is using a tiny overhead and a practically continuous 0 GC! Anyways, thanks for such a great asset!
     
    Trinary likes this.
  23. mrleon

    mrleon

    Joined:
    Sep 27, 2014
    Posts:
    18
    Hi, I got Movement / Time in the past thinking it was the equivalent of the pro version of More Effective Coroutines because MEC is great, but M/T doesn't get updated much and uses an old version of MEC... and is a little incomplete (like not being able to kill movements) — anyways, now that there's an actual pro version of MEC — what's the relationship between the two assets? Will M/T always just use an older version of MEC free? Or will it be updated to use MEC pro?

    Thanks
     
  24. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    @ephemeral-life Can you double check that you actually have the latest please? It sounds like you are experiencing a bug that was in 1.7.0 and 1.7.1 but was fixed in 1.7.2.

    @mrleon Sorry, I have been neglecting Movement / Time. I plan to do an update to it next week that will include MEC Pro. There's a lot that I want to add to MoT but I get a lot more feedback and suggestions about MEC, and the feedback kind of drives my development process. I'll PM you a free copy of MEC Pro in the meantime.
     
  25. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    @ephemeral-life You are right that using string compares is usually quite inefficient. However, in this case the strings are being added to a dictionary and only being evaluated once per kill operation. The core execution loop completely ignores any tags. Since it's a lookup and not a search, using tags is actually far more efficient than using pointers.

    Strings become really inefficient when you search through a list of strings, or when you drive your processing based on the value of a string. Neither of those things are happening in this case, I can assure you.
     
    Alverik and ephemeral-life like this.
  26. ephemeral-life

    ephemeral-life

    Joined:
    Sep 12, 2015
    Posts:
    488
    I'm using v1.7.2.
    Code (CSharp):
    1. void Awake()
    2.     {
    3.         Timing.RunCoroutine(CRTesting());
    4.     }
    5.  
    6.  
    7.     private IEnumerator<float> CRTesting()
    8.     {
    9.         print("TESTING");
    10.         yield return Timing.WaitForSeconds(1.5f);
    11.         print("TESTING");
    12.     }
    That code snippet results in:
    Capture.JPG
     
  27. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    Ok, I'll look into it and see if I can reproduce the issue.
     
  28. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    I am unable to reproduce it on the latest version. Can you please delete the MEC plugin and reinstall to be absolutely sure you are on the latest version?

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using MovementEffects;
    4.  
    5. public class Testing4 : MonoBehaviour
    6. {
    7.     private int frameCounter;
    8.  
    9.     void Awake()
    10.     {
    11.         Timing.RunCoroutine(_CRTesting());
    12.     }
    13.  
    14.     void Update ()
    15.     {
    16.         frameCounter++;
    17.         if(frameCounter < 5 || frameCounter % 20 == 0)
    18.             Debug.Log("Frame " + frameCounter);
    19.     }
    20.  
    21.     IEnumerator<float> _CRTesting()
    22.     {
    23.         print("TESTING");
    24.         yield return Timing.WaitForSeconds(1.5f);
    25.         print("TESTING");
    26.     }
    27. }
    28.  
    Output:
    scrshot1.png
     
  29. ephemeral-life

    ephemeral-life

    Joined:
    Sep 12, 2015
    Posts:
    488
    Sure. I'll do that tomorrow and will report back. Thanks.
     
  30. ephemeral-life

    ephemeral-life

    Joined:
    Sep 12, 2015
    Posts:
    488
    I ended up running a test tonight and it worked after removing and importing the package again. Strange cause I opened the script and it said v.1.7.2, maybe wouldn't recompile to latest version? Thanks for the fast help.
     
    Trinary likes this.
  31. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    Maybe I sent you an earlier draft of that version, or there could have been a merge error. In any case, I'm glad it was a relatively easy fix.

    Let me know if you have any other problems with time. I switched over to keeping track of time semi-independently of Unity's Time class in order to have a deltaTime in SlowUpdate and EditorUpdate, and also to provide a solution for apps that run for several days at a time.
     
    ephemeral-life likes this.
  32. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Hi, I know this is sort of out context, but how do you perform better searches in a list or dictionary, like you guys mentioned?. Right now, I'm using some dictionaries<string,int> to keep some item ids (the dict is relatively empty for now, but will likely get very big). Anyway, I've been using the .containsKey method to search a string against the string Keys inside the dictionary. Supposedly that method is good, but I've heard from other people that using strings for searches is bad. Would anyone please explain this to me? and if possible give me a basic example of how to work around it? that is, if you guys have the time.
     
  33. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    Well, in your case the answer is "it depends on how you use it."

    Code (CSharp):
    1.     public Dictionary<string, System.Action> dict = new Dictionary<string, System.Action>();
    2.  
    3.     public void ExecuteNamedFunction(string function)
    4.     {
    5.         dict[function]();
    6.     }
    7.  
    The above code will be far slower than a function call for two reasons.
    1. Every time you make a function call you will have to instantiate a new string in order to tell where exactly it should look.
    2. .NET will normally pre-load and optimize many aspects of the function call, and this structure breaks all that.
    It's that first point that makes many people (rightly) fear using strings. A string has a variable size, but in most cases strings use more memory than any other built in data type, and more bytes in memory directly correlates to more milliseconds per operation. I should also add that magic strings break the compiler's ability to check your code, so if you forgot to capitalize your function in the above example it would just fail. (MEC tags have that problem as well :-/ )

    The speed is variable, but I'm just going to throw out as a ballpark that the above code will take about 40x as long to execute the function as calling it directly would have. So if you were doing this every second or every time the user pressed a button then the speed difference would hardly matter, but if you did this in your update loop then it would slow down your application quite a lot.

    In Unity you can do a SendMessage command, which is at least as slow for the same reasons: In order to send a message you have to create the string that matches what you are looking for and Unity has to search through the classes in the scene to find a matching function.

    But to answer your question directly: The way you are using it is pretty good. A list would perform a search, but a dictionary just performs a hash lookup.
     
    ephemeral-life and Alverik like this.
  34. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Alright, thanks! :D
     
  35. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    v1.8.1 came out today. It contains some process improvements for MEC free that should result in a little bit of a speed up. Documentation was also updated.

    Pro gained the AddDelay extensions method, which is awesome, and the ability to pause and resume tagged coroutines. I've added a section explaining it on my website: http://trinary.tech/category/mec/mec-pro/
     
  36. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Great, thanks! I really wanted to read the updated documentations. There's some really cool stuff here :)
     
    Trinary likes this.
  37. 1point21gigabytes

    1point21gigabytes

    Joined:
    Aug 24, 2016
    Posts:
    2
    The latest version of MEC removed the WaitUntilTrue and WaitUntilFalse functions, which broke some of my code. I had to roll back to 1.7.2. It doesn't look like the names were changed; the functions are just missing.
     
  38. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    @1point21gigabytes I'm sorry to hear that that change affected you. I'm trying to keep the MEC core lean and also fully functional for the most common uses cases of coroutines. WaitUntilTrue and WaitUntilFalse didn't seem like core features. They seem more like the kind of extended "power user" type of features that belong in the Pro version.

    Feel free to copy those three functions forward into the new version when you update. They will work just as well in 1.8.1 as they did in 1.7.2.
     
  39. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Hi, I have a question. It may be something easily solvable with a little math I guess (but I've forgotten most of it...). Anyway, how would you go about doing some very basic smoothing/lerping of values while using MEC coroutines (or any coroutine really)?

    Currently I was using DotTween for smoothing the depletion of a bar/meter. Problem is, I also have items which can heal you while you are losing points, and sometimes when both happen at the same time the values get overwritten, basically, completely cut short by DotTween's smoothing.

    I actually added a global pause bool for the code to know when to deplete the bar or not, and I change it right before starting the tweening, then reset it in the following line, but the tweening seems to live separately, similar to a coroutine, and the hiccups still occur. Thing is, unlike a coroutine, I have no clue how to change it's behaviour.

    I'm actually using a DoTween method which calls a separate custom method (like a delegate, but just adding +1 to the gVar), and I could do the pausing in there, but the code is going to end up pausing and unpausing the depletion hundreds if not thousands of times... which I feel is a waste. So I was thinking of turning the process into MEC coroutines (I use them everywhere now), but if I do that I'd end up with linear increments. I imagine I can tinker with Timinig.WaitForSecond to achieve the desired result but some ideas will be greatly appreciated!
     
  40. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    You're right: As much as I hate to even say the word "tween" they are basically just a higher level way of using coroutines. I would suggest dropping the idea of a global pause, that's always going to end up quirky. I think there's a flag in DOTween to not use pre-cached start and end values, which would allow you to apply more than one tween to the health bar at once, but you would still see jarring changes in speed at times.

    In MoT you could just apply some inertia to the value and set it back and forth arbitrarily, which is what I think you want. It's pretty easy to set that up with a coroutine as well. Something like this should work:
    Code (CSharp):
    1.     public float actualValue;
    2.     private float displayValue;
    3.  
    4.     public void AddHealth(float change)
    5.     {
    6.         actualValue += change;
    7.         Timing.RunCoroutineSingleton(_UpdateDisplay(), "HealthBarUpdate");
    8.     }
    9.  
    10.     private IEnumerator<float> _UpdateDisplay()
    11.     {
    12.         float drag = 0.1f;
    13.  
    14.         while(!Mathf.Approximately(actualValue, displayValue))
    15.         {
    16.             displayValue = Mathf.Lerp(displayValue, actualValue, drag * Timing.DeltaTime);
    17.             // Update the UI element to show "displayValue" here.
    18.             yield return 0f;
    19.         }
    20.     }
    21.  
    You'll have to play with the "drag" variable until it works. It depends on your units. Don't use WaitForSeconds, you need the display to be updated every frame to make things look smooth.
     
    Last edited: Aug 24, 2016
    Alverik likes this.
  41. 1point21gigabytes

    1point21gigabytes

    Joined:
    Aug 24, 2016
    Posts:
    2
    @Trinary Thanks for the comment. Good to know that the 1.7.2 functions will work correctly with the 1.8.1 plugin. I will copy them forward as you suggested.

    I don't agree with the rationale to remove them though. If they were exclusively in MEC Pro from the beginning, that would be one thing, but they were already in the free version and their removal diminishes the free version's capabilities. Also, it's not like they're obscure; you yourself mentioned WaitUntilFalse in your August 15 post above.
     
  42. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Ooh, thanks! I'll give that a try! the only thing is, I had though of using the Pause variable because I have a script that is constantly removing points from the player's meters, which can also be used sort of like poison (in case of Health points). Would I be able to keep both effects at the same time if I add and remove points using this method? Seeing how that's a singleton, wouldn't using a health potion while poisoned get canceled, because the coroutine would already be active removing points from the player?

    Anyways, as usual, any advice would be greatly appreciated :)
     
  43. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    345
    Think of the UpdateDisplay function as a task that continuously moves the value that is displayed by the health bar a little bit closer to the actual value. The UpdateDisplay task will quit when there's no more work to be done, and RunCoroutineSingleton just insures that there is an instance of that task running.

    Btw, anyone who is using MEC free can replace RunCoroutineSingleton with a KillCoroutine then a RunCoroutine and it will work nearly the same. Also, I didn't put it in there, but it might be a good idea to set displayValue equal to actualValue after the while loop.
     
    Alverik likes this.
  44. TiberiuMunteanu

    TiberiuMunteanu

    Joined:
    May 9, 2015
    Posts:
    18
    Hi,

    I purchased the pro asset beeing excited about the 0 GC allocations in the asset description. Then in the docs you say:

    "Q: Does MEC completely remove GC allocs?"
    "A: No. MEC removes all per-frame GC allocs. (unless you allocate memory on the heap inside of your coroutine, but MEC has no control over that.) When a coroutine is first created the function pointer and any variables you pass into it are put on the heap and eventually have to be cleaned up by the garbage collector. This unavoidable allocation happens in both Unity’s coroutines and MEC coroutines."

    So what I understood by reading this forum is that the problem with unity coroutines was that the per frame allocations were all because of the "new WaitForSeconds(1)" objects? Did I get it right? If that is the case, I already had that problem solved with a static class that kept a dictionary of objects like this. And I never used "yield return new WaitForSeconds(1)", but "yield return Yielders.Get(1)"..which allocates memory only the first time I requested an object like this for a specific float in the entire project. All subsequent requests for a wait for 1 second would serve me the existing object by searching for it in a static dictionary (isn't that similar to your Timing.WaitForSeconds(1)?).
    So the only allocation was that function pointer you create when you start a coroutine (so I think...maybe I'm missing something). And now you say you have it too (I understant that it is not in your control since that is how .net works but maybe there are workarounds?).

    So I was kinda dissapointed to have purchased the asset and see that it's not actually 0 GC allocs but I ended up where I already was.
    Don't get me wrong, even if my understanding of this is correct, I don't regret buying the asset, because it comes with some pretty neat stuff.. and I will continue using it. All I want is to better understand the magnitude of the problem with that function pointer alloc, and if there are ways to go around it (by cache the function pointers or something).

    I have some scenarios which I'm not entirely sure if I'm doing right with your asset:

    1. I have a behaviour which starts a coroutine at some point, but stops an existing and currently running one if it's the case. I'm doing this by caching a reference (handle?) to the function when i start the coroutine. Before actually starting it, I check if the handle is not null and use the KillCoroutines() on it. Is this the right way? Is there a better one?

    2. I pool a lot of objects. So on poolable objects I have a Release() method for cleanup and resseting stuff. I usualy used to add StopAllCoroutines() in there... which stopped all running coroutines ON THAT BEHAVIOUR. How would I do this with your asset?

    PS: Sorry for the long post!
     
    Trinary likes this.
  45. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Thanks, I think I understand now, but I have a problem, how would I apply this to intergers too? For Hp and Mp I have both a meter and a numerical value (and just a numerical value for combats stats), but when there's a change or a level up you can see the numbers increment (or decrement when hurt). Can Mathf.Lerp be used with int ranges? or else, how'd I'd get a similar behaviour using a coroutine?

    Edit: Well, I'll try converting it and see if what happens.
     
    Last edited: Aug 25, 2016
  46. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    181
    Just purchased the pro version! Wanted to support Trinary. You are an awesome person/dev. Please don't burn your self out and get jaded!
     
  47. TiberiuMunteanu

    TiberiuMunteanu

    Joined:
    May 9, 2015
    Posts:
    18
    3. Let's say I have an Enemy unit and it contains some run/kill coroutines logic inside it. You recommend using tags. Ok.. I run a coroutine and give it a tag like "do stuff". Then I kill it by doing KillCoroutines("do stuff"). This would work well and all given I only use one enemy. From what I understand, the timing manager is independent of specific behaviours, so if I have 10 enemies in the scene, when I kill one's "do stuff" coroutine, it would kill all "do stuff" coroutines for all the enemies. Now, I don't want to start managing the uniqueness of the tags myself by dong smth like "do stuff - enemy2".. that seems like alot of stress and increase in codebase.

    I kinda liked the StopAllCoroutines() of unity coroutines implementation because it only killed the ones on that specific behaviour, and I didn't have to worry about this problem.
    Someone here suggested to use the GetInstanceID().ToString() as the tag... This could be a good thing.. imho.
    Maybe all the coroutines defined with no specific tag could automatically have the GetInstanceID().ToString() tag, and a "KillCoroutinesOnThisBehaviour()" method would kill all coroutines with the tag GetInstanceID().ToString().
    What do you think?
     
  48. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Well, I have some cases where I want some things to be checked automatically after an action has occurred. For example if the player clicks and holds over the character that launches a process(also a coroutine) which changes the camera and some menus if a condition is met. Once that's done, in the last lines I launch a new coroutine, which using a do while with an if statement inside (and a Timing.WaitforSeconds), starts to check the state of the menu every some time. Once it detects the menu is closed it automatically changes back to the previous camera and UI and then terminates (by ending the loop). I do all that without ever using a tag. I only use tags when I want to kill groups of coroutines at a time, and that's probably what it was for anyways...

    If you use loops inside your coroutines (in the Update,Slow Update, or Fixed Update Segments), you can easily have a cheap replacement for the Update() function,and you can put conditions for terminating right inside of them. A simple do while works great that way, once you reach the end path you can do your stuff and then instantly end the coroutine by just finalizing the loop.
     
    Last edited: Aug 26, 2016
    Trinary likes this.
  49. TiberiuMunteanu

    TiberiuMunteanu

    Joined:
    May 9, 2015
    Posts:
    18
    So in short.. is it better to minimize the use of "kill coroutines" in favor of "loops inside coroutines with an end condition"?
    My scenarios are:

    1. more like "resetting" a coroutine... as in.. if it is currently running.. abort and start over.
    2. when sending an object back to the pool, abort all coroutines that might be running on it.

    What are the best way of doing these?
     
  50. Alverik

    Alverik

    Joined:
    Apr 15, 2016
    Posts:
    391
    Well I use WaitForSeconds every time a condition is not met and have the coroutine continue after a specified delay to save processing, and it works great but obviously it will depend on the scenario. I'm not yet an expert or anything, there may be much better ways to do it. Trinary and other's may have better ideas for your case.

    Edit: Still, if its something like when an enemy is killed and the respawned later as a "new" enemy, couldn't you just add a call for the coroutine on OnEnable? that way you can safely terminate the coroutine when the enemy "dies", and it will immediately restart when he is enabled again?
     
    Last edited: Aug 25, 2016