- Home /
The question is answered, right answer was accepted
How can I bind a custom object or variable to an UI button?
Hello,
I have a ScriptableObject that holds some data for a song object. I created multiple instances of that ScriptableObject and saved them as assets, one for each song.
I instantiate multiple UI Buttons at runtime, the button text is the title of a song. That works fine, but how do I know which song I chose when I click a button? How can I bind a song object to an UI button?
Do I have to write a custom button class and inherit from Button somehow? Tips are very much appreciated. :-)
Answer by fafase · Feb 25, 2018 at 10:36 PM
I will assume the button creation and you will have to turn that into what you actually have (since you did not expose your code).
We going to consider that each of your ScriptableObject contains a songName variable.
public MyScriptableObject [] scriptables;
void CreateButtons()
{
foreach(MyScriptableObject so in scriptables)
{
GameObject obj = new GameObject(so.songName);
Button button = obj.AddComponent<Button>();
// Now the tricky part
MyScriptableObject temp = so;
button.onClick.AddListener(()=>{ Debug.Log(temp.songName); });
}
}
That code should print the name of the song contained in the ScriptableObject on press. There is the tricky part that needs a quick explanation. In a foreach loop the compiler does some magic on the background and long story short, if you don't do the trick in the temp variable, all your buttons will point to the last ScriptableObject of the array. So you need to store in a temporary to fix the problem. Not a Unity issue, it is a C# limitation.
The AddListener is the method to add a delegate to the button on click.
Answer by Vencarii · Feb 26, 2018 at 06:25 AM
I had a similar idea about an hour after I posted my question, but I still didn't get it to work. But your code helped me to fix the problem. I use this methid now:
public void ShowSongList()
{
GameObject canvas = (GameObject)Instantiate (songMenuPrefab) as GameObject;
GameObject menu = canvas.transform.Find("Panel").gameObject;
List<Song> songs = _gameManager.GetAllSongs ();
for (int i=0; i<songs.Count; i++) {
// Create song item
GameObject item = (GameObject) Instantiate (songItemPrefab) as GameObject;
// set menu button label
item.transform.Find ("SongTitle").GetComponent<Text> ().text = songs [i].songName;
// onClick Event
// Storing the song in a tmp variable helped me! I tried to use OnClickSongItem(songs[i]), that didn't work
Song tmpSong = songs[i];
item.GetComponent<Button> ().onClick.AddListener (() => OnClickSongItem(tmpSong));
// add item to panel
item.transform.SetParent (menu.transform, false);
}
canvas.SetActive (true);
}
public void OnClickSongItem(Song song)
{
Debug.Log (song.songName);
}
GameObject item = (GameObject) Instantiate (songItemPrefab) as GameObject;
you don't need casting before and after. Just pick one, I'd remove the as GameObject.
For the rest you seem to get it right. I'd use some global references ins$$anonymous$$d of looking for SongTitle each iteration.
Follow this Question
Related Questions
UI button does not press if colliding with other UI object 1 Answer
How to rotate boject using UI button? 3 Answers
How to display UI elements in front of the gameObject? 1 Answer
I need to move a sprite when an UI button is clicked 1 Answer
Can't interact with world space ui button when cursor mode is locked any solutions? 1 Answer