Search Unity

GameObject.Find(...)

Discussion in 'Scripting' started by boylesg, May 25, 2016.

  1. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    It has nothing specifically to do with OO programming - I just can't see why people are suggesting that dealing with strings in Unity Scripts (rather than having hard links in the editor) is bad.
     
  2. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,205
    You can't see why because you're simply writing off the advice of everyone here because your opinion is different.

    I doubt many people have bothered to write a replacement for GameObject.Find() as the Unity way is the ideal way of handling this matter. After all I imagine hooking up 75 objects is significantly less effort than designing, building, and debugging an alternative solution.

    Regardless of the side discussion though profiling is the way to go.
     
    Mycroft likes this.
  3. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Essentially this.

    Unity supports all your regular polymorphic OOP stuff. And this is totally unrelated to string programming.

    This is just straight out wrong. You can call GetComponent on a base class or an interface. This is the 'proper' OOP way to work. Call common methods by using the polymorphism. Not using strings.

    The community is split, about half have formal training, about half are self taught. The community is also fairly split on how valuable a formal education actually is. The 'educated idiot' who knows everything and has no practical skills to apply that knowledge is very much a thing.

    For an interesting contrast to your uni education, check out Jonothan Blow's video.

     
    JasonBricco, passerbycmc and Ryiah like this.
  4. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    It doesn't - I just don't get why you think that doing so is worst than basing a unity project around drag and drop links in the editor.
     
  5. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
     
  6. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Strings are only relevant to the OO discussion as they apply to their use in Unity's SendMessage("FuncName", object)

    The discussion about strings versus drag and drop links was a bit of a tangent from the core discussion about OO and inheritance, polymorphism and re-usable code.

    If I am dead wrong then I am happy to admit it, but I fail to see how I can use MyScript GetComponent<MyScript>() in the following sort of situation...

    The code below represents a windows radio button group:


    If I don't use generic SendMessage(...), plus a perfectly reasonable amount of supporting code infrastructure including the binary tree thing to avoid using Unity's Find(...) function , I don't see how I can avoid doing away with the base class hierarchy, have all the code in a single script and class and painfully make specific changes to copies of the same script for every game object button I attach it to.

    It is necessary to send message back and forth between the individual radio buttons, some where in the game object hierarchy below the group manager game object, in order:
    1) For the group manager to be aware of all the toggle buttons in the group.
    2) For the group manager to ensure that only one of the toggle buttons appears pressed at any given time.

    And I don't want it to have to create a situation specific group manager, with drag and drop links to specific toggle buttons, each time over the next 10 years or what ever. As far as I am concerned, that is just a pain in the ar$e and totally avoidable even if it means a Unity 'work around'.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BaseButtonManager : McCormickMonoBehaviourBase
    5. {
    6.     protected Sprite m_spriteButtonPressed, m_spriteButtonUnpressed, m_spriteButtonDisabled;
    7.     protected SpriteRenderer m_spriteButtonrender = null;
    8.     protected bool m_bEnabled = true;
    9.  
    10.     public virtual void DoPress()
    11.     {
    12.     }
    13.  
    14.     public virtual void Enable(bool bEnable)
    15.     {
    16.         m_bEnabled = bEnable;
    17.         if (m_bEnabled)
    18.             m_spriteButtonrender.sprite = m_spriteButtonUnpressed;
    19.         else
    20.             m_spriteButtonrender.sprite = m_spriteButtonDisabled;
    21.     }
    22.  
    23.     protected bool IsPressed()
    24.     {
    25.         return m_spriteButtonrender.sprite == m_spriteButtonPressed;
    26.     }
    27.  
    28.     public bool IsEnabled()
    29.     {
    30.         return m_bEnabled;
    31.     }
    32.  
    33.     private void RestoreButton()
    34.     {
    35.         m_spriteButtonrender.sprite = m_spriteButtonUnpressed;
    36.     }
    37.  
    38.     // Use this for initialization
    39.     protected override void Awake()
    40.     {
    41.         m_spriteButtonrender = (SpriteRenderer)GetComponent("SpriteRenderer");
    42.         base.Awake();
    43.     }
    44.  
    45.     // Update is called once per frame
    46.     protected override void Update()
    47.     {
    48.         CheckForMouseClickOrTouch();
    49.  
    50.         if (WasClicked())
    51.         {
    52.             DoPress();
    53.         }
    54.     }
    55.  
    56. /*
    57.     protected void OnMouseDown()
    58.     {
    59.         DoPress();
    60.     }
    61. */
    62.  
    63.     protected virtual void SetPressed(bool bPressed)
    64.     {
    65.         if (m_spriteButtonrender != null)
    66.         {
    67.             if (bPressed)
    68.             {
    69.                 m_spriteButtonrender.sprite = m_spriteButtonPressed;
    70.             }
    71.             else
    72.             {
    73.                 m_spriteButtonrender.sprite = m_spriteButtonUnpressed;
    74.             }
    75.         }
    76.         else
    77.         {
    78.             Debug.Log("BaseButtonManager.SetPressed(bool bPressed) - m_spriteButtonrender not set...");
    79.         }
    80.     }
    81.  
    82.     protected override void DoReceiveMessage(Parameters p)
    83.     {
    84.         if (p.m_nMsgID == BingoMessageIDEnum.MSGID_ENABLE)
    85.         {
    86.             m_bEnabled = (bool)p.m_objParam1;
    87.         }
    88.         base.DoReceiveMessage(p);
    89.     }
    90. }
    91.  
    92.  
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class BaseToggleButtonManager : BaseButtonManager
    5. {
    6.     protected bool m_bChecked = false;
    7.     protected GameObject m_gameobjToggleButtonManager = null;
    8.     protected object m_objValue = null;
    9.     protected int m_nMsgID = MessageIDEnum.MSGID_NULL;
    10.  
    11.     public BaseToggleButtonManager()
    12.     {
    13.     }
    14.  
    15.     protected virtual Parameters GetParameters(int nMessageID)
    16.     {
    17.         m_parameters1.New(nMessageID, m_bChecked, m_objValue);
    18.  
    19.         return m_parameters1;
    20.     }
    21.  
    22.     public override void DoPress()
    23.     {
    24.         if (IsEnabled())
    25.         {
    26.             m_bChecked = !m_bChecked;
    27.             SetPressed(m_bChecked);
    28.             if (m_gameobjToggleButtonManager)
    29.             {
    30.                 SendMessageDirect(m_gameobjToggleButtonManager, MessageIDEnum.MSGID_NOTIFY_TOGGLE_BUTTON_CHECKED);
    31.             }
    32.         }
    33.     }
    34.  
    35.     public void SetCheckState(bool bChecked)
    36.     {
    37.         m_bChecked = bChecked;
    38.         SetPressed(m_bChecked);
    39.     }
    40.  
    41.     public bool IsChecked()
    42.     {
    43.         return m_bChecked;
    44.     }
    45.  
    46.     protected override void DoReceiveMessage(Parameters p)
    47.     {
    48.         base.DoReceiveMessage(p);
    49.         if (p.m_nMsgID == MessageIDEnum.MSGID_NOTIFY_TOGGLE_GROUP_MANAGER)
    50.         {
    51.             m_gameobjToggleButtonManager = (GameObject)p.m_objParam1;
    52.         }
    53.         else if (p.m_nMsgID == MessageIDEnum.MSGID_SET_BUTTON_STATE)
    54.         {
    55.             string strName = (string)p.m_objParam1;
    56.             if (strName == gameObject.name)
    57.             {
    58.                 m_bChecked = (bool)p.m_objParam2;
    59.                 SetPressed(m_bChecked);
    60.             }
    61.         }
    62.         else if (p.m_nMsgID == MessageIDEnum.MSGID_GET_BUTTON_STATE)
    63.         {
    64.             string strName = (string)p.m_objParam1;
    65.             if (strName == gameObject.name)
    66.             {
    67.                 GameObject gameobj = (GameObject)p.m_gameobjSender;
    68.                 m_parameters1.New(MessageIDEnum.MSGID_GET_BUTTON_STATE, gameObject.name, m_bChecked);
    69.                 SendMessageDirect(gameobj, MessageIDEnum.MSGID_GET_BUTTON_STATE, m_parameters1);
    70.  
    71.             }
    72.         }
    73.         else if (p.m_nMsgID == MessageIDEnum.MSGID_GET_BUTTON_VALUE)
    74.         {
    75.             GameObject gameobjRequester = (GameObject)p.m_gameobjSender;
    76.             m_parameters1.New(MessageIDEnum.MSGID_SET_BUTTON_VALUE, m_objValue);
    77.             SendMessageDirect(gameobjRequester, MessageIDEnum.MSGID_SET_BUTTON_VALUE, m_parameters1);
    78.         }
    79.     }
    80.  
    81. }
    82.  
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class ToggleButtonGroupManager : McCormickMonoBehaviourBase
    6. {
    7.     private List<GameObject> m_arrayButtons = new List<GameObject>();
    8.     private object m_objCheckedToggleButtonVal = -1;
    9.  
    10.     public ToggleButtonGroupManager()
    11.     {
    12.         RegisterMessageReceiver("ToggleButtonGroupManager", MessageIDEnum.MSGID_NOTIFY_TOGGLE_BUTTON_CHECKED);
    13.     }
    14.  
    15.     // Use this for initialization
    16.     protected override void Awake()
    17.     {
    18.         FindAllToggleButtons(gameObject);
    19.         base.Awake();
    20.     }
    21.  
    22.     protected override void Start()
    23.     {
    24.         base.Start();
    25.         m_parameters1.New(MessageIDEnum.MSGID_SET_BUTTON_STATE, m_arrayButtons[0].name, true);
    26.     }
    27.  
    28.     private void FindAllToggleButtons(GameObject gameobj)
    29.     {
    30.         foreach (Transform transformChild in gameobj.transform)
    31.         {
    32.             string strName = transformChild.gameObject.name.ToLower();
    33.             if (strName.Contains("togglebutton"))
    34.             {
    35.                 m_parameters1.New(MessageIDEnum.MSGID_NOTIFY_TOGGLE_GROUP_MANAGER, gameObject);
    36.                 PostMessage(MessageIDEnum.MSGID_NOTIFY_TOGGLE_GROUP_MANAGER, gameObject, m_parameters1);
    37.                 m_arrayButtons.Add(transformChild.gameObject);
    38.             }
    39.             else
    40.                 FindAllToggleButtons(transformChild.gameObject);
    41.         }
    42.     }
    43.  
    44.     protected override void DoReceiveMessage(Parameters p)
    45.     {
    46.         base.DoReceiveMessage (p);
    47.  
    48.         if (p.m_nMsgID == MessageIDEnum.MSGID_NOTIFY_TOGGLE_BUTTON_CHECKED)
    49.         {
    50.             GameObject gameobj = (GameObject)p.m_objParam1;
    51.  
    52.             for (int nI = 0; nI < m_arrayButtons.Count; nI++)
    53.             {
    54.                 if (m_arrayButtons[nI] != gameobj)
    55.                 {
    56.                     GameObject gameobjTemp = m_arrayButtons[nI];
    57.  
    58.                     m_parameters1.New(MessageIDEnum.MSGID_SET_BUTTON_STATE, gameobjTemp.name, false);
    59.                     SendMessageDirect(gameobjTemp,  MessageIDEnum.MSGID_SET_BUTTON_STATE, m_parameters1);
    60.                 }
    61.             }
    62.             m_parameters1.New(MessageIDEnum.MSGID_SET_BUTTON_STATE, gameobj.name, true);
    63.             SendMessageDirect(gameobj,  MessageIDEnum.MSGID_SET_BUTTON_STATE, m_parameters1);
    64.  
    65.             m_parameters1.New(MessageIDEnum.MSGID_GET_BUTTON_VALUE, gameObject);
    66.             SendMessageDirect(gameobj,  MessageIDEnum.MSGID_GET_BUTTON_VALUE, m_parameters1);
    67.         }
    68.         else if (p.m_nMsgID == MessageIDEnum.MSGID_SET_BUTTON_VALUE)
    69.         {
    70.             m_objCheckedToggleButtonVal = p.m_objParam1;
    71.         }
    72.         else if (p.m_nMsgID == MessageIDEnum.MSGID_GET_TOGGLE_GROUP_VALUE)
    73.         {
    74.             GameObject gameobjRequester = (GameObject)p.m_objParam1;
    75.             m_parameters1.New(MessageIDEnum.MSGID_SET_TOGGLE_GROUP_VALUE, m_objCheckedToggleButtonVal);
    76.             SendMessageDirect(gameobjRequester, MessageIDEnum.MSGID_SET_TOGGLE_GROUP_VALUE, m_parameters1);
    77.         }
    78.     }
    79. }
    80.  
     
    Last edited: May 27, 2016
  7. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,205
    That would require you to build the solution and profile it though! :eek:
     
    Mycroft likes this.
  8. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    As far as I can see BoredMormon, no, this is not wrong!. Because the bit in <> has to be the name of the script you have attached to the game object. The script that the base class is defined in is not attached to anything......directly at least.

    I wouldn't mind betting that I would get null if I tried it!
     
  9. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    My Bingo3D app is working beautifully with base class hierarchy and required Unity work arounds - no bugs.

    And my central 'post office' virtual/override function 'DoReceiveMessage(...)' has been making debugging and setting break points in the right place quite easy indeed. The only issue I have had is matching up what the message receiver expects to find in the message parameters to what the message sender is giving it. On the whole they have been easy fixes, with a couple of curly ones on account of my message ID values not being unique.

    The only issue for me is if my use of Find(...) is going to see me run into problems down the track with bigger more complex apps. But nothing that a programming 101 binary tree (or some other even more efficient sorting system) can't fix.
     
    Last edited: May 27, 2016
  10. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    No you can supply the base class or a interface in GetComponent and it will work and return you the component on that object with that base class
     
    Ryiah and Kiwasi like this.
  11. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Perhaps I should have pointed it out earlier. SendMessage is pretty much obsolete in favour of ExecuteEvents and the event system. SendMessage is also notorious for being slow. Trading Find for SendMessage is probably not going to net you any performance.

    That's a heck of a lot of code to reproduce some built in functionality. See my earlier video about getting code done as quickly as possible. But that aside, here is how I would duplicate the same functionality:

    Instead of trying to identify a toggle by its name, I would search for a Toggle component or interface. Its a much more robust system. You have the potential to break if someone renames a GameObject. Or if the GameObject doesn't have the appropriate receivers in place. Or if there is a GameObject named 'togglebutton Parent Object'

    And remember how this thread started being about performance? Calling methods directly is going to be much faster then your messaging system. Type comparisons are (probably) quicker then string comparisons. Anyway, none of this is hot code, so optimising it is probably a waste of time.

    Trust me. I've been coding in the Unity environment for a while. GetComponent<ISomeInterface> is perfectly valid. It has been for a while now. You seem to be lacking a basic understanding of polymorphism. An instance of a derived class is an instance of the base class. A instance implementing and interface is an instance of the interface.
     
  12. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Programming 101 seems to be getting in your way more then helping...
     
    Mycroft and Baste like this.
  13. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Well I am going to try it out then...

    And I will check this EventSystem thing out that some one else mentioned.
     
    Last edited: May 27, 2016
  14. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    You can trust me that I have a VERY GOOD grasp of what polymorphism is all about.

    Perhaps what you are justified in saying is that I don't yet have a full grasp of how Unity handles polymorphism through its API (or what ever you call them) functions like SendMessage(...) and GetComponent<MyScript>() etc.
     
  15. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,205
    Isn't that basically what everyone has been telling you? :p
     
    Mycroft and Kiwasi like this.
  16. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Fair enough. In pseudo code GetComponent<T> basically looks like this.

    Code (CSharp):
    1. public T GetComponent <T>(){
    2.     foreach (component in allComponentsOnThisGameObject){
    3.         if (component is T) {
    4.             return component;
    5.        }
    6.     }
    7.     return null;
    8. }
    Of course some of that happens on the C++ side instead of in C#, I'm not sure exactly where the border lies on this one.

    SendMessage is a much rougher function that looks something like this.

    Code (CSharp):
    1. public void SendMessage (string method, object paramater){
    2.     foreach (component in allComponentsOnThisGameObject){
    3.         Look for an appropriate method to call via reflection
    4.         if (component has an appropriate method) {
    5.             method.Invoke(component, parameter);
    6.        }
    7.     }
    8. }
    This sort of reflection generally has a bad rep for being pretty slow. Hence my comments earlier that trading Find for SendMessage might actually reduce performance. Its worth profiling if you think you have an issue.
     
  17. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Well if this thing about BaseClass GetComponent<BaseClass>() works then problem solved - I don't need to use SendMessage(...) and it still requires minimal change to my current base architecture.

    I'll let you know how it goes.

    But another question has just come to mind....

    In C++:

    CArray<MyClass> m_array gives me an array of MyClass objects.

    If I want just references or pointers then I do CArray<MyClass&> or CArray<MyClass*>

    But given that C# seems to always pass objects as references into functions etc, what is this actually giving me?
    CArray<MyClass>

    Is it an array of MyClass references or is an array of MyClass objects?

    If I want references then how should I declare it?
    CArray<ref MyClass>?
     
  18. ezjm427

    ezjm427

    Joined:
    May 17, 2014
    Posts:
    100
    Like others have said, there are a lot of things in Unity that are just better off by using the Unity API, then trying to find a solution around it with algorithms or sorting, etc.

    Almost all of my scripts have references to some or all of their components in the base GameObject, and I load references to them in Start using GetComponent.

    I am not a fan of using the drag and drop either, except for cases such as a 'resourceManager' object/script I use that contains a lot of preset resources I can just reference the resourceManager to use them, and this is one of the only cases I use drag and drop to put in a few presets or prefabs rather than trying to load them thru code. The downside to this is they are always in memory, but you can write a more advanced resourceManager that uses a pool of objects.

    If you're using Find() repeatedly to find the same game object (such as enemies looking for the player), consider storing a static reference in the enemy class with a Player gameobject or script, load at the first start of enemy and then all enemies can access it at any time. This is only one case but I've seen people trying to use Find over and over just to find the player when a static reference works better. I've never found a good case to use Find all the time except usually only in my Start functions
     
    Kiwasi likes this.
  19. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    C# has two default behaviours. Pass by value and pass by reference.

    Pass by value means that every time you pass an object around you get a copy of the object. The object itself live on the stack. This is the default behaviour for structs. Its only recommended for small, transient objects.

    Pass by reference means that every time you pass an object around you get a copy of the reference to the object. The reference lives on the stack, but the actual object lives on the heap. This is the default behaviour for classes. And its recommended for pretty much everything.

    So an array of classes is an array of references. An array of structs actually contains the structs in the array. To further confuse the matter arrays themselves are passed by reference, meaning an array of structs will get allocated on the heap.

    You can override this behaviour when passing arguments into a function with the ref and out keywords. I won't go into them here, but let me know if you need help on how they work.

    The main gotcha for memory management in C# and Unity is simply keeping an eye on the Garbage Collector. All of those heap allocations get automatically deallocated, whenever the run time feels like it needs more memory. This can cause noticeable stutters if you are allocating a lot of garbage. This is where strategies like object pooling come in. This is a pretty common performance concern in Unity games.
     
    Mycroft and Ryiah like this.
  20. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    That sounds rather like what I am considering doing with a programming 101 binary tree in Start() or Awake() in all my derived scripts that are attached to a game object. Perhaps the precise implementation details differ but the principal is the same.
     
  21. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    This template class thing why I say giving up control over memory in C# comes with costs as well as benefits.

    In this case the cost is ambiguity when viewing the code.

    It is quite obvious in C++ what you are getting:

    CArray<MyClass>
    CArray<MyClass&>
    CArray<MyClass*>

    But at first glance, it is not obvious what you are getting in C#
    CArray<MyClass>
     
  22. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Yes and I accept the apparent balance of the varying advice that folks have given me. I am now considering the best strategy to deal with it - I am still leaning towards the binary tree thing unless some compelling reason not to comes to light.
     
  23. ezjm427

    ezjm427

    Joined:
    May 17, 2014
    Posts:
    100
    Yeah it's fine for player because it's only one, but you don't want a ton of static references going either. You can always use find to get a reference to an 'Empty GameObject' that contains children and then just search through the transform's children after that only using Find once, such as an empty gameobject 'Horses' that contains all horses as children

    I believe this is the syntax:
    Code (csharp):
    1.  
    2. //Code assumes an existing Empty GameObject called 'Horses' that contains all horse gameobjects as children
    3.  
    4. Transform transformHorses;
    5.  
    6. void Start()
    7. {
    8.     transformHorses = GameObject.Find("Horses").GetComponent<Transform>();
    9. }
    10.  
    11. void searchFunc()
    12. {
    13.     foreach(Transform child in transformHorses)
    14.     {
    15.        if(child.gameObject.name == "horseX")
    16.                //dostuff
    17.     }
    18. }
    19.  
    I'm not sure how the performance would be compared to using Find all the time, but it is an alternative.


    If you haven't been using Unity long, there's a weird relation between GameObject and Transform where sometimes they are used in place of each other, like a GameObject's Transform contains its position in the parent/child hierarchy, and so getChild and other functions like that sometimes return a transform or gameobject when you'd think they'd return the other one, and you can get the gameobject from transform component, but I won't go into it any further, just wanted to note they are sometimes used interchangeably.
     
    Last edited: May 27, 2016
  24. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    I just tried the following:

    Code (CSharp):
    1.     public void SendMessageDirect(GameObject gameobj, int nMsgID, Parameters parameters)
    2.     {
    3.         if (gameobj != null)
    4.         {
    5.             parameters.m_nMsgID = nMsgID;
    6.             parameters.m_gameobjSender = gameObject;
    7.             parameters.m_nSemiphore = 1;
    8. McCormickMonoBehaviourBase scrip = GetComponent<McCormickMonoBehaviourBase>();
    9. //OptionsButtonManager scrip = GetComponent<OptionsButtonManager>();
    10. scrip.DoDispatchMessage(parameters);
    11.             //gameobj.SendMessage("DoDispatchMessage", parameters);
    12.         }
    13.         else
    14.         {
    15.             string strError = "McCormickMonoBehaviourBase.SendMessageDirect(...) - gameobj is null...";
    16.             Debug.Log(strError);
    17.         }
    18.     }
    19.  
    This function is in my base class that replaces MonoBehavior (public classMcCormickMonoBehaviourBase : MonoBehaviour)


    McCormickMonoBehaviourBase scrip = GetComponent<McCormickMonoBehaviourBase>();
    scrip.DoDispatchMessage(parameters);

    Works fine except that it winds up calling the empty virtual function McCormickMonoBehaviourBase.DoReceiveMessage(...), not the implemented overridden DoReceiveMessage(...) in my derived class that is actually attached to a game object.

    So this method of invoking functions in a script is unusable if I want to take advantage of object oriented polymorphism - I am stuck with SendMessage("FuncName", object).

    Unless you can do something like this perhaps:

    void DoDispatchMessage(...)
    {
    super.DoReceiveMessage(...)
    }
    Is this possible in Unity / C#?

    Or unless there is a way through with the event system that some one mentioned.
     
  25. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Shouldn't you be calling GetComponent on the receiver, not the caller? As in

    Code (CSharp):
    1. McCormickMonoBehaviourBase scrip = gameobj.GetComponent<McCormickMonoBehaviourBase>();
    2. scrip.DoDispatchMessage(parameters);
     
  26. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    I have implemented a range of SendMessage(...) functions, including SendMessageToChild(...) which makes use of Transform.Find(...) and SendMessageToParent(...) which makes use of transform.parent.gameObject.

    So I am not using GameObject.Find(...) exclusively - only when there is no direct relationship between the sender and recipient in the game object hierarchy.
     
  27. ezjm427

    ezjm427

    Joined:
    May 17, 2014
    Posts:
    100
    I considered SendMessage a while ago, but then every resource I found said it was bad for performance compared to other methods like getcomponent from a collision or anything else, I think transform.find is the same as gameobject.find but returns a transform, I think it would use a lot more performance than simply getting the children of an already found transform.
     
  28. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Yes you are right.

    I didn't even stop to consider that, but my purpose was only to see if this method respected function overrides and apparently doesn't.

    Both the sender and the receiver script have overriden the void DoRecieveMessage(...), so I should have ended up stepping through the sender's DoRecieveMessage(...) function.

    I am rather puzzled by why SendMessage(...) respects function overrides but this method does not!
     
  29. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    But why would it matter if you had a 1000 static references to all your game objects?

    They are just references....equivalent to 64 bit pointer values or what ever.

    In the wider context 1000 x 8 byte values in a static array that remains for the life of the app is probably going to be fairly insignificant compared to dozens or hundreds of 10k png files and models etc?

    If you app can't cope with a permanent 8K then it sure aint going to cope with the rest of your resources.
     
  30. ezjm427

    ezjm427

    Joined:
    May 17, 2014
    Posts:
    100
    It depends where the references are stored - if all those static references are only in one class (like a resourceManager i mentioned), then no problem, but if each enemy has all those static references than it adds up quickly
     
  31. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Ok, I'm with you.

    In my case the head of the binary tree, and the functions that build and traverse it, would be a static members in McCormickMonoBehaviourBase. So all scripts attached to all game objects would refer to the same tree through my base class.
     
  32. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Transform.Find searches all children. GameObject.Find searches the entire scene. They are pretty significant differences.

    This is odd. Calling a method on an instance should call the most overrided method. I might run a test to check this behaviour. You've got a lot of other things going on in your messaging system that could be introducing bugs.

    Static references are evil because they break encapsulation and strict OOP practices. Abuse can lead to tangled, highly dependent spaghetti.

    Memory footprint for references is laughably insignificant in game dev.
     
  33. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Depends how you use the them I suppose.

    I can't see how using a static reference, in the binary tree context, is leading to spaghetti code. There is no reason why the member variable that forms the head of the tree can't be a private data member and all the derived classes can only access it through well defined interface of static functions.

    That would still comply with encapsulation to my way of thinking.
     
  34. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    As far as my messaging system goes it seems pretty straight forward as I have been stepping through the code with the debugger. There does not seem to be any asynchrony with SendMessage("FuncName", obj) at all and it seems little different to doing a direct call to the target function.

    What sort of bugs do you envisage it creating?
     
  35. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Here is another thing that I am struggling a little to come to terms with.

    In C++ all variables on the heap are declared thus:

    int *pInt = new int;
    MyClass * pObject = new MyClass;

    In C++ all variables that you want allocated on the stack or globally are declared thus:
    in Int = 0;
    MyClass Object;

    There is no difference in the way your declare the two different variables, whether you want them local or on the heap.


    But in C# (as I currently understand it) the following....

    int Int = 0;
    MyClass Object = new MyClass;

    Mean exactly the same thing, in that they are both allocated on the heap, regardless of the difference in the way they are declared.

    Coming from C++, the inconsistency in declaration style is disconcerting.
     
  36. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    You're not in charge of what goes on the heap and what goes on the stack. That's the runtime's job. The heap and stack are implementation details, and not important for the semantics of the language.

    In short, stop worrying. If you can't handle writing code without knowing exactly where your bits live, you shouldn't be writing C# (or Java or JavaScript or Ruby or... anything else than Assembly, C or C++).

    Eric Lippert (one of the devs creating C# back in the days) wrote about this here.
     
    Mycroft, Ryiah and Kiwasi like this.
  37. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    No. An int is a struct. Most of the primitive data types are. So an int goes on the stack of declared locally, or on the heap of declared as member of an object on the heap.

    The whole point of a managed language is you don't worry about these things in 95% of cases. Quit caring about it, and move on to actually writing code.

    Plenty. Mostly do do with getting a function name wrong. Or trying to call a function that does not exist.

    By using SendMessage you are bypassing compile time checking. Any bugs will move to run time. And that's normally a bad thing.

    You can make it work. But I'd strongly advise checking out ExecuteEvents to get the same functionality with compile time type checking.
     
    Mycroft and Ryiah like this.
  38. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Well what ever, but why is the difference in declaration style necessary?

    Why not:

    int Int = new int;
    MyClass Object = new MyClass;

    or

    int Int;
    MyClass Object;

    Either way the C#/Javascript/Ruby decides where to allocate the memory for them.

    All or nothing.......consistency...... to my way of thinking anyway.
     
  39. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    New invokes the constructor. A primitive has no constructor. So calling new makes little sense.
     
    Mycroft likes this.
  40. Dave-Carlile

    Dave-Carlile

    Joined:
    Sep 16, 2012
    Posts:
    967
    Then you're doing something wrong. This is just basic C# generics functionality. A component is just a normal object. When you get an instance of it and call a virtual method it's going to call the most derived method. I do this regularly and rely on it.
     
    Mycroft and Kiwasi like this.
  41. Dave-Carlile

    Dave-Carlile

    Joined:
    Sep 16, 2012
    Posts:
    967
    To be fair, you can do that.

    Code (CSharp):
    1. int z = new int();
    2. z = 10;
    3. Debug.Log(z);
    If you right click on "int" (in Visual Studio at least) and Go to Definition you can see the declaration - actually Int32 in this case since "int" is an alias...

    Code (CShar):
    1. public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int>...
    And there is no constructor declared so nothing special happens beyond whatever internal stuff the runtime does when allocating a struct (filling it with 0 I suppose?)

    But as @BoredMormon said, it makes little sense to allocate a primitive struct this way. Some structs may have an important constructor such as Vector2.
     
    Ryiah likes this.
  42. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Then show me how you have done it in the past with some code snippets.

    Because, as far as I can see, there is not much that you can do wrong.

    Either it works or is doesn't.
     
  43. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Ohhh!

    I did not realise that - in C# or javascript.

    And it never occurred to me to try it.

    I may be being pedantic but I might start doing that, regardless of whether it is required or not.......for the sake of consistency.
     
  44. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Did you try it again after my fix?
     
  45. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,205
    Yes, it fills it with a 0.

    Consistency? Or your desire to make C# act more like C++? That seems to be the real hangup here.
     
    passerbycmc, Mycroft and Kiwasi like this.
  46. Dave-Carlile

    Dave-Carlile

    Joined:
    Sep 16, 2012
    Posts:
    967
    Don't have Unity in front of me at the moment because I'm "working". But do some searching and there is plenty of evidence that it works.

    http://answers.unity3d.com/questions/119516/inheriting-from-a-class-that-inherits-from-monobeh.html

    Simple enough for you to test in an empty scene outside of your custom framework.
     
    Kiwasi likes this.
  47. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    No, I just like consistency period.

    I don't care which way it is in C# as long as it looks consistently the same.

    I am the same way (or at least I try my hardest to be) absolutely consistent in every way with my own classes etc. It avoids confusion down the track.

    But as some one has pointed out to me, it is entirely within my power to make my particular C# (and javascript) code consistent in the area that I was whinging about.

    And that's good enough for me.
     
  48. boylesg

    boylesg

    Joined:
    Feb 18, 2016
    Posts:
    275
    Sorry I must have missed you 'fix'. Time for a cuppa and then I will scroll back up and try and find it.
     
  49. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,205
    How is writing code in a manner contrary to the way the language was designed a way to avoid confusion?
     
    passerbycmc, Dave-Carlile and Kiwasi like this.
  50. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This one.

    At a glance it looks like your SendMessage implementation is targeting a different GameObject to your GetComponent implementation.