Search Unity

[FREE] More Effective Coroutines

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

  1. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    If you pass three variables into the WaitUntilDone function then the third variable is the instance that it should use. I guess that's a bit of a departure from how the other ones are defined.

    There are also a few other options that you may not have considered:
    #1 you can set the Instance variable to another instance of the Timing class yourself and everything will continue to run fine.
    #2 If you add a GameObject with the Timing class on it to a scene, or if there is a Timing instance already in the scene from a previous scene then MEC will just use the existing instance without creating a new one.
    #3 The second parameter in WaitUntilDone is whether that warning should display. In this case you might be able to safely just set warnIfNotFound to false, since if the scene changes I don't think you even need the coroutine to continue.
    #4 You can call DontDestroyOnLoad(Timing.Instance.gameObject); to make all the default coroutines keep going between scenes.
     
    fairchild670 likes this.
  2. fairchild670

    fairchild670

    Joined:
    Dec 3, 2012
    Posts:
    69
    Fantastic info (can't believe I missed the args there)! But thanks once again!
     
    Trinary likes this.
  3. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Hey all, big things coming out soon: I'm changing the name of the asset to "More Effective Coroutines."

    Why, do you ask? Well I've been doing some performance testing and I've found that MEC runs about twice as fast as Unity's coroutines. Also, I added an EditorUpdate timing segment. So at this point they've kind of grown beyond being just memory efficient.
     
    PhoenixRising1 likes this.
  4. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    V 1.6 is out, with all that stuff I mentioned in the post above. Also, I attached a video going over the performance comparison (Link for the lazy).

    I do need to make a new video soon for Unity 5.4 (spoiler: Unity's per frame memory issue is fixed, MEC still runs twice as fast.)
     
    idurvesh and FuguFirecracker like this.
  5. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    So I deleted the previous video and uploaded a much better edited and more informative version here. I also improved the exception handling so that it doesn't appear as though MEC is the one throwing the exceptions.
     
    idurvesh and PhoenixRising1 like this.
  6. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Hi I was wondering what the proper process to setup my own instance. The instance is on an object that is set to DontDestroyOnLoad, but I still want Timing.Instance point to the default one that is usually created. I was setting up a scene fade with MEC only to have other parts start throwing null reference exceptions. I only want a select few things using the persistent instance, while everything else uses the default one.

    Essentially I am adding the Timing script to the object through the editor. Once initialized the object then calls DontDestroyOnLoad() then sets Timing.Instance = null;

    I figured it would sit null until I call Timing.RunCoroutine() somewhere else but still be able to use the instance I have on the persistent object without problems. It seems to work, I want to make sure there isn't another way I should be doing it.
     
  7. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Chris,

    That's exactly why I made the Timing.Instance variable set-able, and designed it with a defensive structure that loads on demand like that. What you are doing sounds great, keep it up!
     
  8. Chris-Trueman

    Chris-Trueman

    Joined:
    Oct 10, 2014
    Posts:
    1,261
    Thanks for the quick reply. I thought that's why you set it up like that.
     
    Trinary likes this.
  9. Gekigengar

    Gekigengar

    Joined:
    Jan 20, 2013
    Posts:
    738
    Hello, I noticed it is no longer "More Efficient Coroutines", does this mean this is less efficient than Unity's Coroutine now?
     
    ZJP likes this.
  10. PhoenixRising1

    PhoenixRising1

    Joined:
    Sep 12, 2015
    Posts:
    488
    No :). It does benefit more than memory hence the change of name.

    Edit: misread it.
     
    Trinary likes this.
  11. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    MEC is still more memory efficient. However, Unity did make things slightly better recently, so now if you limit yourself to only ever using the regular update segment and always yield return null then you can run Unity's coroutines with zero per-frame allocs as well.

    This asset has been quite popular (500 - 800 downloads per month). Unity has access to those numbers and I think this asset is showing them that people really want more memory efficient coroutines. So I think it's safe to assume that someone at Unity is working on the problem right now. (If someone at Unity is reading this then please contact me, I'd love to talk.)

    Also, a lot of people start using MEC in order to decrease their GC allocs but end up getting hooked on the extra features. MEC is about twice as fast as Unity coroutines and offers a larger number of timing segments. I'm just trying to get away from the idea that MEC is just about memory allocs. Maybe that's a bad move for marketing. Maybe a single clear value proposition would bring in more users.. but hey, it's a free asset.

    What I really want to use MEC for is as a tool that improves the lives of the greatest number of people possible that are feeling frustration with this aspect of Unity. Memory usage was the biggest sticking point, but now that that has improved somewhat in the new version of Unity, it is no longer vastly more important than the other advantages MEC has, like editor corutines, the SlowUpdate segment, the ability to set up coroutine groups, and the ability to define custom exception handling. I want to bring those aspects a little more to mind.
     
  12. EliasMasche

    EliasMasche

    Joined:
    Jul 11, 2014
    Posts:
    92
    Congrats in your updates of MEC, question, looking in forums and github i found this library UniRx(Reactive Extensions) that is a reimplementation of the .NET Reactive Extensions. The Official Rx implementation is great but doesn't work on Unity for network operations in asynchronous operations use WWW and Coroutines, i trying to test is are compatible MEC & UniRx but limited time not posible to do, think that is going to improve your perfomance and productivity, or is going to be useless.
     
  13. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    I've used UniRx. I like the technology, and I think it's well built. It uses a different development philosophy than I do though, which makes it hard to upgrade.

    As you may have noticed, MEC doesn't use any custom editor scripts. MEC is also built using a single class that resides in a single file which is capable of performing the entire job of managing coroutines by itself. I didn't put things together this way because I'm afraid of editor scripting or breaking things up into separate classes. I develop the way I do because I don't believe in complexity in the underlying systems unless it serves a purpose and/or provides a real tangible benefit to the user. I have to admit that the coroutine system would look a lot more complex if I were to break it up into 20 different files with 20 different classes that sat in 5 different directories, I just can't operate in that kind of clutter unless it serves a tangible purpose.

    Unfortunately, UniRx is built with a different philosophy, one that breaks the framework up into something like 100 classes in something like 100 different files. I tried digging through it once, and I don't intend to be trying that again.
     
  14. EliasMasche

    EliasMasche

    Joined:
    Jul 11, 2014
    Posts:
    92
    Thanks for the opinion and info about UniRx, well is too good to be real, haha, so i going to stay with MEC, is working great and improve a lot the usage of Coroutines in Unity, and makes me happy to work with then somethink that i don't like, that want the async operations of C#6 or alternatives of multiples threads for unity
     
  15. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Ohh.. I didn't know you were trying to compare them directly. UniRx is not an alternative to MEC. It's a different framework that is designed to solve a different set of problems than MEC.

    UniRx is all about controlling the timing of data streams, which makes it great for controlling things like input devices and network traffic. It could also be used for sorting lists, and if you really like a fluid interface UniRx can turn almost any data-processing function into a one line operation (a very long one line, but one line nontheless). If that kind of stuff appeals to you, then you should use UniRx.

    MEC doesn't attempt to do any of those things. It's for a different problem set. I suggest using a technology if it solves problems that you have, and don't use it if you don't have those problems. There's no reason why you can't use both either, MEC and UniRx don't interfere with each other.
     
  16. EliasMasche

    EliasMasche

    Joined:
    Jul 11, 2014
    Posts:
    92
    i wasn't comparing directly, i ask bad sorry for confusion, my real question: is posible to use together MEC with UniRx or there is going to be problems like complaining each other, but in your third paragraph tells all i need, but thanks again for extra info about UniRx and MEC.
     
  17. Emre_U

    Emre_U

    Joined:
    Jan 27, 2015
    Posts:
    49
    Because it is free, the least I can do is thank you here! Awesome stuff Trinary! Welldone.
     
    Trinary likes this.
  18. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    Thank you, for this great asset. I just tried it out for the first time. Works great. I used it to make burst fire shooting.

    One question regarding triggering the same MEC multiple times. Lets say I trigger a MEC twice;

    Timing.RunCoroutine(Shoot());
    Timing.RunCoroutine(Shoot());

    Does this mean I started 2 instances of the same Shoot() MEC?

    Thanks,
     
    Trinary likes this.
  19. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    giraffe1,

    That's right. That will make two separate instances of the same coroutine. If it was like this then they would both be able to shoot:

    Timing.RunCoroutine(_Shoot(Npc1));
    Timing.RunCoroutine(_Shoot(Npc2));

    I think it's good practice to put an underscore before all coroutine functions. It's just nice to know by looking at the function that it's a coroutine function, since every coroutine function (Unity and MEC) will break if you forget to wrap it in the RunCoroutine statement.
     
    hopeful likes this.
  20. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    Thanks, I made the change you suggested.

    Each AI uses this logic.

    I am worried each time the AI is calling the _MultipleShoot MEC a new instance is created. Can you suggest a good way to be able to call and run _MultipleShoot MEC multiple times, but to make sure only 1 instance of it ever exists per AI?

    Code (CSharp):
    1. private void Update()
    2.     {
    3.         if (isShootingBool && _currentAmmo > 0)
    4.         {
    5.             PreShoot();
    6.         }
    7.     }
    Code (CSharp):
    1.  private void PreShoot()
    2.     {
    3.         if (!inShootAnimation)
    4.         {
    5.             inShootAnimation = true;
    6.  
    7.             if (gunShootType == GunShootType.Pistol)
    8.             {
    9.                 Shoot();
    10.             }
    11.  
    12.             else if (gunShootType == GunShootType.Rifle)
    13.             {
    14.                 Timing.CallDelayed(timeToDelayFirstShot, delegate { Timing.RunCoroutine(_MultipleShoot(Time.time, totalShootTime)); });
    15.             }
    16.         }
    17.     }
    Code (CSharp):
    1. IEnumerator<float> _MultipleShoot(float startTime, float shootTime)
    2.     {
    3.         while (inShootAnimation && Time.time - startTime < shootTime)
    4.         {
    5.             Shoot();
    6.  
    7.             yield return Timing.WaitForSeconds(timeBetweenBullets);
    8.         }
    9.     }
    Code (CSharp):
    1. private void Shoot()
    2.     {
    3.         // 1 shot, do stuff!
    4.     }
     
    Last edited: Jun 25, 2016
  21. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    I like that you are experimenting with multiple ways to use coroutines. Since we're talking about efficiency, I would avoid checking the value of a boolean in the Update loop and performing an action when that value is true. I would look at any code that sets your isShootingBool to true and just start the coroutine there instead.

    Your inShootAnimation boolean should stop multiple instances from being created. Don't forget to set the boolean to false at the end of the coroutine. Also, it's a small matter, but I wouldn't pass in startTime into the coroutine. You can easily make that a private variable inside the coroutine function itself. Every variable that you pass into a coroutine ends up being a GC alloc.

    A MEC coroutine only costs 40 bites plus the size of any variables you pass in, so it really isn't much overhead to create a lot of coroutines if you want to. (Unity's create 80 bites plus any variables) Demo scene #3 of my Movement/Time package runs 2,500 MEC coroutines at the same time (continuously swapping out old ones with new ones) while still maintaining 80-90fps on my laptop.
     
    hopeful and giraffe1 like this.
  22. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    Thank you very much for your input even though it is beyond the scope of your asset and more of a general C# question.

    Really appreciate the advice, I will make the changes you suggested.

    Thank you again!
     
    Trinary likes this.
  23. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    You're welcome. It's actually really good to hear from users. Considering how many people are using MEC on a daily basis, I don't really get a whole lot of questions about it anymore. I take that as a sign that it's just quietly working for most people, but I still like the feedback and the interaction with the Unity community.
     
    isidro02139 and Emre_U like this.
  24. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    No problem.

    I just now noticed the 'Movement Effects' game object that gets automatically created and shows the exact number of active instances of MEC coroutines running.

    That really helped me understand how default MEC coroutines behave. I didn't realize that MEC coroutines automatically kill themselves after they are done.

    Seems like I don't even need to use the yield break statement because it seems to self maintain itself and kill itself after it is done or if my while condition returns false.

    Great stuff man. Your asset should just replace Unity coroutines. They behave so nicely!

     
  25. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    I guess MS thought that it wouldn't be different enough if you were allowed to do "yield return 0" and "return" in the same function, so they changed the return statement to "yield break" for IEnumerator functions. It's for when you want to quit the coroutine function in the middle. I should probably reword the documentation to make all that a little more clear.
     
  26. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @Trinary
    Hey I was wondering if I want to use Timing.CallPeriodically is there a way to interrupt/kill it?

    Oh and also I noticed CallPeriodically doesn't really work as I'd expect it, and it's because of that yield return period; line
    When I changed it to Time.time + period it works fine as stated in the documentation. Give it a look.

    Cheers
     
    Last edited: Jun 28, 2016
  27. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    You're right! How did that get through qa? I'll fix it in the next version, and there's no reason why that function couldn't return the IEnumerator as well.
     
  28. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Actually, if you want to stop it you'll have to modify the file or copy it yourself. My testing indicates that those functions look too much like coroutine functions if they return their own IEnumerator<float> value.
     
  29. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    Hey so if I want to use KillCoroutine on a coroutine that has a yield return Timing.WaitUntilDone(otherCoroutine)
    Will it also kill the otherCoroutine inside it if it's already started?

    Cheers
     
  30. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    No, it's designed to only kill the one that you passed in the handle for. If you call wait then the coroutine does get put into a different buffer, but KillCoroutine searches through the waiting buffer as well.

    If you're seeing a behavior that's different then I definitely want to know.
     
  31. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    v1.7.0 is out!

    The major new feature is that coroutines can now be tagged with a string. That string will then be stored along with the coroutine for as long as it lives. The kill functions no longer do a search through the entire list, they can now do a simple dictionary lookup on the string. I'm leaving the kill via pointer functionality in there for now, but I plan to mark it as deprecated soon.

    If you want to kill coroutines, tags are now the best way. You can assign a bunch of coroutines a unique tag and kill them one by one, or you can assign a group of them the same tag and kill the entire group.
     
  32. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @Trinary
    Great update!

    I really like the Tags feature, but I have a suggestion. How about making another dictionary where the tags are ints? So this will work nicely with GetInstanceID() which always gives a unique int for every instance, so this way you could pair coroutines to specific script instances and you can kill all coroutines on that instance, instead of needing a local Timing manager for that instance.

    You can do this with the strings as well, by maybe caching the GetInstanceID().ToString() in Awake, but why go the extra mile? You have to add a field then remember to assign it in Awake, every time you want to do this.

    Cheers
     
    Trinary likes this.
  33. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    Also in the latest Unity 5.3.6f1 update the release notes state they have fixed the GC for coroutine iteration.

    Have you tried it out, and what do you think about this?
     
  34. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Thank you for the suggestion. I haven't thought about using instance ids. There are a lot of concerns that I'm trying to juggle that made me want to go with string based tags. Dictionaries aren't as fast as arrays, so I don't want them to be used in the core execution pump. As I have it now the memory for the tag is not used if you don't set one. If it was the instance id of the calling class then the temptation would be to automatically preserve that info on every call to run.

    Also, I think string based tags can be used in more ways than just for kill statements. I'm working on some cool new features now.

    As for the note from the release notes, good for Unity! That's exactly the same wording as I found in the 5.4 beta a while ago, so I'm assuming it's the same change that I go over in the video that's attached to my asset (and linked to in a previous post in this thread). I'll take a closer look when I get a chance and make sure my assumption isn't flawed (you never know).

    Unity could eventually get close to the speed of MEC if it stopped using unmanaged code in its execution pump and stopped using a generic IEnumerator, but I don't think their architecture allows them to do either of those things.
     
  35. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    I have a major bug that I suspect is caused by Unity's crappy co-routines implementation - I have a lot of them, and performance is very poor, but in profiler it shows up as perfect (lies! :)).

    I heard good things about MEC, so I tried integrating it today, but ... this is not a compatible change. The switch of signature on coroutine methods is a deal-breaker - there's no way I can afford to rewrite an entire game, and all the coroutine-related libraries, to switch to your proprietary protocol :(. I have extensive use of the different kinds of unity-coroutine too, which I guess are incompatible with this (the docs call-out a few of the mainstream ones, like WaitForSeconds, but there's now loads of these, especailly since they're now user-definable).

    Is there a workaround, or a plan to make MEC compatible with Unity coroutines?

    Thanks!
     
  36. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @Trinary

    Hey, so just to clarify, I didn't mean that the instance ID int should be automatically used whenever you Run a Coroutine. But to optionally be able to provide an int instead of a string for the tag version. So it would essentially be the same. If I understand correctly how you implemented it.
     
  37. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    @a436t4ataf

    Do you mean that you're using Custom Yield Instructions that don't work with MEC?
     
  38. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Hello string of random numbers. Are you the person who sent me an email through my website while supplying a fake reply email?

    Regardlessly, it sounds like you are having a frustrating day. I suggest you take a deep breath. Maybe go on a walk around the block.

    Switching requires using find and replace. MEC doesn't disable Unity's coroutines, so you can switch over little by little of you like. All the changes to the signature are made to either not cause a conflict or increase efficiency.
     
    isidro02139 and giraffe1 like this.
  39. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    That made my day!
     
  40. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    My co-routines are chained. To change "one" means changing 20 or more methods.

    Your change to the method signature = and, note, your docs say you have changed *the meaning* of the return values - requires rewriting all surrounding code and checking what needs to change to make it compatible with the different data, the different definition, and the different datatype.

    So, as far as I can tell:
    1. No, I cannot "switch over little by little". The entire codebase would break if I tried to change only one signature.
    2. No, find and replace won't fix anything: the code is fundamentally different in meaning with your change.

    ?
     
  41. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    MEC docs suggest they won't work - I'm looking for a clear answer on this :).
     
  42. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Ok, MEC specifically returns a value type because coroutines are the kind of thing that is executed continuously every frame and value type variables are faster. I think I'm starting to understand what you have done, and it sounds like a very strange use case. Can you explain to me what specific problem you are trying to solve?

    MEC coroutines cannot be used to return generic data from the yield return statement. Returning data has to be accomplished by using a shared variable or a delegate. If you have a use case for something other than returning data then it is something that I've never heard of.
     
  43. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    No, I am using coroutines exactly as specified by Unity.

    Off the top of my head...

    Coroutines must start with a method that was originally written containing one of the triggers that cause the C# compiler to remove your method from your class, and compile it as a special pseudo-method. The trigger (AIUI) is that the method must contain a yield statement and must return IEnumerator. If you remove all your yields, it gets compiled differently. If you remove IEnumerator, IIRC it won't compile.

    Given all this, the IEnumerator also - specifically in Unity - has special meaning. It is something that must execute one "Chunk" of coroutine work each time it's iterated.

    The actual object returned can be any of many special magic classes Unity wrote that have special meaning in how/when they are iterated. This is how WaitForSeconds etc is implemented.

    You can write your own wrapper that executes coroutines in Unity without using StartCoroutine (I assuemd this is what MEC was? Maybe not). It must, however, interpret the IEnumerator instances correctly and check each possible class/type to establish what rules to use for execution.

    NB: I've successfully done this before. We wrote our own "coroutines in the Editor" class that works perfectly.

    Recently Unity also added a mechanism by which you could explicitly add to the set of magic classes that implement IEnumerator, and Unity promises to honor those classes, executing them correctly even though it's code Unity has never seen.

    ...

    From the docs, and from what you've written here, it sounds like you're incompatible with some of the above?
     
  44. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Yes, MEC is an execution pump that does the same basic operation that Unity does.

    Do you have a link to this mechanism you are talking about? Where did you hear about it?
     
  45. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Unity docs + Unity official blog + Unity release notes.
     
  46. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    So that's a no?

    If you don't want to use this free resource, feel free to request a refund.

    It feels like you are trying to be difficult by purposefully not explaining yourself.
     
    Last edited: Jul 28, 2016
    guzzo and giraffe1 like this.
  47. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    You don't have access to Unity docs?

    If you're not using / haven't seen the new features, probably best if I write a test rig for them myself, as I'm using coroutines aggressively, and have a lot of gamecode to check with.

    Any recommendations on how/where you improved performance vs stock Unity? Are there any perf benefits that are still possible even if you restrict to not changing the method signature?
     
  48. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    Listen, random number dude: you've vaguely described something using terms like "magic classes" and "unity promises". When asked to produce a specific link to what you are talking about you seem unable to, you instead reply with "it's somewhere in the entire body of documentation that unity has released."

    This doesn't seem like a dialog. It seems like you're just trolling on a throwaway account.
     
    PhoenixRising1 and guzzo like this.
  49. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    It's certainly a poor dialog. I keep asking simple questions about your asset. You keep being rude and aggressive. Your asset does some wrong things (changing method signature) - which might be OK (maybe Unity's code is safe with this?) but you don't seem to care. You seem to have missed the most important feature change for the past 3 or 4 years in this part of Unity, that was announced - off the top of my head - 6 months ago, featured widely, but again: you don't seem to care.

    Thanks for your time, but I'm moving on. Judging by your aggression, your non-answers, and your ignorance of the code you're replacing / interfacing with ... using this asset would be a bad idea.
     
  50. Shadeless

    Shadeless

    Joined:
    Jul 22, 2013
    Posts:
    136
    Sounds awfully like someone who has no idea what they're talking about... Sounds like Trump! Ayyy!

    The Unity documentation is thousands of pages long and all the release/patch notes have thousands of lines among them. I'm sure everyone here and the developer of the asset would love to help you if only you provided some examples or links to the things that you're referring to.

    The fault is on you for failing to provide the information so that anyone can help you...
     
    isidro02139, Hodgson_SDAS and Trinary like this.