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

C# classes that don't have corresponding physical object in world

Discussion in 'Scripting' started by willyfreddy, Nov 5, 2011.

  1. willyfreddy

    willyfreddy

    Joined:
    Mar 28, 2011
    Posts:
    50
    Hi there,

    I'm switching to Unity/C# from a DirectX/C++ background and, although I'm pretty comfortable with C# itself now, I'm having a bit of a time getting my head around this new approach to coding in Unity with script components.

    Let's say I have an enemy AI character, that is represented in the world by a simple cube, and has its own script (Enemy.cs). What if, for example, I have a finite state machine system (a whole set of related classes), written in plain ol' C#, that I want my Enemy class to use. I'm having difficulty figuring out how I would model the "has-a" relationship here. The FSM classes have nothing to do with GameObjects and they needn't derive from MonoBehaviour either. How do I integrate this code into the project?

    Many thanks,

    Will
     
    Last edited: Nov 5, 2011
  2. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    It depends on whether you want to expose your class to the Unity framework on not. If you have a state machine, is it standalone or attachable? If you state machine is standalone a MonoBehaviour will "have-a" state machine as part of the MonoBehaviour class definition. Or if your state machine will be attached directly to a GameObject, and therefore, your MonoBehaviour "is-a" state machine.

    I often make my state machines, using the Open Source Stateless project, a "has-a" of a MonoBehaviour and expose some member variables in the MonoBehaviour that feed in to the state machine.

    It comes down to knowing when to make a class derived from MonoBehaviour and when to make a regular old class derived from UnityEngine.Object which is the equivalent of System.Object in regular .NET.

    The preference of is-a and has-a comes down to how you are structuring your architecture and how you want to expose your particular underlying class implementation.
     
  3. willyfreddy

    willyfreddy

    Joined:
    Mar 28, 2011
    Posts:
    50
    Hi Justin,

    Thanks for the response, but I'll admit that I only followed about 73% of it. If you would be willing to spend the time, it would be great if you cache out exactly how both the "have-a" and "is-a" relationships would work, within Unity, with a class (structure) that had originally been written in plain ol' C# for .NET.

    For example, I'll highlight some bits that are iffy to me:

    - "if your state machine will be attached directly to a GameObject, and therefore, your MonoBehaviour "is-a" state machine"
    - "I often make my state machines, [...], a "has-a" of a MonoBehaviour and expose some member variables in the MonoBehaviour that feed in to the state machine."

    Still, speaking more generally, it would be great if you cache out exactly how both the "have-a" and "is-a" relationships would work, within Unity, with a class (structure) that had originally been written in plain ol' C# for .NET.

    Many thanks,

    Will

    p.s. Does every class within a project have to at least derive from UnityEngine.Object? What happens if I utilize a class that doesn't derive from it? (I've included one during testing and it compiled alright).
     
    Last edited: Nov 5, 2011
  4. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Nope.

    Unity3d uses 'plain old C#' just fine.

    Have you written many Monobehaviours?
     
  5. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    No, sorry. That's an awful lot of work to be asking of someone. I am just too busy right now to do a proper write up.

    Sound like you have some more learning to do with the basic C# and the .NET framework based on these last couple of questions. :) All objects in Unity, even those not explicitly stated, are derived from UnityEngine.Object.
     
    Last edited: Nov 5, 2011
  6. willyfreddy

    willyfreddy

    Joined:
    Mar 28, 2011
    Posts:
    50
    No, I am brand new to Unity and have only been fiddling with coding for it for a week or so now.
     
  7. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    Best bet, and I know it may sound flippant, but I suggest one of the many reasonably good introduction to Unity books available.** Any Unity book will get you up to speed very quickly. There are a bunch of very good free tutorials around too, but the quality can be rather hit and miss. A good book with teach you all you need to know about the basics. Obviously you are a programmer, so go for one that is more programming centric with lots of good code in it, rather than the more editing environment/design focused books. Even if the book presents all of its code in UnityScript (a dialect of Javascript/ECMAScript) it doesn't matter as converting between Unityscript and C# is a trivial skill teachable to anyone who can read the other dialect in about 30 minutes.

    **Try and avoid Packt publishing.
     
  8. willyfreddy

    willyfreddy

    Joined:
    Mar 28, 2011
    Posts:
    50
    No problem Justin, I understand. I Personally, I think I would understand what you're trying to say quite easily (i.e. the Unity techniques themselves), I'm just having trouble creating contextual examples from your generalized explanations.

    Let me try this alone and hopefully someone can point out the mistakes.

    1) "if your state machine will be attached directly to a GameObject, and therefore, your MonoBehaviour "is-a" state machine"

    I'm guessing an example of this would be something like the following:

    - I have a cube GameObject
    - That GameObject has a script component (i.e. deriving from MonoBehaviour)
    - That script component's class is the state machine

    2) "I often make my state machines, [...], a "has-a" of a MonoBehaviour and expose some member variables in the MonoBehaviour that feed in to the state machine."

    I'm guessing an example of this would be something like the following:

    - I have a cube GameObject
    - That GameObject has a script component (i.e. deriving from MonoBehaviour)
    - That script component's class contains the state machine as a member variable
    - That script component's class contains some other member variables that are made public so the state machine can act on them directly

    What do you guys think? Is that what he meant?

    Cheers,

    Will
     
    Last edited: Nov 5, 2011
  9. willyfreddy

    willyfreddy

    Joined:
    Mar 28, 2011
    Posts:
    50
    Apologies for the bump. I just want to make sure that I'm not way off in my thinking here.

    Cheers,

    Will
     
  10. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    Sorry, I only do useful posts during the week. Weekends I take off and spend my time trolling the forum posters who are working on an MMO or posting their new MMO a day or two early -- everyone knows that new MMO posts and forming new MMO teams should be posted on Monday so that we have something to laugh at on a Monday morning at the office.

    If you want to make a new MMO thread, or mention that you are making a state machine for an MMO I would be happy to take a few minutes and troll you.
     
  11. willyfreddy

    willyfreddy

    Joined:
    Mar 28, 2011
    Posts:
    50
    Fair enough. Now that it's Monday, perhaps you could take a look? :)

    Cheers,

    Will
     
  12. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    $kidding_me.jpg
    You have called my bluff. Well played Sir, well played.


    $challenge_accepted.jpg



    Because of the simplicity of the examples, please do not fall in to the trap of thinking "yeah, but you could make that example be the other type because of reason X." The examples are simple, but if you scale up the complexity, you can easily choose the wrong solution. What got you to here, won't get you to there.


    If you are making a simple state machine, you may desire to derive your state machine from MonoBehaviour itself, e.g.:
    [FONT="]
    [/FONT]
    [code]
    [FONT="]public class StateMachineBehaviour : MonoBehaviour
    {
    IDictionary<TState, StateRepresentation> _stateConfiguration = new Dictionary<TState, StateRepresentation>();[/FONT]
    [FONT="] IDictionary[/FONT][/COLOR][FONT="]<TTrigger, TriggerWithParameters> _triggerConfiguration = new Dictionary<TTrigger, TriggerWithParameters>();[/FONT]

    [FONT="] public[/FONT][/COLOR][FONT="] StateConfiguration Configure(TState state)
    {[/FONT]
    [FONT="]// blah blah
    }[/FONT]

    [COLOR=blue][FONT="] public[/FONT]
    [FONT="] [COLOR=#2B91AF]StateConfiguration[/COLOR] Permit(TTrigger trigger, TState destinationState)
    {
    // blah blah
    }[/FONT]

    [COLOR=blue][FONT=Consolas]public[/FONT][/COLOR][FONT=Consolas] [COLOR=blue]void[/COLOR] Fire(TTrigger trigger)
    {[/FONT]
    [FONT=Consolas] // fire a state transition here based on the permissions ruleset[/FONT]
    [FONT=Consolas] }[/FONT]

    [FONT="]}[/FONT]
    [/code] And then implement a state machine that will do what you need based off of that:
    Code (csharp):
    1.  
    2.   [FONT="]public class [/FONT][COLOR=#2B91AF][FONT=Consolas]SensorDevice [/FONT][/COLOR][FONT="]: StateMachineBehaviour
    3. {
    4. [/FONT][FONT=Consolas]  [COLOR=blue]protected[/COLOR] [COLOR=blue]enum[/COLOR] [COLOR=#2B91AF]States[/COLOR][/FONT]
    5.   [FONT=Consolas]  {[/FONT]
    6.   [FONT=Consolas]    Uninitialised,[/FONT]
    7.   [FONT=Consolas]    Initialising,[/FONT]
    8.   [FONT=Consolas]    Reinitialise,[/FONT]
    9.   [FONT=Consolas]    Reading,[/FONT]
    10.   [FONT=Consolas]    ShuttingDown,[/FONT]
    11.   [FONT=Consolas]  }[/FONT]
    12.  
    13.   [FONT=Consolas]  [COLOR=blue]protected[/COLOR] [COLOR=blue]enum[/COLOR] [COLOR=#2B91AF]Triggers[/COLOR][/FONT]
    14.   [FONT=Consolas]  {[/FONT]
    15.   [FONT=Consolas]    Initialise,[/FONT]
    16.   [FONT=Consolas]    ReadData,[/FONT]
    17.   [FONT=Consolas]    Shutdown,[/FONT]
    18.   [FONT=Consolas]    Unplugged,[/FONT]
    19.   [FONT=Consolas]    StartOver,[/FONT]
    20.   [FONT=Consolas]    FailInitialisation[/FONT]
    21.   [FONT=Consolas]  }[/FONT]
    22.  
    23.  
    24.   [FONT=Consolas]  // set up our state machine[/FONT]
    25.   [FONT="]void Awake()
    26. [/FONT][FONT="]{[/FONT]
    27.   [FONT="]    // because of Unity's parameterless constructors we have to do some behind the scenes magic to get a generic type that can handle our states and triggers
    28. [/FONT][FONT=Consolas]    InitialiseMachine<[COLOR=#2B91AF]States[/COLOR], [COLOR=#2B91AF]Triggers[/COLOR]>([COLOR=#2B91AF]States[/COLOR].Uninitialised);[/FONT]
    29.  
    30.  [FONT=Consolas]    Configure([COLOR=#2B91AF]States[/COLOR].Uninitialised)[/FONT]
    31.  [FONT=Consolas]      .PermitReentry([COLOR=#2B91AF]Triggers[/COLOR].Shutdown)[/FONT]
    32.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Initialise, [COLOR=#2B91AF]States[/COLOR].Initialising);[/FONT]
    33.  
    34.  [FONT=Consolas]    Configure([COLOR=#2B91AF]States[/COLOR].Initialising)[/FONT]
    35.  [FONT=Consolas]      .OnEntry(() => StateEntry_Initialising())[/FONT]
    36.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].FailInitialisation, [COLOR=#2B91AF]States[/COLOR].Reinitialise)[/FONT]
    37.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Shutdown, [COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    38.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].ReadData, [COLOR=#2B91AF]States[/COLOR].Reading);[/FONT]
    39.  
    40.  [FONT=Consolas]    Configure([COLOR=#2B91AF]States[/COLOR].Reinitialise)[/FONT]
    41.  [FONT=Consolas]      .OnEntry(() => StateEntry_Reinitialise())[/FONT]
    42.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Shutdown, [COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    43.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Initialise, [COLOR=#2B91AF]States[/COLOR].Initialising);[/FONT]
    44.  
    45.  [FONT=Consolas]    Configure([COLOR=#2B91AF]States[/COLOR].Reading)[/FONT]
    46.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Shutdown, [COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    47.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Unplugged, [COLOR=#2B91AF]States[/COLOR].Reinitialise);[/FONT]
    48.  
    49.  [FONT=Consolas]    Configure([COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    50.  [FONT=Consolas]      .OnEntry(() => StateEntry_ShuttingDown())[/FONT]
    51.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].StartOver, [COLOR=#2B91AF]States[/COLOR].Uninitialised);[/FONT]
    52.  [FONT="]}[/FONT]
    53.  
    54.   [FONT="]  protected void StateEntry_Initialising()[/FONT]
    55.  [FONT="]  {[/FONT]
    56.   [FONT="]    // initialiise the sensor device upon state entry[/FONT]
    57.  [FONT="]  }[/FONT]
    58.  
    59.   [FONT="]  protected void StateEntry_ReInitialise()[/FONT]
    60.  [FONT="]  {[/FONT]
    61.   [FONT="]    // reinitialise the sensor device upon state entry[/FONT]
    62.  [FONT="]  }[/FONT]
    63.  
    64.   [FONT="]  protected void StateEntry_ShuttingDown()[/FONT]
    65.  [FONT="]  {[/FONT]
    66.   [FONT="]    // shut down the sensor device[/FONT]
    67.  [FONT="]  }[/FONT]
    68.  
    69.   [FONT="]}[/FONT]
    70.  
    71.  [FONT="]// and some basic code to interact with the state machine[/FONT]
    72.   [FONT="]public class UserInterface : MonoBehaviour[/FONT]
    73.  [FONT="]{[/FONT]
    74.   [FONT="]  void OnGUI()[/FONT]
    75.  [FONT="]  {[/FONT]
    76.   [FONT="]    if (GUI.Button(new Rect(25, 25, 200, 50), "Initialise") == true)[/FONT]
    77.  [FONT="]    {[/FONT]
    78.   [FONT="]      SensorDevice device = GetComponent<SensorDevice>();[/FONT]
    79.  [FONT="]      device.Fire(Triggers.Initialise);[/FONT]
    80.   [FONT="]    }[/FONT]
    81.  
    82.  [FONT="]    if (GUI.Button(new Rect(25, 100, 200, 50), "Shutdown") == true)[/FONT]
    83.   [FONT="]    {[/FONT]
    84.  [FONT="]      SensorDevice device = GetComponent<SensorDevice>();[/FONT]
    85.   [FONT="]      device.Fire(Triggers.Shutdown);[/FONT]
    86.  [FONT="]    }[/FONT]
    87.   [FONT="]  }[/FONT]
    88.  [FONT="]}[/FONT]
    89.  
    90.  
    This obviously is the is-a composition.

    You could just as well implement your state machine as an interface, in which case your declaration would become:

    Code (csharp):
    1.  
    2.   [FONT="]public class [/FONT][COLOR=#2B91AF][FONT=Consolas]SensorDevice [/FONT][/COLOR][FONT="]: MonoBehaviour, IStateMachine
    3. {
    4. }
    5. [/FONT]
    But whilst this is-a is correct, it would lead to a lot of duplication for anything but the most simplest of state machines. Also, because of how Unity handles construction of new objects, and hence scripts, you have to do some fancy footwork to make a class that can be generic.

    By now, all the programmers on the forum already like:
    $now_what.jpg
    "S.O.B. made a mistake but I can't find!"


    You are also exposing a lot of the internal workings of your state machine to scripts that may not need to know about it, and if you are in a team of many people, or you take a break from your code if you are solo, you or they may end up, one day, on pulling strings in your state machine that your state machine doesn't want to be pulled on. You can obviously wrap your triggering firing mechanisms in yet more isolating functions and abstract it away, but again, more duplicated code for each state machine you make.

    Let's try the same idea with a has-a composition, e.g.:
    Code (csharp):
    1.  
    2.   [FONT="]public class [/FONT][COLOR=#2B91AF][FONT=Consolas]SensorDevice [/FONT][/COLOR][FONT="]: MonoBehaviour
    3. {
    4. [/FONT][FONT=Consolas]  [COLOR=blue]protected[/COLOR] [COLOR=blue]enum[/COLOR] [COLOR=#2B91AF]States[/COLOR][/FONT]
    5.   [FONT=Consolas]  {[/FONT]
    6.   [FONT=Consolas]    Uninitialised,[/FONT]
    7.   [FONT=Consolas]    Initialising,[/FONT]
    8.   [FONT=Consolas]    Reinitialise,[/FONT]
    9.   [FONT=Consolas]    Reading,[/FONT]
    10.   [FONT=Consolas]    ShuttingDown,[/FONT]
    11.   [FONT=Consolas]  }[/FONT]
    12.  
    13.   [FONT=Consolas]  [COLOR=blue]protected[/COLOR] [COLOR=blue]enum[/COLOR] [COLOR=#2B91AF]Triggers[/COLOR][/FONT]
    14.   [FONT=Consolas]  {[/FONT]
    15.   [FONT=Consolas]    Initialise,[/FONT]
    16.   [FONT=Consolas]    ReadData,[/FONT]
    17.   [FONT=Consolas]    Shutdown,[/FONT]
    18.   [FONT=Consolas]    Unplugged,[/FONT]
    19.   [FONT=Consolas]    StartOver,[/FONT]
    20.   [FONT=Consolas]    FailInitialisation[/FONT]
    21.   [FONT=Consolas]  }[/FONT]
    22.  
    23.   [FONT=Consolas]  [COLOR=blue]protected[/COLOR] [COLOR=#2B91AF]StateMachine[/COLOR]<[COLOR=#2B91AF]States[/COLOR], [COLOR=#2B91AF]Triggers[/COLOR]> m_stateMachine;[/FONT]
    24.  
    25.  
    26.   [FONT=Consolas]  // set up our state machine[/FONT]
    27.   [FONT="]void Awake()
    28. [/FONT][FONT="]{[/FONT]
    29.   [FONT="]    // no such problem with making a generic class now[/FONT]
    30.  [FONT=Consolas]    m_stateMachine = [COLOR=blue]new[/COLOR] [COLOR=#2B91AF]StateMachine[/COLOR]<[COLOR=#2B91AF]States[/COLOR], [COLOR=#2B91AF]Triggers[/COLOR]>([COLOR=#2B91AF]States[/COLOR].Uninitialised);[/FONT]
    31.  
    32.  [FONT=Consolas]    m_stateMachine.Configure([COLOR=#2B91AF]States[/COLOR].Uninitialised)[/FONT]
    33.  [FONT=Consolas]      .PermitReentry([COLOR=#2B91AF]Triggers[/COLOR].Shutdown)[/FONT]
    34.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Initialise, [COLOR=#2B91AF]States[/COLOR].Initialising);[/FONT]
    35.  
    36.  [FONT=Consolas]    m_stateMachine.Configure([COLOR=#2B91AF]States[/COLOR].Initialising)[/FONT]
    37.  [FONT=Consolas]      .OnEntry(() => StateEntry_Initialising())[/FONT]
    38.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].FailInitialisation, [COLOR=#2B91AF]States[/COLOR].Reinitialise)[/FONT]
    39.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Shutdown, [COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    40.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].ReadData, [COLOR=#2B91AF]States[/COLOR].Reading);[/FONT]
    41.  
    42.  [FONT=Consolas]    m_stateMachine.Configure([COLOR=#2B91AF]States[/COLOR].Reinitialise)[/FONT]
    43.  [FONT=Consolas]      .OnEntry(() => StateEntry_Reinitialise())[/FONT]
    44.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Shutdown, [COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    45.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Initialise, [COLOR=#2B91AF]States[/COLOR].Initialising);[/FONT]
    46.  
    47.  [FONT=Consolas]    m_stateMachine.Configure([COLOR=#2B91AF]States[/COLOR].Reading)[/FONT]
    48.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Shutdown, [COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    49.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].Unplugged, [COLOR=#2B91AF]States[/COLOR].Reinitialise);[/FONT]
    50.  
    51.  [FONT=Consolas]    m_stateMachine.Configure([COLOR=#2B91AF]States[/COLOR].ShuttingDown)[/FONT]
    52.  [FONT=Consolas]      .OnEntry(() => StateEntry_ShuttingDown())[/FONT]
    53.  [FONT=Consolas]      .Permit([COLOR=#2B91AF]Triggers[/COLOR].StartOver, [COLOR=#2B91AF]States[/COLOR].Uninitialised);[/FONT]
    54.  
    55.  [FONT="]}[/FONT]
    56.  
    57.   [FONT="]  protected void StateEntry_Initialising()[/FONT]
    58.  [FONT="]  {[/FONT]
    59.   [FONT="]    // initialiise the sensor device upon state entry[/FONT]
    60.  [FONT="]  }[/FONT]
    61.  
    62.   [FONT="]  void StateEntry_ReInitialise()[/FONT]
    63.  [FONT="]  {[/FONT]
    64.   [FONT="]    // reinitialise the sensor device upon state entry[/FONT]
    65.  [FONT="]  }[/FONT]
    66.  
    67.   [FONT="]  void StateEntry_ShuttingDown()[/FONT]
    68.  [FONT="]  {[/FONT]
    69.   [FONT="]    // shut down the sensor device[/FONT]
    70.  [FONT="]  }[/FONT]
    71.  
    72.   [FONT="]  // let's make some functions to expose just those bits of our state machine we want to show to the rest of the world[/FONT]
    73.  [FONT="]  public void InitialiseSensor()[/FONT]
    74.   [FONT="]  {[/FONT]
    75.  [FONT=Consolas]    m_stateMachine.Fire([COLOR=#2B91AF]Triggers[/COLOR].Initialise);[/FONT]
    76.  [FONT=Consolas]  }[/FONT]
    77.  
    78.  [FONT="]  public void ShutdownSensor()[/FONT]
    79.   [FONT="]  {[/FONT]
    80.  [FONT=Consolas]    m_stateMachine.Fire([COLOR=#2B91AF]Triggers[/COLOR].Shutdown);[/FONT]
    81.  [FONT=Consolas]  }[/FONT]
    82.  
    83.  [FONT="]// and some basic code to interact with the state machine[/FONT]
    84.   [FONT="]public class UserInterface : MonoBehaviour[/FONT]
    85.  [FONT="]{[/FONT]
    86.   [FONT="]  void OnGUI()[/FONT]
    87.  [FONT="]  {[/FONT]
    88.   [FONT="]    if (GUI.Button(new Rect(25, 25, 200, 50), "Initialise") == true)[/FONT]
    89.  [FONT="]    {[/FONT]
    90.   [FONT="]      SensorDevice device = GetComponent<SensorDevice>();[/FONT]
    91.  [FONT="]      device.InitialiseSensor();[/FONT]
    92.   [FONT="]    }[/FONT]
    93.  
    94.  [FONT="]    if (GUI.Button(new Rect(25, 100, 200, 50), "Shutdown") == true)[/FONT]
    95.   [FONT="]    {[/FONT]
    96.  [FONT="]      SensorDevice device = GetComponent<SensorDevice>();[/FONT]
    97.   [FONT="]      device.ShutdownSensor();[/FONT]
    98.  [FONT="]    }[/FONT]
    99.   [FONT="]  }[/FONT]
    100.  [FONT="]}[/FONT]
    101.    
    The empirical difference between the two architectural approaches is one of encapsulation. Is-a is the subsumption of the inherited class, it is a specialization of a more generic abstraction, e.g. Granny Smith (very specific) is-a Apple (specific) is-a Fruit (generic) is-a Food (very generic) is-a Object (very, very generic). In .NET but not in C++, you can only usually be, i.e. is-a, one particular type of object at any particular level of inheritance, though of course, interfaces to a degree can further generalize or make a narrower specificity through their own definitions, but interfaces do not provide concrete behaviour, only a behavioural contract, vis C++ and multiple inheritance.

    By which point in time, most non-programmers' eyes have glazed over and they are thinking:
    $mother_of_god.jpg

    On the opposite, has-a is a composition, where one object belongs to another object, Orchard has-a Apple Tree (many trees probably) has-a Apple (many apples on the tree), but the Orchard can also has-a Orange Citrus Tree has-a Orange (many oranges).

    The architectural choices between is-a and has-a can often be subtle and the art is knowing in which case you should use each technique and there is actually a lot of overlap between choosing either option which then becomes a series of trade-offs. General rule of thumb is "expose as little as possible, minimise your code."

    Unity, as stated, throws in its own little wrinkle because of the way object construction is done, making it difficult to make generic MonoBehaviours.

    I think I did a pretty good job of explaining all that!
    $fuck_yeah.jpg
     
    Last edited: Nov 8, 2011
  13. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    Has-a and is-a: making programmers speak like a LOLCAT

    $fhc6wm6l.jpg
    But I have many flavours!


    $c9ave6jo.jpg
    But I can only ever be ten ninjas, but sometimes I can have an interface or two that changes me in to a special kind of killer ninja with a contract.

    Let me know if anything needs clarification


    $y_u_no_let.jpg
     
    Last edited: Nov 8, 2011
  14. justinlloyd

    justinlloyd

    Joined:
    Aug 5, 2010
    Posts:
    1,680
    Of course, C++ makes the whole is-a and has-a choice a little fuzzier because of the multiple inheritance ability.

    $u_jelly.jpg
     
  15. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    I can't believe I've had to do this twice in one week...

     
  16. willyfreddy

    willyfreddy

    Joined:
    Mar 28, 2011
    Posts:
    50
    Wow, Justin, I certainly never expected such a detailed response. Regardless, I think you did a great job of explaining it and, yes, I followed it completely.



    Thanks again.

    Will
     
  17. Fishypants

    Fishypants

    Joined:
    Jan 25, 2009
    Posts:
    444
    This is probably one of the greatest posts I have read on these forums.

    Unity Forums:
    Y U NO LET ME +1 JUSTIN!?!?!1!