Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Linking References vs. GetComponent()

Discussion in 'Scripting' started by Vega4Life, Nov 16, 2018.

  1. Vega4Life

    Vega4Life

    Joined:
    Nov 27, 2012
    Posts:
    17
    This is just a conversation about which do people general prefer to do when they need references to specific scripts. Do you link the reference directly or do GetComponent() on Awake.

    I tend to to use GetComponent() as much as I can. I feel the inspector can get really muddy with references to a lot of scripts. Also, it terrifies me to look at an object and magically have missing references. I have seen this on numerous occasions and a lot of the times you have no idea where those links are coming from.

    I feel GetComponent() cuts down on user error (accidently killing a linked reference or whatever), but GetComponent() probably has a loading cost against just linking them.

    I do tend to always link prefabs though unless they are coming from an asset bundle.

    Thoughts? Best practices?
     
    heeyoung_abc likes this.
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    GetComponent() is "slow".
    Should be utilized as little as possible.
    You should cache your references at initialization, to have later fast access.

    So if I got MyClass, in other script I for example

    Code (CSharp):
    1. MyClass myClass ; // Class from other script
    2.  
    3. private void Start ()
    4. {
    5.    myClass = new MyClass () ;
    6. }
    Personally I keep as little components on GameObjects as possible.
    Bu that may not be suitable for every one.
     
  3. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    4,459
    How do you do that for components that are not scripts?
     
    heeyoung_abc likes this.
  4. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    Like from GameObject for example ?
    For simplicity

    If I really have to get GameObject from the hierarchy, I do use at Start

    Code (CSharp):
    1. GameObject myGameObject1 ;
    2. GameObject myGameObject2 ;
    3.  
    4. private void Start ( )
    5. {
    6.     myGameObject1 = GameObject.Find ( "Full/Path/To/GameObjectName1" ) ;
    7.     myGameObject2 = GameObject.Find ( "Full/Path/To/GameObjectName2" ) ;
    8. }
    Similarly if I need instantiate.

    If I know I never going use Game object but only transfrm

    Transform myTransformObject1 ;
    Transform myTransformObject2 ;

    Code (CSharp):
    1. private void Start ( )
    2. {
    3.     myTransformObject1 = GameObject.Find ( "Full/Path/To/GameObjectName1" ).transform ;
    4.     myTransformObject2 = GameObject.Find ( "Full/Path/To/GameObjectName2" ).transform ;
    5. }
    If I need light for example

    Code (CSharp):
    1. private void Start ( )
    2. {
    3.     GameObject myGameObject1 = GameObject.Find ( "Full/Path/To/GameObjectName1" ) ;
    4.     GameObject myGameObject2 = GameObject.Find ( "Full/Path/To/GameObjectName2" ) ;
    5.  
    6.     myLightObject1 = myGameObject1.GetComponent <Light> () ;
    7.     myLightObject2 = myGameObject2.GetComponent <Light> () ;
    8. }
    Etc.

    I can keep stuff in array or List, or other collection if needed.
     
    heeyoung_abc likes this.
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    ???

    Caching a component works on any type of Component. You can use GetComponent<Rigidbody>() just as well as GetComponent<MyScript>()
     
  6. Marble

    Marble

    Joined:
    Aug 29, 2005
    Posts:
    1,266
    I prefer direct references initialized by the inspector, because at no point are those references null, and I don't have to manage initialization order. If I'm terrified of a cluttered inspector, then I will only show those references that are null using a custom inspector.

    I rarely use GetComponent, except maybe with MonoBehaviour methods like OnTriggerEnter where I don't have control over the type of the parameter that gets passed in.
     
    heeyoung_abc, Menyus777 and ZombieTFK like this.
  7. Vega4Life

    Vega4Life

    Joined:
    Nov 27, 2012
    Posts:
    17
    @Marble That's a good idea for only showing null references with a custom inspector.
     
  8. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    For triggers I always create lookups, they are o(1), also if possible use layers to minimize collision with objects that are not relevant.
     
  9. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    4,459
    The question was about not using getcomponent... You should have read the previous post. :)
     
  10. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    4,459
    Wait, isn't using "find" worst than using getcomponent?
     
  11. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    Probably yes, since using string. But since running only once at start, who cares. And wouldn't expect to search thousands of objects anyway. Probably few, to few dozens at most.

    Could use tags alternatively. But I don't use them. They can be problematic when moving project files for example.

    Then you of course can load prefabs. If so, still should be cached.

    Once objects are cached, you don't need to worry about GetComponent or loading prefabs, or Find etc. for rest of your game, or level. Unless explicitly request it.
     
  12. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Tags should never be used. Better to use a markup component instead.
     
  13. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    At least you could justify your short statement, so learners can understand why. Just try use simple language ;)
     
  14. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Well tags are just strings and they encourage code like

    If(someobject.tag == "something")
    DoSomething(someobject)

    This is not very robust.

    With a markup component combined with a lookup you would do

    If(lookup.ContainsKey(transform)) lookup[transform].DoSomething()
     
    Teocuitlatl likes this.
  15. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Yes, it is indeed. If you asked me, avoid it whenever you can, in other words: always avoid it, because there's always a better, more reliable, type-safe and faster way.

    I would say that depends. There's no doubt the built-in tag stuff could be improved a lot, that's why the marker components are sometimes the next alternative.

    However, adding multiple marker components for the sake of achieving the ability to say "hey, that's something that I'm looking for" is very annoying, and each instance adds to memory footprint. On top of that, you run into risk of having many useless type definitions and instances that solely serve for identifcation purposes.

    If you look around, there are better tagging solutions out there. Basically, the tagging component is the only "marker" that you'd need, and it allows to query tags - most importantly they support multi-tags. An approvement to many of the systems would be to replace plain strings with an SO-based approach, as this leverages the power of the inspector linking and the asset system. So it's type-safe, defined in one single place and greatly extensible.
     
  16. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    GetComponent<MyComponent>() is slow in comparison to having a cached reference, but it really isn't that slow. I wouldn't get too worried about using GetComponent when it makes sense. Use the profiler to see how much of an impact it is having regardless.
     
    Menyus777, Rallix, Munchy2007 and 3 others like this.
  17. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    We have written an entire game without a single tag. Not more than a few markup components for example a material markup for bullet impacts, ballistics penetration simulation and footstep sounds. It's actually the only one I can remember, probably have one or two more. Would say the memory difference is negligible on anything more than a pocket calculator
     
    Last edited: Nov 16, 2018
    Antypodish likes this.
  18. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    And do it built, GetComponent is one of those method calls that makes no sense in editor
     
  19. ZombieTFK

    ZombieTFK

    Joined:
    Sep 6, 2016
    Posts:
    55
    I'm with @Marble on this one. Explicitly add your references through the inspector, unless Instatiated directly in a script from a prefab (Itself a reference from the inspector), in that case I keep a reference the created object somewhere on an appropriate manager or singleton object. I used to use GetComponentInChildren<T> and GetComponent<T> for everything but now I might only use it for some typechecking when i do raycasts or other physics calculations, and find my code is much cleaner because of it. I find the more you use GetComponent<T>, the more it spreads throughout your codebase until it's used in every nook and cranny. Best to just stay away. Having said that, I don't believe the hoo-har about performance. It never had an impact for me anyway, calling >1000/update cycle. Maybe it's more noticeable for mobile devices though.

    I never use tags or find stuff by a string name, it's a) uuuugly and b) creates needless coupling between the inspector and your code. Any time you change a referenced name you'll also need to change at least 1 value in your scripts or external configuration. You might argue that you still need to link any references to other components, but that's the whole point. Your dependencies between components are supposed to be manged in the Inspector, by Unity. If you want to use your component in another project you'll have to use the exact same strings or go into the code and change them yourself. I very rarely make an exception for layer names because they are so handy, and even then store them in their own enumeration.
     
    Last edited: Nov 16, 2018
  20. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    This also pretty much depends on the game/application, and the overall software architecture . There are certainly applications where the number of tags can be much higher, just like the number of overall tag candidates.

    It's also possible to not depend on tagging or marking at all, and solve this in completely different ways.

    Anyway, I wasn't saying tagging, marking or anything alike is wrong in its fundamental idea, just that there's room for improvements with regards to the built-in solution.
    The system I talked about only requires the following: Tag compoent, Tag type (preferably an SO in Unity), and the Tag instances (which represent the tags itself - comparison by reference and linkage through inspector).
     
  21. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    What you describe is basically our material tag system
    upload_2018-11-16_21-22-7.png

    We also have some systems that I guess could fall under the markup genre of types. For example our spawn point types
    upload_2018-11-16_21-25-58.png
     
  22. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Wait, I guess I misunderstood your interpretation of markup components.

    I thought you were talking about empty components, that only (really only!) serve the purpose of identifying an object, without any further use (no fields/properties, no behaviour). Because that's what I've seen on the forums an in some projects. Your components have an actual use and provide more information than just existence of a component, so they're simply small modular components.
     
  23. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    The first one I would call a markup component, the second is grayzone. I have never seen the need for a markup component that does not need some additional meta.

    edit: The whole idea of markup components like these are seperation of concerns. Tag code often endup on the wrong place as a if block
     
    Last edited: Nov 16, 2018
  24. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Well, exactly.
    It is/was one way to get rid of string tags, and to achieve quick and easy multi-tagging. Comparing for a tag was then replaced with at least one call of GetComponent.

    So there was really no additional data available. Something similar was used in the past in order to add some sort of meta data using empty interfaces (one the common examples is/was the Java interface "Serializable"), but these has mostly been replaced in several languages.
     
  25. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    4,459
    So, what exactly are:
    1. markup components?
    2. lookups?
    I'm still learning... :)
     
  26. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    lookups are just like searching / iterating through arrays, or list.
    In mentioned context, it is is how fast (cheap) is to reference, search for given element. Smaller number means faster.
    Searching strings is more expensive for example, than accessing something by index, or reference.

    In terms of markups, is just like tagging something, which gives a certain role to do thing.
    If I tag you as kitchen assistant, you will be able to do that job. In sense of programming, guys been discussing partially GameObject components, which has no other function, than just tag this GameObject itslef. So for example GameObject component Transform, has position, rotation and scaling. But without values, can just act as a tag (markup).

    In ECS the later is useful.

    I hope I have't mixed up ;)
     
  27. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    markup component is not a offcal Unity term. Its just a term i use for simple components that markup or tag a gameobject. But its much more modular and maintainable than using the classic string based tags.

    Lookups are dictionaries or hashmaps, etc that are fast o(1) at looking up values based on keys. Here is an example from our Inventory code

    Code (CSharp):
    1. protected virtual void OnTriggerEnter(Collider collider)
    2. {
    3.     if (!StoringAllowed) return;
    4.     if (items.Count >= Capacity) return;
    5.  
    6.     var interactable = NVRInteractables.GetInteractable(collider) as TItem;
    7.     if (interactable == null) return;
    8.     if (!interactable.IsAttachedToAnyMover) return;
    9.     if (items.Contains(interactable)) return;
    10.     if (!CanStore(interactable)) return;
    11.  
    12.     if (!hoveringColliders.ContainsKey(collider))
    13.     {
    14.         interactable.AttachedMover.TriggerHapticFeedback(1, 1);
    15.         hoveringColliders[collider] = interactable;
    16.     }  
    17. }
    NVRInteractables.GetInteractable is in fact a lookup function it uses a Dictionary internally

    Code (CSharp):
    1.    
    2. public static NVRInteractable GetInteractable(Collider collider)
    3. {
    4.     if (ColliderMapping.ContainsKey(collider)) return ColliderMapping[collider];
    5.     return null;
    6. }
    7.  
    8.  
    Then we have another lookup called items that o(1) check if we allready have stored the item in inventory.
    Methods like these are basically free to call and could even be used from a TriggerStay if needed, though I wold stay away from TriggerStay if possible.
     
    APSchmidt likes this.
  28. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,914
    The problem I've found with hand-set Inspector references is when you re-use the script. I've usually forgotten just what goes into each slot. Takes far too long to set it up again.

    Writing code to set them up seems to save lots of head scratching for anything I might re-use in 6 months. The code can know what it can create, what it needs to search for, who to ask to create it, what is allowed to be null. Your "can't find speechScript in scene" errors are right in Start. It's pretty quick to write: if scriptA is expected to have a personal copy of ButtonColors on its gameObject, that's 1 line which is almost the same as the comment you'd write anyway.

    Sure, it runs slower, but just once, and not much. If you care, later you can use it to hand-set them: Play, write down the contents, drag them in after Stopping, and comment out (or add "if(xx!=null)) you code se-up.
     
    Kiwasi likes this.
  29. ZombieTFK

    ZombieTFK

    Joined:
    Sep 6, 2016
    Posts:
    55
    Doesn't it explicitly tell you what components it expects in the inspector? If I had some things (Component dependencies) in my script which could be null and others which could not then I'd take it as a sure sign that the logic in my component could be broken down and refactored into something smaller.

    Then again, it can be annoying when you delete something in a scene and the reference gets broken, and if it's something I need really often for a lot of components I'll access any "manager" type objects statically as singletons.
     
    Last edited: Nov 17, 2018
  30. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    I agree, but some things are not that easy to reference in runtime

    upload_2018-11-17_11-57-18.png
    The first example is the hand rigs for left and right hands, I could move that to a own component called HandRigs and slap it on the parent object and it have a Enum left or right hand and then it searches its children for the rigs. Pretty nice separation of concerns to.

    A harder prblem to solve would be start and end transforms sure it could be done with Convention over configuration approach, and I'm a huge fan of that pattern. But I suspect it would be too much magic for this case.
     
  31. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    All of these caching problems can be avoided via .OnValidate() method.

    1. Serialize the reference. Either by making component public, or private with marked [SerializeField].
    2. Do the same thing with .GetComponent<T>() but instead of putting it in Awake(), put it in .OnValidate().
    Code (CSharp):
    1. [SerializeField]
    2. private SomeRef Component;
    3.  
    4. private void OnValidate() {
    5.        Component = GetComponent<SomeRef>();
    6.        // GetComponent ... or even Find can be used freely here.
    7.        // Or even some complex logic to retrieve the reference.
    8. }
    Pros:
    1. OnValidate is never called in runtime. This means your .GetComponent<T> / Find Calls cost 0 for the runtime application. Actually it is equal to dragging & dropping the reference into the inspector.
    2. Ensures that component is not de-referenced.
    3. Clear understanding what reference is picked without enabling Debug mode in the inspector.
    4. Reusing scripts is simple. Attach the script. Bam! You've already got all dependencies assigned automatically. If some of them are not already set - they still will be picked upon entering playmode.

    Cons:
    1. Insubstantial slowdown on script reload time / playmode start time. (.OnValidate is executed at those points in editor). This is barely noticable on any rig.
    2. Cannot cache instantiated objects in runtime.

    (Note that you shouldn't create architectures like that anyway, as they're prone to bugs, components de-refing, etc.

    This one can be easily bypassed by communicating between components correctly, e.g. avoiding direct referencing of dependencies from another component, by encapsulation or other means. Public refs are evil for a reason)
    _______________________________

    Also, this is how internally some of the Unity's UI elements retrieve their component dependencies.

    As an extra: Write your own editor extensions to make inspector look nicer. Foldouts is the key to this. You can and should write your own attributes / drawers to notify how your dependencies are picked up to notify somehow the designers whom setup these scripts in the scene. Let it be tooltip, or some bold text in front of the label.

    Last thing to note, performance order (some are magnitudes slower than other, there's a huge gaps between call times).

    From fastest to slowest:
    Direct ref > Unity's transform > GetComponent > GetInChildren > GetComponentInParent > FindObjectOfType > FindWithTag ~ Find("") > Instantiate > Resources.Load.

    End of facts part, rant ahead.

    It's fun to see how the manual & tutorials spam people with .Awake() caching whereas actual devs use .OnValidate for that instead. Those tutorial makers tend to forget, that .Awake() caching doesn't scale well at all.

    For one object .GetComponent<T> is okay, but once it comes to the complex games it's a no go, since thousand components will slow loading process drastically.
    Imagine spawning a thousand AI entities in the scene at the scene load with each having a dozen of components?

    No wonder games tend to load like S***. This is especially transparent on mobile.

    But then, again.
    ecs.png

    I should stop now, this is a forum, not a blog.
     
    Last edited: Nov 17, 2018
    Chris_Martin, Rallix and codestage like this.
  32. APSchmidt

    APSchmidt

    Joined:
    Aug 8, 2016
    Posts:
    4,459
    Is what follows comparable to using markup components and lookups?

    In the Stealth project, we have one script in which all the tags we'll need to refer to are stored:
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Tags : MonoBehaviour
    4. {
    5.     public const string player = "Player";
    6.     public const string alarm = "AlarmLight";
    7.     public const string siren = "Siren";
    8.     public const string gameController = "GameController";
    9.     public const string mainLight = "MainLight";
    10.     public const string fader = "Fader";
    11.     public const string enemy = "Enemy";
    12. }
    (it derives from MonoBehaviour but, not being added to any object, I guess MonoBehaviour could be removed).

    Then the tags are defined in Awake() in other scripts using "FindGameObjectWithTag":
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class LaserPlayerDetection : MonoBehaviour
    4. {
    5.     private GameObject player;
    6.     private LastPlayerSighting lastPlayerSighting;
    7.  
    8.     private void Awake()
    9.     {
    10.         player = GameObject.FindGameObjectWithTag(Tags.player);
    11.         lastPlayerSighting = GameObject.FindGameObjectWithTag(Tags.gameController).GetComponent<LastPlayerSighting>();
    12.     }
    13. }
    Also, could this way of doing things be improved? :)
     
  33. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Player can be injected using DI or classic singleton pattern. That player seen stuff might be better as a event on a event bus. We use a behaviour tree for our AI then it fires some events to the outside domain
     
  34. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    If I ever would need string / naming reference, I first use enumerator.
    Then in worse case scenario, having enumValue.ToString () ;

    So for example

    Code (CSharp):
    1. enum Tags
    2. {
    3.     Player,
    4.     AlarmLight,
    5.     Siren,
    6.     GameController,
    7.     MainLight,
    8.     Fader,
    9.     Enem
    10. }
     
  35. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    We were lazy so we used singleton for our Player, would have used DI at day job.

    Edit: it's not even a singleton since you can add a new player and it would just overwrite the instance
     
  36. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,914
    For tags vs. scripts, consider a health pick-up. You might start with a "healthpack" tag. Hit a trigger, check for that tag, if so get +10 health and destroy it. To make bigger packs you could made another tag, or maybe localScale determines the +Health. That's a pain. Pretty soon you want to add an expiration date, animation and so on, so you make a HealthPack script, added to every healthpack gameObject.

    Now the tag isn't needed. Anything with that script on it is a healthpack.
    HealthPack hp=thing.GetComponent<HealthPack>(); if(hp!=null)
    checks whether "thing" is a healthpack and also gets a link to the data.

    But Unity had to have tags. When it was made, all game engines had them, and all designers used them. No one would have switched to Unity if they weren't there.
     
  37. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    What I would do in case of HealtPack is,

    cache healthPack class into Dictionary, where key is a collider and value is reference to the object class.
    Now if I pick (collide / raycast) at runtime with collider, I simply check in dictionary what it is, by collider key.

    This way I don't need any on wake up stuff, or GetComponents at run time.
    And I can have as many object references in single dictionary as I like.
    Easy to procedural generate as well, if needed.

    But I do recognize, people my like using GameObject Components.
     
  38. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    Wouldn't it cause a memory leak when collider gets destroyed though?
    It sure does when gameObject is set as a key and then destroyed without removing from dictionary.

    And it sure does leak as well with the collider as a key. Removing items from it probably will drop performance to be on par with GetComponent<T>. Sum adding colliders to the lookup.

    (They have to be grabbed from somewhere, whether it would be cached from GetComponent / GetComponents or any other mean).

    You might get some performance boost from calling method, but overall performance will degrade much more worse than that.

    I don't see it being that useful in the end.
     
    Last edited: Nov 17, 2018
  39. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    If I would destroy object, I make sure, I destroy every reference.
    Is the designer responsibility.
    I don't walk and instantiate and destroy objects blindly ;)

    In fact, I wouldn't probably destroy object at all. Just put it in pool, to reuse later.
     
    xVergilx likes this.
  40. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    Scene loading / unloading is another case.

    Also, unless pool never shrinks (bad design), references eventually will leak (even worse flaw? But then, barely has any impact).
    So, thats 4 bytes / 8 bytes per each ref lost. Not much, I guess? Maybe it's not even worth removing them.

    Because OnDestroy on itself makes it not worth it.

    So, on 1MB memory leaked it is ~128 000 references on 64-bit OS, or double that on 32-bit.
    Hmm, now it looks like a much better option to simply ignore removing at all.
     
    Last edited: Nov 17, 2018
  41. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Adding and removing from a Dictionary is a O(1) operation. Its basicly free
     
  42. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    OnDestroy message is not though. Caching colliders is not free as well.

    Adding O(N) at best, caching is 0 if cached from editor (note memory footprint from this is not 0);
    Otherwise caching is O(N) as well. Then, Unity's native -> managed messages come into play.

    Context is always important when looking into optimizations.
     
  43. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Thats a Monobehaviour problem. It you need that kind of performance you should have your own data structure and use DrawMeshInstanced. Or wait for ESC to leave beta.
     
    Suddoha likes this.
  44. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,292
    I don't need anything :)

    I'm just saying that it not that worth it I guess, and all of this is completely situational.
    Pointing out unobvios flaws before anyone else decides to do the same thing just to realize it's not that good.

    Also, people tend to praise ECS, but tend to forget it is years before an actual completion. This is not about the beta, it's about all that surrounds MonoBehaviour. Proper Editor integration, thousands of extensions and assets and lot more.

    Also, native Unity's components ALL have to be rewritten to gain burst benefits on each and every level. That would take time, and some extra testing time.

    We should stop derailing this thread. OP asked about basic .Awake caching, not about some vague optimizations or ECS.
     
    Last edited: Nov 17, 2018
  45. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    Well its a forum and discussion went from GetComponent to tags. And tags are bad design so I gave a solution to that which is markup components. And they need lookup collections.

    edit: btw; i think many people prematuarly optimize GetComponent. its not that expensive
     
  46. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    To start worry, you would need have many thousands of objects, with large data set each. You are likely spent hundreds of MB ram on graphics rather on holding data.
    And good thing having cached things specially on mobile, you save on battery.

    For few dozens objects may be insignificant, or prototyping. But people using on update along with instantiating and destroying objects + GetComponent (). All adds up. Then we end up with bunch of crappy performance games. Even felt on desktops.
    If they don't learn from beginning, that there is better way to handle this, or that, including optimal optimization, then they can carry bad habits further.
     
  47. AndersMalmgren

    AndersMalmgren

    Joined:
    Aug 31, 2014
    Posts:
    5,358
    For fun I tested our game with adding 20 items per second and keeping them for 10 seconds. So OnDestory together with Dictonary remove key will trigger 20 times per second and adding to the dictoary etc will happen 20 times per scond roghly. Also these are networked objects so they will be serialized to network stream. We didnt design the game for this many alive objects but fun to see if it holds up.


    200 objects is not much. But for many game types its enough, and if you need more you should look into custom domain types and not Monobehavoirs.
     
  48. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    Nice performance results.
     
    AndersMalmgren likes this.
  49. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Not all of them, especially when objects are composed at runtime.

    And to be honest, GetComponent in Awake isn't really such a high cost.
    You have to consider that the serialization engine needs to do its thing as well, so if there's supposed to be a reference in a field, it'll take at least an internal lookup for the instance + field injection. That can be fast, but it does not necessarily have to be faster at all.

    Getting back to the topic, there is not THAT ONE solution. Some things are simplx better to be set up through code, for instance, if it's someting that a designer is not supposed to care about. Same applies to required components (which can also be enforced with the RequireComponent attribute + GetComponent).

    Not very suitable when you want to extend it. It also introduces extra ToString calls + corresponding garbage, whereas the constant (or static readonly) solution already leverages the benefit of interning strings, so there's no garbage.


    Well, it's not a real memory leak. And it's more than just the 8 byte. I'd say definitely clean them up anyway, even if it's just a routine that's triggered every 10 seconds to clean the cache from objects that pretend to be "null". Luckily, this works just fine with the overriden == operator.
     
  50. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,558
    I don't see to be hones less advantage, from other methods. All got pros and cons anyway, so need weight options. Just as you said.

    But this is very valid point. Personally I wouldn't be trying call string on per update basis. But that was example for tags. Which yes you are right.
    But in this particular case, I would have rather own tags based on enumerators, which I can cheaply compare for example, without using strings at all.