Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

"GetComponent" overusage - am I misunderstanding the design pattern here?

Discussion in 'Scripting' started by iEchoic, Mar 27, 2014.

  1. iEchoic

    iEchoic

    Joined:
    Dec 27, 2013
    Posts:
    14
    Hello,

    I'm new to Unity, and I'm doing some research into the best design practices for a game I'm planning. I recently stumbled upon this: "How to access a variable or method on another script or object"

    http://unitygems.com/mistakes1/

    This comes off as a bad code smell for me. As far as I can tell (and as this article suggests), GameObjects can't be treated as strongly-typed classes with well-defined interfaces, they're instead treated as property bags for which you can grab scripts (components) off of. If I'm understanding correctly, this violates encapsulation and is poor design - if I need to know what scripts a class implements, I'm reaching directly into the implementation and grabbing the internals of a GameObject instead of interacting with an interface.

    What I'd like to be able to do, for a rough example, is instead of a player hitting a mine and doing:

    Code (csharp):
    1.  
    2. void OnTriggerEnter(Collider other)
    3. {
    4.    var detonationScript = other.GetComponent<Detonator>() ;
    5.  
    6.    if ( detonationScript != null )
    7.    {
    8.       detonationScript.Detonate()
    9.    }
    10. }
    I'd like to do:

    Code (csharp):
    1. void OnTriggerEnter(Collider other)
    2. {
    3.    if ( other is Mine ) // Mine extends GameObject in this case
    4.    {
    5.       ((Mine)other).Detonate();
    6.    }
    7. }
    One thought is that instead of referring to objects via their GameObject implementation, I instead instantiate the component scripts with the GameObject they are associated with, and treat the corresponding GameObject as a property of the script. In this way, there would be classes interacting, not GameObjects.

    It's hard to imagine this is the intended pattern - am I missing something here? Is there a way to interact with objects directly as classes instead of diving into their implementation and grabbing the scripts?

    Thanks!
     
    Last edited: Mar 27, 2014
  2. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Just think of GetComponent as a check for interface implementation. Recalibrate your expectations according to those lines.
     
  3. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    .. and then eventually you end using CompareTag() as a means of interface filtering because you may need to validate for several types of collisions/triggers and I think its cheaper to do several CompareTags instead of several GetComponents

    For every tag I would create in unity , there would be an corresponding interface.

    Its still all a bit yucky but there really is no other alternative, Unity is a component based architecture / OOP hybrid of sorts and Using GetComponent is unavoidable
     
  4. booiljoung

    booiljoung

    Joined:
    May 12, 2013
    Posts:
    57
    Hi,

    Should player test mine?

    Mine should be explode any touch.

    Mine.cs

    Code (csharp):
    1. void OnTriggerEnter(Collider other) {
    2.     if (other.gameObject.tag == "Player") {
    3.         this.Explolde();
    4.     }
    5. }
     
  5. iEchoic

    iEchoic

    Joined:
    Dec 27, 2013
    Posts:
    14
    It makes more sense for each object in the hierarchy to have to extend GameObject, not be the GameObject base class. If this were the case, you could have strongly-typed objects that are still GameObjects and you wouldn't have to do this type of silly manual detection of interfaces.

    It looks like I'll just have to work around this. Thanks for the responses.
     
    Last edited: Mar 27, 2014
  6. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    @booiljoung

    gameobject.tag comparison allocates memory and is slower than CompareTag()

    also you can call CompareTag directly on the Collider reference eg:

    you should do this instead :

    Code (csharp):
    1. if (other.CompareTag("Player")) {
    2.  
    3.         this.Explolde();
    4.  
    5.     }
     
    Last edited: Mar 27, 2014
  7. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    Takes some time to adjust your paradigm..

    Technically GameObject can be thought of as representing a empty physical Entity in the scene. While it obviously does have underlying code representations inside Unity Engine , it is essentially a compiler driven black box.

    Enter The Component...

    The purpose and functionality of an Entity(GameObject) is defined via components. At a minimum all GameObjects have a mandatory Transform Component added to them when created.

    There is a Base class for all components but you never actually create the base class of "Component". This is extended to a "Behaviour" class and this is again extended to a "MonoBehaviour" class in the Unity API.

    Enter The MonoBehaviour...

    So from a GameObject Component Scripting point of view , MonoBehaviour is the entry point for all of us users. All Scripts that are attached to GameObects need to extend MonoBehavior.

    Then the other special class to learn about would be ScriptableObjects. These are NOT attached to GameObjects but do support Serialization.

    Then you have you regular regular Object class extensions ( not serialized without special attributes ) and structs etc.. these you create within your MonoBehaviours as needed.

    and finally Interfaces... Monobehaviours can use interfaces and you can engineer many aspects of your logic to function according to interface implementations as you would expect.

    but alas when it comes to internal methods like OnTriggerEnter there really is no other option but GetComponent .. unless you have some very clever event/delegate system.

    I have not investigated that fully myself and settled on hooking into the Component system but feel free to see if you can engineer a fantastic delegate solution and then share the code with me ;)
     
  8. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    What?

    A GameObject is a container. It's sole job is to store references to a bunch of other components which are related to one another and (probably) require a shared Transform component.

    I think there might be a terminology mixup here. A class doesn't implement scripts. A GameObject is a container, not a class to be extended. When you GetComponent you're not "grabbing the internals" of the GameObject, you're referencing one of the components that it contains which, yes, is by design.

    You seem to be thinking with a very inheritance-based point of view, where Unity is built around a very composition-oriented philosophy. It's not a "violation of encapsulation" or "bad design"*, it's just conceptually quite different to what you're used to. A GameObject is a container and each of the components in it represents some functionality. The component level is where the encapsulation should happen, not the GameObject level.

    I suggest checking out "composition vs inheritance", then hitting up the manual page on GameObject and going from there.

    * Though Unity isn't perfect. A classic example of this is how we're encouraged to make stuff public so it's Inspector-visible (though there are alternatives, and I suggest using them!).
     
    Last edited: Mar 27, 2014
  9. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    read this article why it is advantageous in gamedevelopment to have gameobjects as collection of components rather than monolithic classes implementing several interfaces.

    EDIT:
    with require component you can ensure that a gameobject has components attached your script depends on.
     
    Last edited: Mar 27, 2014
  10. iEchoic

    iEchoic

    Joined:
    Dec 27, 2013
    Posts:
    14
    Admittedly, I am thinking from an inheritance point of view. But I fail to see how this doesn't couple an observer directly to the implementation of a subject. For example, if I have a subject called "player", and my implementation of player puts some of its actions (such as Run, Jump) in a Movement component, and the others (such as Attack) in an Actions component, my observer needs to know about this implementation detail. My observer doesn't (or shouldn't) care about this. It wants the player to attack - it shouldn't care if it's in a "actions" component or a "player" component. Yet, it needs to know this. If I decide to refactor the implementation of the Player prefab and break these into different components, I now need to refactor all of my code that utilizes this class. Again, I fail to see how this is advantageous. I don't think making things inspector-visible is good justification for this design, since it hampers the design of an engine that does not rely on the inspector for defining/modifying values.

    From a purely practical point of view, it also produces ugly (and probably less-performant) code, where you're constantly checking for components instead of interacting with rich objects. Attaching components via the Unity UI also provides inferior IDE support in VS/MonoDevelop.

    I'll read exiguous' link and give it some more thought. But I'm not seeing it right now. It may violate some important design principles, and it indisputably produces uglier code and inferior IDE support.
     
    Last edited: Mar 27, 2014
  11. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Let me see if I can answer some of your questions, albeit I'm tired ATM.

    To be fair, it does not. Unity has a SendMessage function which negates the need of knowing which components implement what. If you like strong typing, you could probably implement something that works off of interfaces (e.g. |Attack which could be implemented by PlayerMB or AttackMB as you desire.)

    Don't think from a 'is a' perspective but from a 'has a' perspective. You want a player to be able to move? Add a move component. Want him to have health... add a health component. Very quickly and easy - can be done ahead of time or at runtime.

    Here's (hopefully) a simplistic example of a logical structure that does not lend itself to a hierarchical inheritance tree:

    I have a object that has no health, but has movement. E.g. Projectile
    I have another object that has no movement, but has health. E.g. Barricade.
    I have yet another object that has movement and health. E.g. Player.
     
    Last edited: Mar 27, 2014
  12. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I saw that post yesterday just before I was about to go crash, with some sickness making my head spin. There's so many thing wrong with it, that I wanted to reply, but didn't have the energy.

    Good morning!

    GameObject are sealed, spatial container of components. Nothing more. They are not property bag. It's a component-based design. They are component bag.

    Also, be careful when you say "script". The "script" is the text file that define a class. Unity requires any class deriving from its own internal type to be name the same way as the file holding them, because serialization pass by the file and not directly by the type.

    When you are at the point of picking object from the GameObject, there is no script anymore, as everything was compiled and instantiated. What you grab are objects.

    It does not violate encapsulation and is NOT poor design.

    As you can see, component-based approach still fulfill the requirement for proper encapsulation. However, it's an smaller encapsulation, where specific independent behavior are encapsulated individually.

    An interface is a method/property contract between unrelated class. Nothing prevent you from implementing interface and using them.

    Code (csharp):
    1.  
    2. public interface IMyInterface { }
    3.  
    4. IMyInterface[] interfaces = gameObject.GetComponents<IMyInterface>();
    5.  
    Your example is NOT about using interfaces, but about using inheritance, which works fine is you understand that your class inherit from MonoBehaviour : Behaviour : Component : UnityEngine.Object : System.Object. It wouldn't make sense to derive from the spatial container, because you will not change that specific behaviour, which is to be placed spatially and hold a collection of behaviour. In that sense, it's perfect that GameObject is sealed.

    And yes, it is the intended design pattern. Look up component-based design. http://en.wikipedia.org/wiki/Component-based_software_engineering

    To be fair, I think SendMessage is rather weak, and I never use it. If I know before hand what method I should call, I could make an interface implementing that method and have all the MonoBehaviour that is required to implement it.
     
    Last edited: Mar 27, 2014
    AbandonedCrypt likes this.
  13. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    A game engine like Unity and rich media application like flash all have an IDE , all have physical objects and a scene and have coding structures. They also can have many people with different skill sets working on a same project at any given time.

    From a pure programmers point of view they want everything to be achievable via code without ever looking at the IDE, 100% code centric.. but this point of view does not aid the designers / content developers who want to work with visual objects and really want to have your code exposed to them in the most user friendly manner as possible.

    I used to work as a senior flash developer in a media agency that constantly had too short deadlines and many people working on a project. After a few years I grew to learn the best Coding solution I could provide for the project is the one that a non-coder can also use with the least amount of effort.

    Unitys' component system IDE inspector integration is perfect for this.
     
  14. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    I"d like to point out that there is actually 2 distinct things that are interfaces.

    There is the concept of interface, being the publicly accessible parts of a type. It is the other side of encapsulation, you encapsulate away the stuff you don't want other objects to be able to access, and the interface allows restricted access to what you would like (as method, properties, etc).

    Then there is the interface construct in .Net/Mono which allows you to define the interface concretely, so that a type can implement it in a type safe way.
     
  15. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Also note that the non-generic overload of GetComponent works with interfaces and you could achieve something similar with the generic overload by creating an abstract base class that inherited from MonoBehavior.
     
  16. Abu-Faisal

    Abu-Faisal

    Joined:
    Sep 11, 2013
    Posts:
    15
    -wrong turn-

    GameObject = Script = Component => all of them are object (in correct wrod they are classes).

    GameObject is class that has built in components (just a variable pointing tocomponent class) which you can use without using GetComponent (e.g. somegameobject.renderer). Also, it has a custom components (also just variables pointing to class) which isn't not public (I'm not familiar with internal Unity structurs, but it can be array, list, private ... etc).

    To access those custom components, you need to use reflection to get reference to them which is headache. Unity gives a builtin method to do the reflection for you which is GetComponent.

    As I know from programmer point of view, reflection is the best if you are going to build a customizable framework as Unity did. I can't think of another good way for that. Reflection is always there and can be used where ever (in Unity or other application). if you know object type or name (by hacking or you just know), you can reach that reference as easy as that. It isn't against these days systems law and no one can prevent that.

    Also for IDE reflecting code for non-coder, as coder build custom inspector. Unity developer can't build inspector that based on your unknown custom component.

    -back to right turn-
     
  17. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    For what it's worth, the 'build in components' being discussed are not direct references to an always available component. They are simply wrappers for GetComponent. Behind the scenes:

    Code (csharp):
    1.  
    2. Renderer renderer = gameObject.renderer;
    3.  
    Is really just doing:

    Code (csharp):
    1.  
    2. Renderer renderer = gameObject.GetComponent<Renderer>();
    3.  
    You should still be caching components, and outside of .transform and .gameObject you should never rely on them actually being there. Always null check.
     
  18. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    So this is the second time I've read this in relation to 'transform'.

    Is it true that the 'transform' property ALSO is just a 'GetComponent' call? I'd think Unity would have cached that reference for you considering that ALL gameobjects have transforms.
     
  19. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Yes, it is. If you do multiple .transform every frame, you could gain noticeable performance by caching it. It also depends on the number of Component on your GameObject.

    Also, just to be clear, for everybody "Class != Object".

    Class is the blueprint to create an object.

    Object is the instance existing in memory, created from a class.

    Type is an object holding the compiled representation of a class.
     
  20. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Actually, it's not, and you'd have to do really a lot of .transform accesses to have anything close to "noticeable" performance benefits by caching. Do some benchmarks if you don't believe it. :) .transform is cached, and is not just a wrapper for GetComponent(Transform), although you can improve on it a little (something like 25%) by caching it yourself. In terms of time taken, manually cache < .transform < GetComponent(Transform) < GetComponent<Transform>().

    --Eric
     
  21. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I already benchmarked it a while ago... I know it's not "a lot", (in the ms range only in tens/hundred of thousands of uses) but it's one of those very easy optimization. We made a base class that do it automatically on startup.
     
  22. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    Good, that's what I was hoping to hear.
     
  23. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    There's a lot of stuff wrong with this original post. But really it's just a case of developer dogma. Writing code is really hard, it's probably the most failure prone modern discipline. When people have success using some approach (or set of approaches) they tend to latch onto it as the 'one true path of software development', then apply whatever rules they can everywhere. Thinking of "getcomponent" as a violation of encapsulation really is a nice example of that. One would naturally go further and start to talk about how you're also violating the law of demeter and all kinds of S*** (also happening in this thread indirectly).

    That said, even though there is a lot that's wrong, the example iEch used to start the discussion is a good one: collider interactions.

    I think that most of us do try to avoid manipulating foreign components most of the time, since grabbing components farther away from a given script will generally make assumptions about other entities and their structure. Making these kinds of assumptions can be dangerous and constraining, so I think that most people tend to restrict component access to the game object itself and perhaps to its children. Basically saying "I can manipulate the entity I am attached to".

    The major case when we have to exceed that is - like in iEch's example - collision (game entities triggering effects in other entities). And there is something worthwhile in discussing different ways of controlling interactions - or the constraints that we place on coding them.
     
  24. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    Well... no. It doesn't create ugly or less performant code unless you insist on sticking to old ways. This isn't just about writing code differently, it's also about the design and the whole of the implementation cycle.

    It sounds very much to me like you're still thinking of GameObject as a class even if you're calling it something else - you're expecting to be able to treat it as one and not have issues elsewhere. If your observer shouldn't have to care about what's in a GameObject then why are you even giving it a reference to the GameObject? If your observer needs to be able to move something then why not just give it a reference straight to the Mover component? You're right in that it shouldn't have to care about the GameObject, so why are you explicitly making it aware of the GameObject instead of the thing you care about?

    Next, you're working against Unity's intended workflow if you intend to hook everything up in VS/MonoDevelop. It's making life harder and your stuff less flexible for... what? Because visual tools make some programmers feel wonky? Having said that you can do things from code if that's what you want to do. I've done projects that way before and, while I wouldn't do it again (as I find other approaches to be far more productive), it does work.

    It does really seem to me that you're trying to project OO expectations and best practices onto a CO system and being bothered that it's not turning out the way you want. All I can suggest is that "When in Rome, do as the Romans do". I've been there before, I know how hard it is the first time... ;)
     
  25. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    Absolutely. My typical model is that any component can mess with the GameObject that it's attached to. If a component on one GameObject wants to have some effect on a another GameObject it lets a relevant component know (e.g.: call a Damage function on a Health component), and that component is responsible for modifying its GameObject accordingly. This keeps things clean - all I have to know from the caller is that there's a Health component, the Health component is responsible for correctly doing anything else that needs to be done.

    Combining this with a solid event system (for when I don't necessarily know what the relevant component is) leads to super flexible design and implementation, small components which are easy to maintain, and code that is both clean and robust (I can typically add/remove things without other things caring regardless of what they are - the functionality of the removed thing just ceases to happen and everything else rocks along).

    I don't see how that's different. That works just like any other interaction in the modes I just described.
     
  26. Abu-Faisal

    Abu-Faisal

    Joined:
    Sep 11, 2013
    Posts:
    15
    Building a framework that will be customizable need a workflow and policies. For example let's take C++ or C# in Visual Studio. If you want to design a program you need to follow the rules for example you must have a function as entry point for the program such as main for console programs or winmain for win application. So building a framework that will run anything up to the user without clear policies or workflow is almost imposible.

    In unity, process is to build objects based on predefined classes by Unitydeveloper to meet thier workflow. Those classes are not open source so you can't declare any public field on them but for some which not sealed you can do method extensions which meet iEchoic requirement. Unity deals with custom components as an objects (instances) of custom classes saved in a field in builtin Unity classes you can enumrate it and get a reference or value using getcomponent.

    Also, Interface or GameObject will not make a different. GameObject is class and Interface is just a template for a class both of them will end up as an object at runtime with the same idea they are just instances of classes. How to get what you want from them it doesn't matter because it is the same idea if you look at it from security point of view. what you want must be known before you ask for it in both cases (except if you are an expert hacker :D).

    [out of the subject] As an advice for creating game logic and I will use Echoic main post example, the detonation must be controlled by the mine not the player. In real life, if someone step on a mine, the mine will detonate itself it will not wait for the person call. This logic will make a big difference for securing Online Games.

    Sorry for the long post...
     
  27. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    No, it is not true. At least, when I tested this claim it turned out to be false:

    http://forum.unity3d.com/threads/130365-CachedMB?p=879615&viewfull=1#post879615
     
  28. Fellshadow

    Fellshadow

    Joined:
    Dec 2, 2012
    Posts:
    169
  29. iEchoic

    iEchoic

    Joined:
    Dec 27, 2013
    Posts:
    14
    You may be right in many regards here - and maybe you've suggesting that this is a problem that is limited to collider interactions, which I hope is true - but something that not a single person in this thread has been able to justify is why the component pattern in Unity produces such ugly code. If people on these boards think that frequently calling "GetComponent" on GameObjects is a symptom of good design, then the dogma is clearly in this community. I look at code examples and see "GetComponent" splattered all over the place. This is ugly code - and ugly code is a symptom of ugly design.

    Is this ugliness limited to collider interactions?

    Is there a way a component can detect collisions between GameObjects without being aware of the GameObject?

    This may be the best answer here. However, when working with a class-based infrastructure (GameObjects as well as every other scriptable component are indeed classes) in a richly OO language (C#), this ends up being a natural reaction.

    ---

    I have definitely learned a fair bit from this thread, however - it is clear to me that what I'm looking for is not possible in Unity, and that is by design. I'll try to adjust to this new paradigm and try to put my reservations on hold.
     
    Last edited: Mar 30, 2014
  30. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    This is horrible fallacy, that I've noticed in a lot of intermediate programmers (4-7 yrs).

    Some of the worst code I've ever seen has been written in a quest for marginally prettier syntax. I've seen people make tremendous sacrifices to performance and simplicity in design for something that makes the code more superficially elegant: "prettier code".

    It's incredible hubris to look at an environment as flexible and successful as unity's - one with which you clearly have little to no experience with - and assume that you know better. Especially considering your examples and complaints are so utterly superficial (please review the code from your original post).
     
  31. iEchoic

    iEchoic

    Joined:
    Dec 27, 2013
    Posts:
    14
    I am in no way an intermediate programmer - your inability to make a compelling argument without resorting to ad hominem says more about your level of talent than mine. And I am in no way complaining about ugliness in a superficial sense - I am complaining about prettiness in a conceptual sense - code that is littered with unstructured attempts to grab strongly-typed information off weakly-typed property bags is ugly in every paradigm and in every language (and I've seen it in many paradigms and many languages).

    But this conversation is going nowhere, and I have no interest in continuing a conversation where your primary argument is "people who disagree with me are bad at programming hur hur", so I'll be done now.
     
  32. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    In a 35k lines framework we use, we have 52 times "GetComponent" and 5 "GetComponents".

    Frankly, it's a tool from Unity that you cannot not use. Do people tend to overuse it? Yes, probably. Should you avoid it? No, not at all.

    However, it's a part of a video game engine that makes it awesome too. Let's say I have a grenade going off, and I want every destructible object around to break from the damage. I could just do a SendMessage to every object around, and THAT, in my opinion, would be a flawed design. It means they need to test, by reflection, every single component of every single game object for the proper method implementation. On top, if my method changes name, the "SendMessage", being a string would not be updated. The solution I tend to use is to do a GetComponent for a IDestructible interface.

    In the end, in a component-based approach, you need a way to retrieve components!

    Tell me, how many component-based framework have you worked on? And how would you retrieve components from a components-holder in an expansible framework?
     
    Last edited: Mar 31, 2014
  33. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Forgive me for insulting you.

    Your original example of good vs bad:

    Bad:
    Code (csharp):
    1.  
    2. void OnTriggerEnter(Collider other)
    3. {
    4.    var detonationScript = other.GetComponent<Detonator>() ;
    5.    if ( detonationScript != null )
    6.    {
    7.       detonationScript.Detonate()
    8.    }
    9. }
    10.  
    Good:
    Code (csharp):
    1.  
    2. void OnTriggerEnter(Collider other)
    3. {
    4.    if ( other is Mine ) // Mine extends GameObject in this case
    5.    {
    6.       ((Mine)other).Detonate();
    7.    }
    8. }
    9.  
    I consider the difference here to be deeply superficial. It seems as though you're applying the law of demeter (http://en.wikipedia.org/wiki/Law_of_Demeter) too literally. Saying that a component based interface check is inherently ugly, whereas an inheritance based interface check is clean. I would hope that we can both agree that, perhaps, in hindsight the difference here is a bit superficial.

    Now, I am not saying you should litter your code with getcomponent calls, and I think we would both agree that littering code with type checks (in whatever form) is a sign of something going wrong. But your arguments tend to be fairly normative:
    Other than repeating that the code is ugly and citing overly literal interpretations of the law of demeter:
    I can't really see what your argument is, except, the above two points. Sorry if I thought you were an intermediate programmer.

    Finally, there's a lot of code on these boards, and in the "answers" section that solves some very specific problem, or acts as an example. Often times this code is littered with GetComponent calls. But this code is almost never an example of "excellent code", it's more designed to be a terse and direct solution to someones problem. (especially given that there is a huge range of experience in the developers on this board - as I'm sure you've noticed).
     
  34. iEchoic

    iEchoic

    Joined:
    Dec 27, 2013
    Posts:
    14
    Fair enough, thanks for the apology.

    I'm still trying to understand myself why this feels wrong to me, so my thoughts may be somewhat scattered. I think a better way to explain it is this: C# is a statically typed language, and this is its great strength. This buys us all kinds of fantastic things. When I have an class called a "Mine", I know that it implements a method called Detonate. More importantly, my IDE and my compiler know it. In fact, the whole concept of a 'class' is a red herring here. This is about typing, not OO - these can be interfaces instead of classes or even implemented as contracts represented as a property on an object.

    Admittedly, my code example doesn't accurately describe this problem. This is a more realistic comparison that more accurately describes the issue:

    Bad:
    Code (csharp):
    1.  
    2. void OnDetonatorEnter(Collider other)
    3. {
    4.    var detonationScript = other.GetComponent<Detonator>() ;
    5.    if ( detonationScript != null )
    6.    {
    7.       detonationScript.Detonate()
    8.    }
    9.    else
    10.    {
    11.       throw "The fact that I forgot to attach this component won't be caught until runtime";
    12.    }
    13. }
    14.  
    Good:
    Code (csharp):
    1.  
    2. void OnMineContact( Mine mine ) // Mine extends GameObject in this case
    3. {
    4.    mine.Detonate();
    5. }
    6.  
    This is no longer superficial. Names are important. Compile-time validation is important. The first sample is ugly, the second sample is elegant. The fact that I don't need to rely upon a runtime GetComponent() call to find type information allows this kind of behavior. I can place a Mine into my scene and my environment knows it's a mine. If I refer to the wrong object in my scene and expect it to behave like a mine, I'll get a compile-time error (and most likely, I wouldn't do it in the first place because my IDE would object).

    In all, this feels like a very scripting-esque pattern. If I was programming this in JavaScript, this would feel natural. This feels unnatural to me in a language specifically designed to eschew these type of patterns. I am not very experienced in component game design patterns. But I am experienced in C# and this type of runtime type inference is something that sets off warning signs.

    Unity encourages patterns that C# discourages, and uses C# as a first class language. It forces you to treat everything like a grab-bag of properties in a language designed to treat everything as an object with an encapsulated set of capabilities. Its SDK is deeply OO but its editor/object model is deeply component-based. It feels strange in C#. I am guessing it would feel right at home in JS.

    It feels almost like pseudo-duck-typing. Even if each component is strongly-typed, the collection of components comes together to make a duck. And then you treat it as a duck.

    I can agree with this. My example was not well constructed.

    About the law of demeter and encapsulation: my reasoning was that if I had an object with a set of capabilities (an interface), and I want to refactor the internal implementation of that object's capabilities, I cannot do so without changing the implementation of its dependencies. This is the classic measure of coupling - which is what this is about (which is related to the law of demeter but not the same).

    The flaw in my logic here is that encapsulation is violated if I am considering an object a logical container as opposed to considering a component a logical container. I shouldn't think about refactoring an object's internal implementation, but instead, I should think about refactoring a component's internal implementation. As I've learned, Unity only supports the latter.

    This probably plays a part as well - most examples I've seen, in the interest of being self-sufficient and terse, rely upon grabbing GameObjects and all necessary components in a single function for the purpose of the example, instead of handling relationships and operations in their own classes. Note that my thread title did complain about "overuse", not "ever using".
     
    Last edited: Mar 31, 2014
  35. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I think I understand your issue now... You think in a code-driven approach. Yes, it is "better" code strong-type each behavior implementation. It is also horrible for any kind of extensible framework or any kind of data-driven editor.

    In the case of your "Mine" example, it would mean this class would have to implement everything that a "Mine" requires, such as visual, physic, spatial position and so on. If it derived from "GameObject", you could assume that the base class implement the spatial transform. However, what should Mine derived from?

    Mine -> Visual -> GameObject?

    or

    Mine -> Physic -> GameObject?

    Or should you make a base class that implement both Physic and Visual? What if you want only some physic and no visual... or some visual and no physic?

    What if it needs particules? What if it needs sounds? What if it needs a rig and a skeleton? Would you code parent class that are combination of all the potential possibilities? Should you bloat your code base with the potential combination?

    You see, a strong unified typing in this case would make the most horrible class inheritance in history.

    Also, your approach is too rigid. Let's say I want object to be destructible. I also want door to be interacted with, so the player can open or close them. On top, I want some door to be destructible, but some are not. In a rigid strong-typed approach, I would code my door with every potential situation. In a component approach, I will make a "Door" class and a "Destructible" class. The designer will combine them the way he sees fit. A GameObject with the Door class will be interactive. The Door with a Destructible will break down. The crate will not have the "door" class, but will have "destructible" and will break down.

    I come from a designer environment, so the data-driven, for me, is a given. Everyday I have to explain to new programmer why they shouldn't make their code to handle everything, but make it generic so a designer can combine the pieces like lego.

    A blog post I made about that a while ago; http://lightstrikersoftware.com/index.php/blog/6-oop-vs-component
     
  36. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    LightStriker has some great points about this at a high level, and why in broad strokes the component based system is so damn good for game development. But I wanted to address some of the details...

    Pseudo-duck-typing is one way to look at it, and probably not a bad one. I look at it like this:

    By putting together a game entity using distinct components, you're sort of generating a full set of interface declarations at runtime. You can even modify the entity at runtime, adding and removing if needed. It's actually pretty cool what's possible with that. It's very dynamic, which I think is why you mention feeling like javascript or duck-typing - although it's also a bit different from the free-for-all of javascript, since you're composing it using strongly typed elements. After getting used to it, it's impressive, and awesome. I am willing to bet you'll agree once it becomes familiar, and you've figured out how to fit it into your specific project and workflow.

    Now here's an example with some meat, and one that I've sort of struggled with as well.

    At first, the 'built in' way to handle this is probably with http://docs.unity3d.com/Documentation/ScriptReference/Component.SendMessage.html. IMO, the key to the value of sendmessage is the smart dispatch. It will call the appropriate overload for a given parameter. This can allow you to really put together a loose, powerful, flexible design. For some, who have the discipline to keep track of this flawlessly, it will produce fast, clean, very flexible code (because of the dispatch). Personally, I know myself, and I need the compile time checks, so despite the power in sendmessage, I avoid it. Someone coming from a javascript or python background, or someone willing to put together robust unit tests for everything could probably get more out of it.

    My game has a bunch of independent AI driven agents that run around, chasing eachother and doing stuff. The collision triggers I use for them (since the result is highly varied, and I wanted to plug in more behaviours later) is inheritance driven. This looks something like:
    Code (csharp):
    1.  
    2. class RaiderEncounter : BaseEncounterHandler{ // : MonoBehaviour
    3.      protected void OnTriggerEnter( Collider encounter_collider ) {
    4.        collider.GetComponent<BaseEncounterHandler>().NegotiateEncounter( this ); // real code is more complex, since both agents have colliders that get triggered.
    5.      }
    6. }
    7.  
    This still suffers from the "compiler won't tell me I forgot to add this component" problem, and honestly, I hate that.

    There's also event based systems, etc. But at the end of the day, I think that just about all of them will either come down to "GetComponent" or "SendMessage" <-- no compiler check for forgetting to assign the script (even if you're wrapping GetComponent/SendMessage in an event system).

    My girlfriend is dragging me away to dinner, but I'd love to hear how other people approach exactly this problem/specifically with collisions.

    Honestly though, I think the trick is really just to give up on help from the compiler on this and either go for unit testing to fill the gap or just learn to live with annoying bugs now and then for forgetting to organize some game entity the way that code expects it.
     
    Last edited: Mar 31, 2014
  37. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    Yeah... the collision callbacks receive Collider or Collision parameters, not GameObjects.

    It may be "natural", but it's still premature. Component Oriented development is a style of Object Oriented development. You're taking best practices from a different style of OO development, applying them to the CO style, and claiming that the style is bad. That's kind of like complaining that a screw makes a terrible nail when you hit it with a hammer - the statement is true, but it misses the point.
     
  38. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,614
    I've never experienced it as a problem. And I don't mean that as a cop out, it just flat out hasn't ever been a thing I've even considered as requiring a solution.

    Having said that, my coding style means that the Editor tells me quick smart at runtime if I've forgotten to hook most things up. If a component can't find something that it needs then it'll tell me. So sure, the compiler isn't flagging it, but my workflow does. Many programmers have negative reactions to that at first take, but consider this - the guys using the tools I create don't compile anything. They assemble stuff in a scene. They don't want and couldn't use a compiler error. They want the Editor to tell them when they hooked something up incorrectly and preferably to give them a non-programmer friendly explanation of what's up, so I have to provide that anyway - it's not "extra work".

    On another note, I don't consider having GetComponent in code to make it "ugly". If the code is unclear of purpose, hard to read, understand or maintain then I really don't think the cause is the use of one particular function.
     
  39. exiguous

    exiguous

    Joined:
    Nov 21, 2010
    Posts:
    1,749
    that was what my link refers to. especially the player class must be include "everything" and often becomes a superclass which is a nightmare to maintain and change.
    in unity you plug a component onto an object and simply check wether an object provides the functionality you want to interact with or not. if so treat it as such an object, if not, ignore it. i see nothing wrong with it.

    some points:
    components can be attached to gameobjects during runtime. so there cannot be a compiletime check.
    components can also be removed during runtime when not needed any more.
    when you fill an array/list at runtime do you complain that you need to search for an object in it?
    you also need to check wether references are valid or null (in C#!).
    unity is not only for programmers, the editor can also be used by designers which have no clue about programming. imagine they would change your code and your inheritance hierarchy to their needing. i think this would be a nightmare for you. so when they only deal with components they can break nothing on the code level.
    SO unity uses C# features and does nothing nasty, its just another approach/methodology. and some people think its superior to OOP in some cases for some applications.

    so unitys system might be not as strict as a programming language but it is not meant to be imo. it is an engine/framework which requires certain features and they are implemented the way it is. albeit unity utilizes C# does not mean it is pure C#. the code is used to control the engine.
    could it be better? certainly, as nothing is perfect.
    could you do it better? good luck.
    should you use the engine? your choice.

    your attempt to achieve perfect/pretty/strict code is a goal of you, and probably not a bad one. but in reality not all programmers have this as their top priority (for several reasons). and not all frameworks/api's supports that to full extent (for several reasons). so you are free to use an engine which does support what you need. otherwise learn to deal with it. and you are also free to ignore getcomponent.

    author unclear
     
  40. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I think this is a strawman, because there is no way a game engine could have a built-in method named "OnMineContact" that referenced a user-defined class called "Mine" unless it traveled to the future and read your mind. Whether it used inheritance-based game objects or component-based ones, you would have to write that method yourself, and figure out a way to have it get called. How are you envisioning this happening in your inheritance-based engine? I'd guess you would have to have a Player class that extends BaseObject and put an IMineContactable interface on it that contains the OnMineContact method, and then you would have to make a MineCollision class that extends BaseCollision and then override a virtual method and basically do this:

    Code (csharp):
    1.  
    2. public override void OnCollisionEnter(Collider thisCollider, Collider otherCollider)
    3. {
    4.    
    5.     if(thisCollider is IMine  otherCollider is IMineContactable)
    6.        ((IMineContactable)otherCollider).OnMineContact((IMine)thisCollider);
    7.     else
    8.      {
    9.           throw("The fact that I forgot to implement one of those interfaces won't be caught until runtime");
    10.     }
    11. }
    12.  
    So you still have to do all the work of figuring out if the thing you're colliding with is a mine and if the thing colliding can handle a mine collision, and it doesn't look "pretty" at all.
     
    Last edited: Mar 31, 2014
  41. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    I agree, how *dare* Unity not use strict typing with C#! That goes against *everything* C# stands for!

    I mean, when I dabbled in ASP MVC there was t̶h̶r̶e̶e̶ one d̶i̶f̶f̶e̶r̶e̶n̶t̶ ̶w̶̶̶a̶̶̶y̶̶̶s̶̶̶ way of passing data to a view! It was ViewData... which was a loosely typed dictionary... No! That doesn't sound right! Maybe it was the Viewbag which was a dynamic type with no static compiler support! Hmm, no maybe it was ViewModel!

    Yeah, that's right. C# only supports ViewModel. That's the only way to deal with objects in C#!

    ------

    From the sounds of it you haven't actually tried to use the component model of Unity and are trying to imagine flaws... which while interesting seems to go against the collective wisdom of those who use it. How about trying to use it?

    For example, stop thinking of a 'mine' as a class. For me a mine is the following:

    • A GameObject.
    • A Collider/Trigger.
    • An Apply Damage component.
    • Various Particle Effects.
    • Various Sound Effects.
    • A Die on Contact Script (or some variation).
    Good news, once these scripts are made... I do not have to declare or maintain the 'mine' entity - and making changes to its functionality is easy. Even better, it helps collaboration! A game designer specced out the mine, a level designer placed it. A modeller created the artwork and my shader guy made it look awesome. My SFX guy made the eye candy and my audio guy made my ears hurt.

    My programmer didn't waste his time doing any of the above (because his hourly rate is comparatively high, and his aesthetic taste could do with some work). However, since we did make the game networked last minute, he modified the Apply Damage script accordingly - that functionality which worked on 18 different entities within the game.

    Each component has a single responsibility, not multiple. Each component is open for extension. A gameobject represents a collection of client specific interfaces, and with some basic effort you can make each component implement an interface which allows my code to rely upon abstractions and be replaceable by their subtypes. Yep, looks like to me the code is SOLID.
     
    Last edited: Mar 31, 2014
  42. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    Just to be clear, OOP is a language paradigm, while Component based design is an architectural paradigm. They are not mutually exclusive, and one does not requires the other.

    Nowhere in any OOP definition is it stated that everything must be strongly-typed all the time. Actually, a polymorphic container is one of the advantage of OOP.

    Exactly. The only two things in Unity that really rustled my jimmies is "Invoke" and "SendMessage", which both takes a string as a method name. I sometimes pass a method name by string, but only when I have no alternative (such as Attribute's argument). However, those two MonoBehaviour's method does not have the excuse of not having alternatives;

    Invoke should take a method, not a string, since it calls it in the same instance.

    SendMessage is - in my opinion - for the lazy who don't want to declare some interface. Someone should do some benchmarking one day of the difference between SendMessage and GetComponents(typeof(IMyInterface)).
     
    Last edited: Mar 31, 2014
  43. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    To be slightly off topic, I've wondered for a while how useful they'd be if they were strongly typed. Possible Implementation:

    Code (csharp):
    1.  
    2.  other.Message<IHealth>(h => h.TakeDamage(50));
    3.  
    But haven't found a use-case to experiment with :(
     
    Last edited: Mar 31, 2014
  44. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    It's funny that I argue against superficial 'pretty' code earlier and then add this post but...

    Personally I avoid SendMessage because I don't have the discipline to manage strings. But for those who do, I think it can save a lot of time and unnecessary code. I am aware that literally anything that can be achieved through sendmessage can also be achieved with a combination of generic interfaces and getcomponent, but the point is that if a programmer comes from a hardcore dynamic language background or just has the discipline to manage string names, then what SendMessage offers is essentially an implementation of multiple dispatch.

    Code (csharp):
    1.  
    2. class Whatever : MonoBehaviour{
    3.   public void OnCollision( ExplosiveDamage arg ){ ... }
    4.   public void OnCollision( MeleeDamage arg ){ ... }
    5.   public void OnCollision( LifeBonus arg ){ ... }
    6.   public void OnCollision( SomeOtherThing arg ){ .. }
    7. }
    8.  
    9. ... SendMessage( "OnCollision", this );
    10.  
    The funny thing is that this is close to the classic example of multiple dispatch:
    http://en.wikipedia.org/wiki/Multiple_dispatch

    Where you have spaceships and astroids collide, and you want to route the method calls to the appropriate overload.
     
  45. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    I come from a background with project in the tens of million of line of code with hundred of coders. Managing hard-coded string is really not good in kind of environment. I understand the appeal of SendMessage for the few lines of code that it saves you and is probably easy to manage in a small project with only a handful of coders. Even in a small group - as I am now - when someone change a string without telling others, it makes me waste hours tracking what happened.

    Your example - I would argue - is not a good one. Packaging all kind of different behaviour within the same method call is bound to bring errors at some point.

    Code (csharp):
    1.  
    2. class Whatever : MonoBehaviour
    3. {
    4.     public void OnDamage(object sender, Vector3 impact, float damage, DamageType type) { }
    5.     public void OnHeal(object sender, float heal, HealType type) { }
    6.     //etc...
    7. }
    8.  
    An object receiving damage - as example - most of the time, doesn't need to know what type of component sent it. Taking it from a modular approach, breaking the dependencies, means the "OnDamage" can be call by everything. If the designer wants, he can calls it in a cinematic, or when the player presses a switch.

    The multiple dispatch has the very problematic issue that as the number of object that can interact with each other increases, so is the number of overload in a N^2 way. Of course, nobody would implement method signature for every potential combination, but you understand my point; there's alternatives to enforcing hard dependencies.
     
  46. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    String based calls are never a good solution in a production environment.
     
  47. shaderbytes

    shaderbytes

    Joined:
    Nov 11, 2010
    Posts:
    900
    Well said exiguous

    C# delegates/events are a perfect example of the last point mentioned in regards to null reference checks.

    another great quote.. exiguous you were on a roll here ;)

    Design pattern paradigms are great conceptual ideas but not always great practical ideas. For example most designs patterns are not pragmatic about real performance scenarios.

    anyway here is to hoping you get settled with the changes in approach and make a great game using Unity ... and GetComponent ;)

    cheers
     
  48. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    You could do it with an extension method:
    Code (csharp):
    1.  
    2. public static void SendMessage<T>(this GameObject obj, Action<T> action) where T : Component
    3.     {
    4.         var components = obj.GetComponentsInChildren<T>();
    5.         foreach (T component in components)
    6.         {
    7.             action(component);
    8.         }
    9.     }
    10.  
     
  49. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,717
    If I could upvote this post, I would. No doubt I'll look into it more to see if I can get something similar in our own framework.
     
  50. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    I know (hence my syntax choices) :) I just haven't been dabbling in the client side to see if it makes life easier or not.