Problem with GameObject.Find(...)

I have this sort of hierarchy in the Unity editor (Bingo3D):

Options->Pattern2Row->Card->Counter1

Counter25

Pattern2Col->Card->Counter1

Counter25
PatternX->Card->Counter1

Counter25

Counters 1-25 are just small circles used to represent the buttons placed by players on their bingo card and they are children of the Card objects if that is not clear above.
The game objects with the name ‘Card’ are just 2D sprites representing the bingo cards.

Scripts/classes of the same name are attached to Pattern2Row, Pattern2Col and PatternX

These scripts/class are derived from a parent script/class called PatternManager and simply set an enumerated type member variable, in their constructors.

The base script/class then use that enumerated type member variable to determine which pattern to display on the bingo card sprite, i.e. by simply showing and hiding Counter1-25. In effect it is an animation I suppose and could have been done with blender…but never mind.

In the base script/class I have GameObject members like this:

private GameObject m_gameobjCounter1;

private GameObject m_gameobjCounter25;

I want to use GameObject.Find(“…”) to set each of these GameObject members to the appropriate game object in the Unity editor hierarchy described above.

In effect I am trying to avoid having to drag and drop over 100 game objects in the Unity editor and also quarantine the links from any changes I might want to make that involves removing or moving scripts - often when you do that sort of thing Unity editor deletes all the links and you have to drag and drop all over again.

So my question…

I know that you can specify a path as well as a name in GameObject.Find(“…”)

But how can I ensure, for example, that the path “Card/Counter1” refers to “Pattern2Row->Card->Counter1” for the instance of the script/class attached to Pattern2Row game object in the Unity editor?

This is kinda hard for me to visualize what your after. Can you post a screen shot of the gameobjects?

So the primary GameObject is “Pattern2Row” with subject “Card” and it has a subobject “Couter1”. Then you have script attached to the primary GameObject. This script has 25 private variables, and you want the script to auto assign the subjects to those?
That can be done with GetComponentsInChildren<>()

but I’m not sure how you’re going to distinguish between them to make sure, Couter1 in assigned to m_gameobjectCounter1. That maybe the hard part to figure out.
And I don’t know what you need to access on the child object.

You might be able to do it the reverse, create a script for the child object to updates the variable on the parent object. That would be easy to create. I could help you with that.

Screen shot attached.

I thought the GetComponent functions were exclusively for fetching components within a single game object - rigid bodies, colliders and such like?

Never mind I figured it out.

This works nicely:

m_gameobjCounter1 = gameObject.transform.Find(“Card/Counter1”).gameObject;

1 Like

here is what I was thinking.

for the parent object

public class Pattern : MonoBehaviour {

    [SerializeField]
    private GameObject m_gameobjCounter1;
    [SerializeField]
    private GameObject m_gameobjCounter2;
    [SerializeField]
    private GameObject m_gameobjCounter3;
    [SerializeField]
    private GameObject m_gameobjCounter4;
    [SerializeField]
    private GameObject m_gameobjCounter5;



    // Use this for initialization
    void Start () {
   
    }
   
    // Update is called once per frame
    void Update () {
   
    }

    public void SetGameObjectInParent(int i, GameObject childObject)
    {
        switch (i)
        {
            case 5:
                m_gameobjCounter5 = childObject;
                break;
            case 4:
                m_gameobjCounter4 = childObject;
                break;
            case 3:
                m_gameobjCounter3 = childObject;
                break;
            case 2:
                m_gameobjCounter2 = childObject;
                break;
            case 1:
                m_gameobjCounter1 = childObject;
                break;
            default:
                print("Not found");
                break;
        }


    }

and for the child object

public class Counter : MonoBehaviour {
    public int myInt = 1;

    // Use this for initialization
    void Start () {
        GetComponentInParent<Pattern>().SetGameObjectInParent(myInt, gameObject);
    }
   
    // Update is called once per frame
    void Update () {
   
    }
}

What about the issue of manually setting 100+ objects
The problem you’ll find with your code it that if you have multiple gameobjects with the same name. it might not assign the correct Counter1 to it parent.

Well the transform version of Find(…) apparently works like the gold old MS DOS 6.22 concept of a ‘current working directory’.

If you care copying files with in the ‘current working directory’ then you don’t have to specify a full path (from the root) in either the source file or the destination location.

Apparently GameObject.Find(…) assumes a full path from the root regardless of whether or not you specify it in the string you pass to the function.

I’d approach this a different way. I’d Instantiate the pieces from prefabs and then keep references to them in an array (or other appropriate collection).

Essentially just a couple of for loops to set up. Then you have solid references forever.

My problem with that method is that it is just a flat list. So if I wanted to send the same message to only particular objects in that list then I would have a lot of code to determine the identity of each game object and whether or not it should receive that particular message type.

And every time I added another game object I would have to go an modify that if/else statement to take account of the new game object.

In the scheme I have come up with the list is structured in that a message type is associated with the game objects that should receive it if it is broadcast - a lot less overhead than iterating through a full array of game objects.

Populating my array is as simple as doing these steps:

Add your message type into this:

public enum MessageIDEnum
{
   MSGID_CHANGE_PATTERN,
   MSGID_CHANGE_AUTO_SPIN,
   MSGID_SAVE_CHANGE_PATTERN,
   MSGID_SAVE_CHANGE_AUTO_SPIN,
   MSGID_SHOW_OPTIONS,
   MSGID_HIDE_OPTIONS,
   MSGID_SHOW_BINGO,
   MSGID_HIDE_BINGO,
   MSGID_SHOW_HIDE_COUNTER,
}

Register you script class to receive a particular message type like this:

public PatternManager()
   {
     RegisterMessageReceiver("PatternManager", MessageIDEnum.MSGID_SAVE_CHANGE_PATTERN);
   }

Post a message to registered receivers of that message type like this:

    Parameters p = new Parameters();
     p.m_objParam1 = m_bAutospin;
     PostMessage(MessageIDEnum.MSGID_SAVE_CHANGE_AUTO_SPIN, p);

And process your received messages like this:

protected override void DoReceiveMessage(MessageIDEnum enMsgID, Parameters p)
   {
     if (enMsgID == MessageIDEnum.MSGID_CHANGE_PATTERN)
     {
       m_enPattern = (PatternEnum)p.m_objParam1;
     }
     else if (enMsgID == MessageIDEnum.MSGID_CHANGE_AUTO_SPIN)
     {
       m_bAutospin = (bool)p.m_objParam1;
     }
     base.DoReceiveMessage(enMsgID, p);
   }

     }
     else if (enMsgID == MessageIDEnum.MSGID_CHANGE_AUTO_SPIN)
     {
       m_bAutospin = (bool)p.m_objParam1;
     }
     base.DoReceiveMessage(enMsgID, p);
   }

My idea is not really original.

It has been spawned from message maps in Microsoft Foundation Classes

In windows programing the windows sub-systems communicate asynchronously with the various windows in your application by means of ‘messages’ which amount to unique integer IDs plus any parameters.

MFC has a series of macros that you use to build a message map.

In your class header file you put this in your class definition: DECLARE_MESSAGE_MAP(ClassName)
In your class cpp file you put this:

BEGIN_MESSAGE_MAP(CMyDoc, CDocument)
ON_COMMAND(ID_MYCMD, &CMyDoc::OnMyCommand)
END_MESSAGE_MAP()

There are dozens and dozens of macros that correspond to the various windows messages that you application can receive from the windows sub-system.

Visual Studio makes this very easy with a ‘Class Wizard’.

You select the class that you want to add a message handler to, then select the type of windows message you want your class to receive and the name of the function that will handle that message.

Then the class wizard populates the above message map with the appropriate macro and it creates the handler function for you along with any specific pre-defined parameters that it needs.

It makes maintaining your application and adding functionality to it very quick and easy.

For example, when I see the ON_COMMAND macro in a message map, then I immediately know that it is related to the menu system (as File->open). The first parameter is the unique integer ID of the menu command that is being acted upon. Normally the name of you ID would reflect the hierarchy of the menu e.g. ID_FILE_OPEN, ID_FILE_SAVE etc. The second parameter is a pointer to the function that handles than menu command.

So the MFC message map provides you with an easily identifiable table of contents, of sorts, for the major functional components of your windows class.

So I am trying to emulate this sort of convenience in my Unity scripts.