Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

MonoBehaviour and messages

Discussion in 'Experimental Scripting Previews' started by bdovaz, Feb 19, 2017.

  1. bdovaz

    bdovaz

    Joined:
    Dec 10, 2011
    Posts:
    1,049
    Have you discussed internally how to improve this? I mean when your released tu new UI System you where smart enough to do it the right way, with interfaces. So if you want to "listen" to clicks you do this:

    Code (CSharp):
    1. class Test : IPointerClickHandler {
    2.     public void OnPointerClick(PointerEventData eventData) {}
    3. }
    But what about MonoBehaviour (and not only MonoBehaviour) messages that are sent "magically"? Have you discussed to do it similarly?

    Code (CSharp):
    1. class Test : IAwake, IStart {
    2.     public void Awake() {}
    3.     public void Start() {}
    4. }
    Apart from performance perspective or that it's not the best way to do it I have the following problem testing the code quality: https://jira.sonarsource.com/browse/SONARCS-677

    Also making unit testing you can't call directly this methods and you need to do it via reflection.

    I Know that it's not a easy matter because you should do it via API Updater carefully do developers that don't want to upgrade their code (or can't like asset store old packages) manually can do it automatically.
     
    sand_lantern and MMOARgames like this.
  2. GabrieleUnity

    GabrieleUnity

    Unity Technologies

    Joined:
    Sep 4, 2012
    Posts:
    116
    Hello,

    we have interesting plans to improve the whole MonoBehaviour and messages story.

    We are experimenting with a few approaches atm, but it will take a bit of time because there are a million things to consider to make sure we are solid enough. The interface-based approach you described above is one of the most promising we are considering.

    We will share more the closer we get with something concrete.

    Thanks for the feedback btw!

    Gab
     
    MechEthan and Pharan like this.
  3. pvloon

    pvloon

    Joined:
    Oct 5, 2011
    Posts:
    591
    Personally I really hope unity drops all the magic and just goes with virtual methods. That is how C# intends this to work - other approaches will always be fighting the language in some way.

    Interfaces would be a lot better than current MonoBehaviours, but still it makes for all of these awkward designs:
    • Common use is to make a behavior intended as some base class. Now you'd still want to frequently mark the used callbacks as virtual anyway - so we'd still be stuck with callbacks sometimes being virtual, sometimes not.
    • These interfaces could only be used on ScriptableBehaviours. So now there needs to be a validation layer to check your code and make sure you're using the interface right etc. That really should just be handled by the compiler
    • ScriptableObject (or whatever the new variant would be) only supports a subset of callbacks so it'd be able to eg use IEnable, but not IUpdate. Again this would require validation of the code, custom errors after compiling, documentation for the users etc. For base functions this would all just be immediately obvious - compiling code is valid code.
    • What happens if users would use public void IAwake.Awake() - now the function is hidden, so there'd still be inconsistent access patterns. This could mean unity would _still_ have to call these new methods with slow and bulky reflection magic vs finally just being able to efficiently do one virtual call for them.
    • Functions are now 'declared' in two places, in the type & the in body. More hassle, redundant code, redundant typing.
    • You would still not get any discoverability on what functions are available (for base functions just write override and you'd get a list)
    • I don't think type declarations like class MyScript : ScriptableBehaviour, IAwake, IEnable, IDisable, IUpdate, IFixedUpdate, IDestroy etc. will be very managable or very nice...
    • It conflates type with functionality. Eg, GetComponent<IAwake>() should work then but that's just jibberish. public void Test(IAwake someObj) ? Not a huge deal but still - compilable code should strife to be valid code.
    • It would force all callbacks to be public. I think it would be nicer if all of these were protected.
    • To any newcomers with a programming background it will just be more weird black box magic. Programmers do understand what base functions are though.

    Just regular virtual functions seem much cleaner, but I'm sure I'm missing some concerns. Maybe it would be quite a lot of virtual functions to have on a behaviour, but I really hope at least the 'basic' functions could be regular virtuals, and only have infrequently used functions hidden behind an interface.

    Can't wait to hear more about the ScriptableObject stuff though!
     
  4. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    Won't this degrade performance a lot when you do GetComponent<T> ?
    It would have to look at every component, and then at every interface it implements, ...
    Or is that not a problem at all?
     
    sand_lantern likes this.
  5. bdovaz

    bdovaz

    Joined:
    Dec 10, 2011
    Posts:
    1,049
    I answer in the quoted message.

    I also want to hear Unity staff so we can give feedback.

    It has to be consistent with uGUI. Unity can't make two different approaches because it would be really confusing.

    Also in 5.6 I'm seeing more "magic" messages replaced with interfaces (Finally we also have IPreprocessBuild! We can now preprocess a build) like: https://docs.unity3d.com/560/Documentation/ScriptReference/Build.IPostprocessBuild.html
     
  6. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    I agree that an empty virtual implementation in a base class + override in your own MonoBehviour is a bad idea!
    Unity would then call Update on EVERY gameobject, even if it doesn't need Update().
    Sure, its empty calls, but it happens *every frame*. And I don't know about you guys but I like to use transforms as "folders" to keep things organized, and also to have a common parent that I can move/rotate/scale. That's important and I wouldn't want Unity to call Update for all the folders for no reason at all :(
     
  7. pvloon

    pvloon

    Joined:
    Oct 5, 2011
    Posts:
    591
    @N3uRo: Don't agree with all your points.

    Most of the time you do only want 2 or 3, and so you'd only override 2 or 3. No need to override all of them, you wouldn't see this base class. It's really not the same as these accessors - just having the basic functions as virtuals wouldn't couple ScriptableObject with other code. Point take that maybe eg collisions callbacks would - but see other point about how infrequently used functions could still work as an interface

    Yes the same problem exists, so let's fix it.

    Again, let's fix it. Just having defaults won't help that much - fundamentally we still have split functions into two, no way around that, I don't think Start and Awake cover that many cases.

    Reading the docs is more work than intellisense ;) Yes, uGUI has the same problem. I care about it, sucks having to google stuff ever time, there is an obvious solution to fix that in this case.

    So... why not mark them as virtual functions in the unity base class and standardize that?

    Fair point, so let's exclude stuff with arguments or that could pull in other sub systems in some way (physics, networking callbacks etc.). No reason to do that for Update, Enable, Disable, Reset, Destroy, Awake, FixedUpdate, render callbacks etc. etc. though, there are really no coupling issues there.

    @dadude123: This really is not a concern - or well, it is, but if you think this is bad - the current state is HORRENDOUS. We (Ori and the Blind Forest) have had lots of fights with how slow messages are at the moment, the Playdead guys (Inside) were talking about it and recommend avoiding them altogether, I hear more devs complaining about it, hell even unity knows it: https://blogs.unity3d.com/2015/12/23/1k-update-calls/.
    Their current way of doing update calls is horrendously awful. Finally having standardized, compiler validated, virtuals would mean they could cut these costs waaay down. If it still turns out to be an issue, nothing is stopping them from not calling the function if it's not overriden anyway.
     
  8. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    New ScriptableObject stuff? I'm also interested...
     
  9. pvloon

    pvloon

    Joined:
    Oct 5, 2011
    Posts:
    591
    woops, *ScriptableBehaviour . That was the draft name for their MonoBehaviour as far as I've gathered
     
  10. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    Hey @pvloon :

    Could you tell me how many scripts / objects you had in the average scene?
    What did you do to circumvent the problem? Surely you didn't roll your own update scheduler for every single script you made, right?

    Or are you talking about SendMessage() ?? (which is something completely different!)

    Now, are you sure that it would make much of a difference between how things are right now, VS making interfaces?
    Surely every call has overhead (and virtual calls even more so!). So I'm not that convinced that the change from what unity is doing now to using interfaces or empty virtual method bodies in MonoBehaviour would make much of a difference.

    The best way to do it is to just not call at all!! Unity could easily check what scripts need Update() and put them into List<MonoBehaviour> ThingsThatNeedUpdateCalls; (or just make it a List<Action> in the first place, then remove them from the list when they get inactive or whatever).

    Keep in mind I'm not *saying* the performance improvement won't be big enough, I'm *asking*.
     
    sand_lantern likes this.
  11. pvloon

    pvloon

    Joined:
    Oct 5, 2011
    Posts:
    591
    We had couple thousand upward to 10K scripts in a scene.

    We did avoid Update at all costs and had some Update managers for that, but we did use 'regular' OnEnable / Awake calls. Those were optimised to be super light weight, but just CALLING those took >15ms, causing a spike, which as the game is totally streamed and scenes are loaded quite often was really bad.

    SendMessage(), ironically, only appears to be a little different. From my measurments it's slower than OnEnable() yes, but not even that much slower.

    So yes I'm sure that doing a straight virtual call is WAY faster that what we have now. If you read the blog post you can see the insane amount of stuff unity is doing. A virtual is 'slow' because of one pointer dereference.... I think unity easily might be doing two orders of magnitudes more than that currently.

    If all of this sounds insane it's because it is - I really can't stress how slow callbacks are at the moment. Most developers of bigger games just recommend avoiding them as much possible altogether - kind of a shame for something so basic :/


    I agree that unity should keep a list of what does and doesn't need to be udpated - but that's independent of whether they go with virtuals or interfaces.
     
    Zuntatos likes this.
  12. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,653
    Without going into details about ScriptBehaviour - because I don't know the details of exactly what the Scripting team are working on - I wanted to respond to a couple of points in this thread:

    While that's true up to a point, what if we have heard from users that there are downsides to the way Unity UI is designed? Surely we should not just ignore that feedback? (The right answer here is most likely 'you should fix those downsides in Unity UI as well' :) )

    No, it wouldn't - that would indeed be a bad idea. When we are looking for Update methods to call, we'd check which class they're actually defined on, and we would skip calling it for objects that didn't override ScriptBehaviour.Update().

    Bear in mind that while a base class full of virtuals would make it easier for coders to discover which methods they could implement, it would make it harder for code to discover which methods have been implemented. Instead of being able to do "someObj is IUpdateable" you'd have to use reflection and stuff. It's a tradeoff that might be worth it, but it's still a tradeoff.

    Neither interfaces nor virtual functions would have any impact on the cost of Update() callbacks. We do all the work to find the magic methods (and check their signatures, etc) once per class, at startup; the cost of actually calling something like Update() is entirely in the actual transition from native to managed code. Switching to interfaces or virtuals might help a little with the cost of the former, but it wouldn't affect the latter.
     
    MechEthan and mdrotar like this.
  13. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    Thanks for the answers guys, really clarified things for me.

    But can that even be optimized more at all?
    Isn't the whole point of this to improve performance? So why switch to interfaces or virtual methods at all then? Just for readability or to make it more clear for newcomers?
     
  14. bdovaz

    bdovaz

    Joined:
    Dec 10, 2011
    Posts:
    1,049
    Benefits of interfaces approach:

    - No "magic" involved.
    - You can't make errors like CollisionEnter vs ColisionEnter.
    - No issues in code analysis tools -> void Awake {} = unused method error.
    - Better Unit testing, you simulate and call directly to Awake, OnEnable, Start, ...
    - Better code coverage reporting.
     
  15. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,653
    Yeah, the new ScriptBehaviour is mostly not about performance, but about improving design and usability. (Naturally if there are opportunities to improve performance in the process then we'll take them, but it's not the #1 goal).
     
    MechEthan likes this.
  16. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    I hope that finally you guys will remove the deprecated properties from MonoBehaviour: "rigidbody" "collider" etc.
    They're really nice names to use, and Unity complains that I need to add "new" "property name" while in the editor, but then complains I don't need "new" when doing a build.
     
    Deozaan and Rennan24 like this.
  17. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    cdf: agreed, I wonder when that and other deprecated functions will get removed

    Hmm thats sad. Because people will eventually get used to a somewhat strange design.
    But they can't get used to bad performance. Having to write your own update scheduler(s) is a constant pain point which is really not optimal or even expected.

    Not sure why you guys are working on something that will (at best) only confuse people a bit because things change, as opposed to something people directly profit from :/

    But then again I don't know how closely those two problems are really related, I don't know how the code works, and I'm hopeful that the .net/mono upgrade will produce or enable some performance improvements along the way :)

    No offense though!! I completely understand that the current situation is not optimal and why you want to fix it. Just my thoughts on this. :)
     
  18. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,653
    I wouldn't worry too much. There are other things in the pipeline that will have a far bigger impact on the way you structure high-performance code than whether our MonoBehaviour-replacement uses interfaces or virtuals ;)
     
  19. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    Oh, that's great to hear! :)
    Any hints or sneak peeks what we could possibly expect?
    I don't want to derail this thread tho. Feel free to PM me if you have some details or even vague hints or ideas :D
     
  20. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,653
    MechEthan and dadude123 like this.
  21. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,332
    I'm not sure if I like the interface option for MonoBehaviours.

    Mostly, it's because that requires things like Update to be public. For all of it's weirdness, one of the good things about the messaging system is that we can get the messages without exposing methods. To be fair, I don't expect people to be calling Update externally even if they could, but proper encapsulation is still important.

    Interfaces would also not help with unit-testing. For MonoBehaviours to be properly testable, we need to be able to mock things like "a frame passed for all the objects in the test", or "these two gameobjects collided", and that requires more than just being able to manually call Update or OnCollisionEnter.

    Finally, as a programmer, the difference between this:

    Code (csharp):
    1. class Foo : MonoBehaviour {
    2.  
    3.     void Start() { ... }
    4.     void Update() { ... }
    5.     void OnCollisionEnter {Collision c) { ... }
    6. }
    and this:

    Code (csharp):
    1. class Foo : MonoBehaviour, IStart, IUpdate, ICollisionEnter {
    2.  
    3.     public void Start() { ... }
    4.     public void Update() { ... }
    5.     public void OnCollisionEnter {Collision c) { ... }
    6. }
    is just a whole lot of boilerplate. I don't see the gain.


    MonoBehaviour has some problems. It's really hard to unit test, since we can't mock messages and such. The .enabled property does completely arbitrary things (pretty much everyone I've met got really surprised when they realized that OnTriggerEnter gets called when .enabled is false). Destroying them doesn't take effect before the end of the frame, but there's no way to check if they're queued for getting destroyed.

    So, a bunch of things. The messaging system is honestly fine as is.

    @superpig, not to hijack the thread, but are you guys planning on anything to help us write proper Unit tests for MBs? We can put layers between the messages and our behaviour, but that's not really what Unit's API is made for.
     
    dadude123 and kru like this.
  22. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    That's kind of a scary thing to hear. If you were to ask the community, I'm ready to bet the overwhelming majority of people would be in favor of focusing on performance, even it the end result isn't as easy to use as the current Monobehaviours. "More performance" might even be the one and only thing most people really expect out of this new system.

    I think people want Unity to feel like more of a serious engine and less like "a great engine for prototyping things". If the new ScriptBehaviours don't focus on performance, people will just keep trying to find ways to not use them at all, and come up with their own more-performant system instead. And in the end, all that effort for improving usability will have been for nothing.

    For instance, in a project we're working on right now, we only have less than a dozen 'active' Monobehaviours in the scene at any time, because these Monobehaviours only serve as "update managers" that call custom update methods on various entity classes that don't use any Monobehaviour methods (and sometimes they are collision handlers, when we don't have a choice). We felt the need to greatly sacrifice iteration time and usability with this because performance is just too important. We'd have to do the same thing if ScriptBehaviour isn't an improvement over Monobehaviour performance

    In short.... it won't be an improvement in terms of usability if people don't want to use it, and the only way people will want to use it is if it is built for performance
     
    Last edited: Mar 7, 2017
    dadude123 likes this.
  23. pvloon

    pvloon

    Joined:
    Oct 5, 2011
    Posts:
    591
    Agreed. Larger developers just avoid many of these callbacks atm:



    (at 48 mins 31 seconds)

    Same for Ori (though a little less strict) and I'm sure more larger developers do. I really feel that without more performance how nice the new design is doesn't really matter super much. Though I am super excited about the new design!

    The weird thing is that unity IS moving away from 'a great engine for prototyping' - so I hope that in that move the performance of these messages doesn't get overlooked.
     
  24. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    Couldn't agree more.
    I don't really like the mindset of "lets just throw more cores at a problem to hide the inefficiencies".
    Now hold up, I can imagine what you're thinking now! I'm not saying that this would be possible by just optimizing the current code, surely the job system plays a great role in even making this demo possible.

    But when it comes to things that are called SO often like Update(), then you better make sure that you implement every friggin optimization you can think of...

    One thing that'd make a huge difference would be to eliminate the transition from native to .net. Why not have a c# List<> of things to update? So not every Update call has to cross the domain border??

    This "usability first, performance second" is the reason why people avoid this stuff in the first place. I think we should take every chance we can get to make this better now!

    edit: @pvloon

    Man that video part... its actually insane. its like he's saying "don't use 90% of unity" haha.
    You really gotta wonder: who's all this usability and ease of use being built for? Most people are avoiding it. I believe that we don't have to sacrifice all the neat things to get really good performance, just some clever solutions...
     
    Last edited: Mar 7, 2017
  25. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,653
    My point is that changing the design of MonoBehaviour can't impact performance a whole lot, because the current design of it does not impact performance a whole lot. Stuff like crossing the native->managed boundary for each message invocation is a problem we could solve today; it's not something we need a new design to solve. (The new design would make it much cleaner, it's true, but it's still possible today).
     
    MechEthan and Novack like this.
  26. pvloon

    pvloon

    Joined:
    Oct 5, 2011
    Posts:
    591
    I do think however that when the entire MonobeBaviour is replaced seems like THE ideal time to fix this. When ScriptableBehaviour launches I expect more people will ask whether this new system performs better.

    I think the problem is that for me fundamentally the performance of the new system _is_ part of the new system. Say unity goes with a C# based update manager (not sure that's the best way, but bear with me) then it starts to matter whether the design in an interface or protected virtual.

    It just feels a little odd unity is tearing the MonoBehaviour apart to address design flaws (yay!) but then doesn't adres one of it's most important ones (nay!)

    Anywho - think we're going in circles :D Still excited for the new design! Even more excited if we at some point get more perf ;)
     
  27. Trisibo

    Trisibo

    Joined:
    Nov 1, 2010
    Posts:
    245
    I don't understand what you mean with "slow and bulky reflection magic", since all it would take to call the method is to do a simple cast: ((IAwake)thing).Awake(), that's all. I have also done some performance measurements, and the difference in calling the method in 4 scenarios (explicit interface implementation, implicit implementation, virtual method, abstract method) is completely negligible. This is the code (the explicit interface implementation doesn't use a public method like in your example, that's a CS0106 compile-time error):

    Code (CSharp):
    1. using System;
    2. using System.Diagnostics;
    3.  
    4. namespace Test
    5. {
    6.     interface IUpdate
    7.     {
    8.         void Update();
    9.     }
    10.  
    11.     class InterfaceExplicit : IUpdate
    12.     {
    13.         public int i = 0;
    14.  
    15.         void IUpdate.Update()
    16.         {
    17.             i++;
    18.         }
    19.     }
    20.  
    21.     class InterfaceImplicit : IUpdate
    22.     {
    23.         public int i = 0;
    24.  
    25.         public void Update()
    26.         {
    27.             i++;
    28.         }
    29.     }
    30.  
    31.  
    32.  
    33.  
    34.     abstract class BaseAbstract
    35.     {
    36.         public abstract void Update();
    37.     }
    38.  
    39.     class DerivedAbstract : BaseAbstract
    40.     {
    41.         public int i = 0;
    42.  
    43.         public override void Update()
    44.         {
    45.             i++;
    46.         }
    47.     }
    48.  
    49.  
    50.  
    51.  
    52.     class BaseVirtual
    53.     {
    54.         public virtual void Update() {}
    55.     }
    56.  
    57.     class DerivedVirtual : BaseVirtual
    58.     {
    59.         public int i = 0;
    60.  
    61.         public override void Update()
    62.         {
    63.             i++;
    64.         }
    65.     }
    66.  
    67.  
    68.  
    69.  
    70.     class Program
    71.     {
    72.         static void Main(string[] args)
    73.         {
    74.             const int iterations = 1000000000;
    75.  
    76.             InterfaceExplicit interfaceExplicit = new InterfaceExplicit();
    77.             InterfaceImplicit interfaceImplicit = new InterfaceImplicit();
    78.             DerivedAbstract   derivedAbstract   = new DerivedAbstract();
    79.             DerivedVirtual    derivedVirtual    = new DerivedVirtual();
    80.          
    81.             Stopwatch sw = new Stopwatch();
    82.          
    83.  
    84.             // Explicit interface:
    85.             sw.Restart();
    86.          
    87.             for (int i = 0;  i < iterations;  i++)
    88.             {
    89.                 ((IUpdate)interfaceExplicit).Update();
    90.             }
    91.          
    92.             sw.Stop();
    93.             Console.WriteLine("Explicit interface: " + sw.ElapsedMilliseconds.ToString() + " ms");
    94.             Console.WriteLine(interfaceExplicit.i.ToString() + "\n");
    95.          
    96.  
    97.             // Implicit interface:
    98.             sw.Restart();
    99.          
    100.             for (int i = 0;  i < iterations;  i++)
    101.             {
    102.                 interfaceImplicit.Update();
    103.             }
    104.          
    105.             sw.Stop();
    106.             Console.WriteLine("Implicit interface: " + sw.ElapsedMilliseconds.ToString() + " ms");
    107.             Console.WriteLine(interfaceImplicit.i.ToString() + "\n");
    108.          
    109.  
    110.             // Derived abstract:
    111.             sw.Restart();
    112.          
    113.             for (int i = 0;  i < iterations;  i++)
    114.             {
    115.                 derivedAbstract.Update();
    116.             }
    117.          
    118.             sw.Stop();
    119.             Console.WriteLine("Derived abstract: " + sw.ElapsedMilliseconds.ToString() + " ms");
    120.             Console.WriteLine(derivedAbstract.i.ToString() + "\n");
    121.          
    122.  
    123.             // Derived virtual:
    124.             sw.Restart();
    125.          
    126.             for (int i = 0;  i < iterations;  i++)
    127.             {
    128.                 derivedVirtual.Update();
    129.             }
    130.          
    131.             sw.Stop();
    132.             Console.WriteLine("Derived virtual: " + sw.ElapsedMilliseconds.ToString() + " ms");
    133.             Console.WriteLine(derivedVirtual.i.ToString() + "\n");
    134.  
    135.          
    136.             // End:
    137.             Console.WriteLine("Finished");
    138.             Console.ReadLine();
    139.         }
    140.     }
    141. }
    I have repeated the test 12 times, also varying the order of the methods, and the average times I got are:
    • Explicit interface: 4834 ms
    • Implicit interface: 4513 ms
    • Derived abstract: 4910 ms
    • Derived virtual: 4693 ms
    So an explicit interface and a derived class are practically the same (I have also done the test in a Unity project, with slower but similar results, actually the explicit interface was faster, though I only tested twice).
     
    MMOARgames, Novack and mikael_juhala like this.
  28. Trisibo

    Trisibo

    Joined:
    Nov 1, 2010
    Posts:
    245
    Actually it wouldn't require to make the methods public, you can implement an interface method explicitly and make it inaccessible using the object instance (see my answer above), for example:

    Code (CSharp):
    1. class Stuff : IUpdate
    2. {
    3.     // Explicit IUpdate.Update() implementation:
    4.     void IUpdate.Update()
    5.     {
    6.     }
    7. }
    Sure, you can still call the method doing ((IUpdate)instanceOfStuff).Update(), but not using instanceOfStuff.Update(), it won't appear in IntelliSense and IMHO it's still properly encapsulated. And anyway you can still call any method, including Unity's messages using reflection. To me, using interfaces doesn't seem like a bad idea.
     
    MMOARgames, Novack and mikael_juhala like this.
  29. bdovaz

    bdovaz

    Joined:
    Dec 10, 2011
    Posts:
    1,049
    Psyco92 likes this.
  30. MMOARgames

    MMOARgames

    Joined:
    Feb 28, 2013
    Posts:
    24
    I definitely like the idea of making all the mono messages interfaces.

    uGUI uses it, and an open source project (The Mixed Reality Toolkit) uses them effectively as well.
     
    Last edited: Oct 16, 2018
  31. Ramobo

    Ramobo

    Joined:
    Dec 26, 2018
    Posts:
    212
  32. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,332
    See dots.

    tl;dr: Unity took a good hard look at the core of the engine, and figured out that the possible gains within the limits of the current architecture were not good enough, so they're rewriting that core instead.

    If it's a good idea is too be determined.
     
  33. Ramobo

    Ramobo

    Joined:
    Dec 26, 2018
    Posts:
    212
    DOTS isn't exactly this "MonoBehaviour 2" thing.
     
    tonialatalo likes this.
  34. OndrejP

    OndrejP

    Joined:
    Jul 19, 2017
    Posts:
    304
    I don't like messages through interfaces, it would be annoying to add interface for each message.
    Having interfaces like IEnable, IDisable, IReset, IValidate, IUpdate, IAwake, IDestroy seems really weird.
    Many classes would have a lot of them. Adding / removing message would require change in two places instead of one.

    Having virtual methods might work better, it would be friendly to intellisense. Performance might be exactly same, since Unity could call methods in exactly same way (non-virtual method call to most-derived method version). There would be ofc big issues with compatibility to old code.

    Another issue with virtual method might be that VS creates body which calls base class implementation. This might be confusing, since it's empty method.

    All things taken into account, I'm okay with current state of messages. There's intellisense for them (with VS tools for Unity) and no extra code (no interfaces, no autogenerated call to base). They even got different color in VS.
     
  35. Ramobo

    Ramobo

    Joined:
    Dec 26, 2018
    Posts:
    212
    Why not go hybrid? Here's my take on the situation:
    • Get rid of all messages. I can't think of replacement implementations for them, so SendMessage and friends can stay, provided they obey access modifiers.
    • Basic life cycle messages (Awake, Start, Update, OnEnable, OnDisable, OnDestroy...) should become virtual methods.
    • Messages that are "called by other objects" should become events: Objects that do things that might be of interest to others should send an event and let those that care about it handle it, instead of calling everyone itself (which appears to be how C# events work, but it's not very user-visible, so I'll ignore it). e.g.
    • OnCollisionEnter -> Collider.CollisionEntered.
    • OnApplicationPause -> Application.Paused.
    • OnAnimatorIK -> Animator.Something (can't think of a name for this type of message).​
    • Interfaces... I can't think of much use for them in this system.

    I'm strongly against messages because I'm a purist. It's the same reason for which I oppose [SerializeField]. It's even inconsistent: Unity ignores access modifiers for messages but obeys them for UnityEvent. Go figure.

    Also, an analogy for events:
    • Listener: You keep checking for a newspaper release.
    • Callback: You get a letter telling you that the newspaper was released.
     
    Last edited: Jan 7, 2020
    brunocoimbra and sirxeno like this.