Dynamic button script, and OnClick actions?

I’ve made a few basic UIs that interface with scrupts, i’ve found the process fairly simple, but up until now most of the things i’ve created are permanantly onscreen. The only thing i’ve done dynamically is filling in textfields

I’m presently attempting to make dynamic buttons for a strategy game. The buttons won’t really handle anything except clicks, all i want them to do is callback to the main script in the panel with their ID number, when they’re clicked.

As can be seen in the picture above, i’ve identified the object, script and function which i want to recieve the callback. But then it gives me a blank text field. What am i supposed to put in there?

I would like to either:

  1. Programatically set the text for that field which will be returned. Is this possible?

or

  1. Have the button send something specific about itself. For example, the name field of its gameobject. Then i can just set the name to the required ID

If there’s a textfield, it’s because you’ve A: set it to reference a member and not a function or B: the function you’re referencing has a “string” or “int” or some other parameter option (like: ReceiveButtonInput(string SomeString){ }). You should use that blank space to put in whatever data you’re wanting that function to receive from this particular button script when it’s clicked. It’s worth noting that if you choose the function to receive a GameObject or Transform parameter, you can simply drag the button object itself into the OnClick() and it’ll send a reference to itself as a parameter.

As for programatically changing the text that’s stored and sent on OnClick, you can use the GameObject method I just described but also have a different little script attached with a public string. You can then (in the function triggered by the OnClick() event) reference the additional script with a GetComponent().someString and get the data that way. Changing the string value can be done from anywhere where the button can be referenced, the same way.

There’s literally a near-infinite number of ways to do just about anything in Unity, so it’s really up to preference, but I have a feeling that it might be best if the script your calling has internal references to ALL buttons, that way when the button sends a reference to itself, all you have to do is check equality to figure out which one fired. Depending on the function of the buttons, it might be better if they all just call different functions anyways, though.

2 Likes

I knwo that unity has lots of ways to do things, and i’m reasonably well versed in C# by now, but i don’t know how this particular feature (the UI buttons) is implemented. That editor interface for telling it what to do when clicked doesn’t seem to correspond to anything i know of internally.

You mention events. Do buttons use an event system of some sort? Can i maybe add listeners to those events?

You’re correct that my function takes a string as input, because i wanted the button to return a string. but the gameobject idea does sound better actually. i can still use my naming idea and store an index number there for quick lookups.

I want to know more though, anymore indepth information you could provide would be helpful.

if you want the button to send a reference to itself you just need a “UnityEngine.UI.Button” parameter on the function, and drag the button into the field.

ok i got the self reference idea basically functional, however it doesn’t seem suitable for my needs…

As i’m creating and removing these buttons dynamically, i need the data to persist when it’s removed and added. But a reference to the actionpanel script doesn’t seem to persist when i move it into the prefabs folder and back out. I can’t say if it’ll work when instantiated from a property of an existing object, but the impermanance already worries me.

see #5, i’m not sure that’s suitable for my needs anymore.

I would like to, even just to see if i can, get this working entirely without using this editor interface

How can i do this purely through code. Say my master script has instantiated an object, stuck the button script onto it, now what. Which properties can/must i set in order for it to callback to the master when clicked.

references are only retained “within” a prefab, they don’t keep the references to gameobjects that aren’t a part of the prefab

the new ui works off the unity eventsystem (hence one appearing when you add a ui element for the first time).

I’ve found this thread handy when figuring out what you can do with it in code:

Reference the button’s GameObject and use someButton.GetComponent<Button>().onClick.AddListener(() => SomeFunction(SomeParameter)); in order to add a new listener to the event. You can remove individual or all listeners using similar functions, but unfortunately the documentation doesn’t look very up-to-date. Let intellisense guide you, I’d say.

1 Like

ok thank s you guys i’m starting to get a little somewhere, i think,

button.onClick.AddListener() takes an input i’m not exactly familiar with. it wants a UnityAction (UnityEngine.Events.UnityAction)

I learn best by example. i think i need some kind of small, workable example. I don’t understand how (or if? ) a listener could influence what the event returns. there’s a bit of a missing link in my mind here. I don’t know exactly what that listener expects as an input, or what format.

I’d assume somewhere, somehow, i could point it to a function?

There’s a video here that may be of some use to you. Also an older question here that covers some of the same stuff and this covers delegates and actions a bit better.

I’m not at all an expert on this topic, so excuse me for only offering links to things my betters have already posted on the subject- I can’t explain it better than they can.

1 Like

so far i’ve got buttonScript.onClick.AddListener(() => panel.RecieveButtonInput(index));

doesn’t seem to be working at all, compiles fine but the function just doesn’t fire
Still watching my way through the videos, would appreciate anymore advice

A different and perhaps easier way to do it: make your button script implement the “IPointerClickHandler” interface (you need the UnityEngine.EventSystems namespace) :

using System;
using UnityEngine;
using UnityEngine.EventSystems;

public class MyButtonScript : MonoBehaviour, IPointerClickHandler
{
    public int myIndex;
    public OtherPanelThingy panel;

    public void OnPointerClick(PointerEventData eventData)
    {
         panel.RecieveButtonInput(myIndex);
    }
}

Any MonoBehaviour that implements IPointerClickHandler will automatically detect clicks and call its OnPointerClick method when you click it.

try one of these:

buttonScript.onClick.AddListener(() => {panel.RecieveButtonInput(index);});
buttonScript.onClick.AddListener(delegate{panel.RecieveButtonInput(index);});
1 Like

yaay, seems i had some other unrelated problems. This is working now. Huge step forward, thank you Lysander :smile:

As a side note, if you happen to know much about buttons or UI in general, i have another, seperate issue with this same button panel: How do LayoutGroups work? I cant get it to behave how i want - Unity Engine - Unity Discussions