0
\$\begingroup\$

I am looking for elements on a canvas (a textfield and a slider); so I can retrieve values and pass them to a function.

But so far I can't figure out how to access them; I can see the methods of the canvas itself, but I can't see the name of the elements that I did add on the canvas.

I have the canvas saved in a variable which is public; then I did place a slider and textfield, connected to text labels; but I can't seem to access neither the label nor the slider or textfield.

Canvas --
        |___ Text
        |        |_____ InputField
        |
        |___ Text
                 |_____ Slider 

I did plan to access to the text value in the input field doing something like canvas.text.inputfield.text, but this does not work, since I can't see the name of the text field when using dot notation. Same goes for the slider.

public Canvas mainmenu;

void Start() {
    mainmenu = mainmenu.Getomponent<Canvas>();
}

public RetrieveData(){
    // dot notation does not find sub elements of mainmenu canvas
    // only the properties and methods of the canvas itself.
}

How do you actually access elements on a UI canvas, if you can't see them directly, even if they are parented to other elements?

\$\endgroup\$
1
\$\begingroup\$

I'd recommend against finding GameObjects by name (using GameObject.Find or transform.Find as suggested in the existing answers) whenever you can avoid it.

In my experience this tends to lead to brittle relationships between scripts that are easy to break accidentally, and other more exotic bugs.

The most reliable method, if you have a script that needs to use a particular InputField and Slider, is to give it public/serialized fields of these types:

public class MyUIForm : MonoBehaviour
{
    public InputField inputField;
    public Slider slider;
    ...

...and wire them up in the Inspector.

This makes the dependency between these objects explicit to anyone using the editor - as soon as you attach this script, the Inspector tells you what it needs.

This dependency is also visible to Unity so it knows which assets need to be included together in a build, if they're not all scene objects. And these references are populated immediately when the object is loaded, which avoids common order-of-execution problems when one script tries to access fields of another one that hasn't had time to Start() yet.

Adding another object of the same type or the same name or changing the parenting hierarchy won't accidentally break this, because the object has a direct reference. Even in a scene with 100 similarly-named/functioning objects, you have the ability to specify the exact one you want.

If these objects don't exist in the same scene, you can also create intermediate scripts that hold these references. eg. a MainMenu script that knows about each of its child pages like HighScoresMenu and SettingsMenu, and SettingsMenu holding references to its inputs PlayerNameField and GammaSlider. Then if another script needs access to these fields (not sure why - any script using them should probably live in SettingsMenu, but let's assume there's a good reason) - it can get a reference to the MainMenu instance and use mainMenu.settingsMenu.gammaSlider. These intermediate scripts are cheap, and save the rest of your code from needing to know the guts of how your menu is organized in the hierarchy, as well as centralizing the work of wiring up scripts in the inspector.


If you want to wire it up more automatically, you can also use GetComponentInChildren:

public Canvas mainMenu;
private InputField _inputField;

void Start()
{
    _inputField = mainMenu.GetComponentInChildren<InputField>();
}

This will search the child hierarchy of mainMenu, including grandchildren and so on, until it finds something with an InputField component and return that. (You can also use GetComponentsInChildren to get an array of all matches)

This has some of the same problems as finding an object by name (moving the InputField to a new parent could cause us to miss it; adding another unrelated InputField could cause us to grab the wrong one), but we do know we're getting the right type back. It also won't be broken by someone trying to improve readability by changing the object named "InputField" to be named "PlayerNameInput" or just fixing a typo/capitalization.

When used with your own types that are explicit about their use, like the SettingsMenu example above, these risks largely disappear. You might create a form with two InputFields, but you're unlikely to ever use two copies of the SettingsMenu, so if the MainMenu script used GetComponentInChildren to find it instead of a direct reference, it's unlikely to ever be broken accidentally.

\$\endgroup\$
0
\$\begingroup\$

There are multiple possibilities here.

  • You can use the static function GameObject.Find() to find a gameobject by name in the entire scene. However, this function is a bit slow so try to call it only once.

  • You can also use the non-static function Transform.Find() that will only search in the childrens of the transform. Transform.FindChild is deprecated and is the same as Transform.Find().

\$\endgroup\$
  • \$\begingroup\$ GameObject.Find() will give you the first gameObject in hierarchy. It cannot be use to find same named objects as OP did show in question, as far as your point of FindChild is concern, you could edit the answer or leave comment if there is no big change or mistake \$\endgroup\$ – Hamza Hasan Feb 2 '16 at 12:24
0
\$\begingroup\$

It is because you brought Canvas itself and you are trying to access gameObject's properties. So for that try,

mainmenu.transform.Find.......
\$\endgroup\$
  • 1
    \$\begingroup\$ You can access mainmenu.transform directly - no need to go via .gameObject. That said, I have some reservations about code wiring itself up by names. In any project I've worked on for more than a week or two, this has started to lead to bugs, from people changing names they didn't know were important, or duplicating objects, or trying to use prefabs, or fixing typos, or... ;) \$\endgroup\$ – DMGregory Feb 2 '16 at 14:26
  • \$\begingroup\$ As far as your this "You can access mainmenu.transform directly" is concerned, you are right. rest is just exact about the solution what he asked. \$\endgroup\$ – Hamza Hasan Feb 2 '16 at 14:34
  • 1
    \$\begingroup\$ OP asked how to access UI components. The only way they could think to do this is by name (likely since this is how it's done in ActionScript and similar environments) but that does not mean this is the only kind of solution we should offer. \$\endgroup\$ – DMGregory Feb 2 '16 at 14:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.