Unity: UI.Button Adding OnClick Event From Script Fails - unity3d

In my application I have a list of GameObject's, and I'm creating a button for each GameObject in the list via code. The problem is that when I add and onClick Event via code nothing shows up in the button onClick list, nothing happens when I click the button and I get no errors in the process. Here is how my code looks like:
public GameObject prefab;
public void Generate()
{
for (int i = 0; i < myList.Count; i++)
{
GameObject _t = Instantiate(prefab, myUIPanel.transform) as GameObject;
//Positioning, naming, ...
_t.GetComponent<Button>().onClick.AddListener(delegate { MyFunction(i); });
}
}
public void MyFunction(int index)
{
//...
}
I've created a GUILayout.Button inside an editor script to call the "Generate" method. The buttons are created and I get no errors, but no Event is added in the buttons.

edit: because you ui element is being instantiated from the prefab the button is not hooking up to EventSystem
workaround: use scriptableobject instead of monobeviour, scriptableobjects work independent from scene/gameobject instances
tutorial:
https://docs.unity3d.com/Manual/class-ScriptableObject.html
https://www.raywenderlich.com/2826197-scriptableobject-tutorial-getting-started

Related

AudioSource attached button doesnt work in another scene

public AudioSource menumusic;
public Button SoundButton;
public Sprite Soundoff;
public Sprite SoundOn;
bool isClicked = true;
private void Awake()
{
DontDestroyOnLoad(this.gameObject);
SoundButton.image.sprite = SoundOn;
}
// Start is called before the first frame update
void Start()
{
menumusic = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
SoundButton.onClick.AddListener(BtnOnClick);
}
void BtnOnClick()
{
changeState();
}
private void changeState()
{
isClicked = !isClicked;
if(isClicked)
{
SoundButton.image.sprite = SoundOn;
menumusic.Play();
}
else
{
SoundButton.image.sprite = Soundoff;
menumusic.Stop();
}
}
I have script like this And I am using on menu scene but when I switch to game scene with other button , this script works in there to except that he can not find soundbutton , how can I link to sound button from another scene should I create same to game scene too ? , sorry I am new on unity.
It looks like you're setting the reference to your "SoundButton" object in the inspector. When you change scenes, you'll need to update the reference on this script to refer to the new button.
There are many ways to do this. You could just tag the soundButtons with "soundButton".
Then, wherever you're loading a new scene, update the reference by searching for the tag.
SoundButton = GameObject.FindObjectWithTag("soundButton").GetComponent<Button>();
It would also be possible to have a SoundButton script which you place on every sound button. This could then reference a static SoundManager object, and tell it to toggle the sound on or off when clicked. This is probably a cleaner way to do it.
Also note: You're adding a listener to the soundButton in Update() (i.e. every frame). This is unnecessary. Add the listener one time in Start(), or when you change Scenes, and it will trigger the event whenever the button is clicked. There is no need to use Update() for this process.
If it still isn't working, you can also check the variable before using it, and get the reference again if it's null:
if(soundButton == null)
{
SoundButton = GameObject.FindObjectWithTag("soundButton").GetComponent<Button>();
}

Need a way to use button click functionality vs GetKeyDown in Coroutine

This animator script works, however, in place of the Keycode input inside the WHILE LOOP, I need to use a UI button for mobile, which I haven't been able to figure out. I found an answer about putting a wrapper around it to make method available to click event, but have no idea how that's supposed to work within update function. It took me a long time to get this far, being a newbie to unity AND c#, so if you could provide a detailed answer or suggestion, I can get my life back, please help if you can.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using SWS;
public class goat_question : MonoBehaviour
{
private Animator anim;
public GameObject player;
public Text ResultText;
public Text AnswerText;
public Text AnswerText2;
public Button GoatButton;
void Start()
{
anim = GetComponent<Animator>();
Button btn = GoatButton.GetComponent<Button>();
btn.onClick.AddListener(TaskOnClick);
}
void TaskOnClick()
{
Debug.Log("You have clicked the button!");
}
// Update is called once per frame
void Update()
{
if (AnswerText.text.Equals(AnswerText2.text))
{
StartCoroutine(GoatWalkPathCoroutine());
}
IEnumerator GoatWalkPathCoroutine()
{
while (true)
{
if (Input.GetKeyDown(KeyCode.K))
{
anim.Play("goat_hi_walk");
player.GetComponent<splineMove>().enabled = true;
yield return new WaitForSeconds(27);
anim.Play("goat_hi_licking");
}
yield return null;
}
}
}
}
In a separate script for just the UI button, have a bool called isClicked or something, and when the button gets clicked set that to true. In this main script, you can reference the one you just made, and instead of the Input.GetKey, you can say, if(otherScript.isClicked).

Unity says that 'Button' is not a Component and doesn't derive from Monobehavior

This was originally coded in unity 2018 then ported to unity 2019 because my main project is on that version. It works perfectly in unity 2018 (aside from that button.clicked += used to be button.AddListener())
The only relevant bit of the code is lines 12 - 17 but who knows.
Script is attached to object with the Button component.
Unity 2019.4.11f1
Returns the following error with the setup listed above:
ArgumentException: GetComponent requires that the requested component 'Button' derives from MonoBehaviour or Component or is an interface.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UIElements;
using UnityEngine.Events;
public class ButtonProperties : MonoBehaviour {
public ButtonType buttonType;
public string dest;
Button button;
void Start()
{
button = GetComponent<Button>();
//perform different functions dependant on the button type
if (buttonType == ButtonType.Scene)
{
//add ButtonLoadScene as a listener to the the button
button.clicked += ButtonLoadScene;
}
else if (buttonType == ButtonType.Menu)
{
button.clicked += ButtonLoadMenu;
}
//change button text to "Load Scene" + the name of the scene
string str = "Load " + dest;
GetComponentInChildren<TextElement>().text = str;
}
//private scene loader because event listeners cant take arguments >:(
void ButtonLoadScene()
{
SceneManager.LoadScene(dest);
}
void ButtonLoadMenu()
{
//array of all canvases in the scene
Canvas[] canvases = GameObject.FindObjectsOfType<Canvas>();
//foreach canvas
foreach (Canvas canvas in canvases)
{
//if the canvases name is the desired name
if (canvas.name == dest)
{
//turn all canvases off
foreach (Canvas c in canvases)
{
c.enabled = false;
}
//turn current canvas on
canvas.enabled = true;
//break loop
break;
}
}
}
}
public enum ButtonType
{
Scene,
Menu
};
I also tested this by adding a NewBehaviourScript to the button with Button button; and, in the start function, Start() {button = GetComponent<Button>();}
Same error
I believe you are using the wrong namespace for your button. Try changing:
using UnityEngine.UIElements;
to
using UnityEngine.UI;
There is a Button in both namespaces, but the one in UnityEngine.UI is likely the component you're working with.
Had somebody else on the team test the build, as I started to think from the comment thread, I was missing the UI library. Changing it to the UI library was the solution but not for me until I restarted the computer. Thank you Thomas Finch for the help.

Instead of LoadLevel, how do i use SceneManager

I super new in coding and I couldn't find the right answer on the web. All I want is just change scene in my game. I've already had the buttons etc. But i can't choose the "menu" script from the On click function menus.
All answer is welcome!
using UnityEngine;
using UnityEngine.SceneManagement;
using System.Collections;
public class Menu : MonoBehaviour {
public void ChangeScene(string sceneName)
{
SceneManager.LoadScene("sceneName");
}
}
Several problems here. First, the function "ChangeScene"
public void ChangeScene(string sceneName)
{
SceneManager.LoadScene(sceneName);
}
Then, you may have a problem in your scripts since you can't add any listener to the OnClick delegate. Fix every problem thrown by the console. A strange thing is that the name of the Menu script does not appear in the Menu component. Make sure the filename is "Menu.cs"
Finally, drag & drop the button into the field under "Runtime Only" and select "Menu > ChangeScene"
You can't just plug in the Menu script to the left slot.
You have to attache Menu to a GameObject then plug that GameObject to the left slot.You will then be able to chose which script and function to send the event to on the right.
The Image below shows the wrong way to do this(This is how you are currently doing it):
The is the correct way to do it:
You can also do this from code:
public class Menu : MonoBehaviour
{
public Button playButton;
void Start()
{
//Add Button Event
playButton.onClick.AddListener(() => buttonCallBack(playButton));
}
public void ChangeScene(string sceneName)
{
SceneManager.LoadScene("sceneName");
}
private void buttonCallBack(Button buttonPressed)
{
if (buttonPressed == playButton)
{
ChangeScene("myscene");
}
}
}

Unity3D Programmatically Assign EventTrigger Handlers

In the new Unity3D UI (Unity > 4.6), I'm trying to create a simple script I can attach to a UI component (Image, Text, etc) that will allow me to wedge in a custom tooltip handler. So what I need is to capture a PointerEnter and PointerExit on my component. So far I'm doing the following with no success. I'm seeing the EVentTrigger component show up but can't get my delegates to fire to save my life.
Any ideas?
public class TooltipTrigger : MonoBehaviour {
public string value;
void Start() {
EventTrigger et = this.gameObject.GetComponent<EventTrigger>();
if (et == null)
et = this.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry entry;
UnityAction<BaseEventData> call;
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerEnter;
call = new UnityAction<BaseEventData>(pointerEnter);
entry.callback = new EventTrigger.TriggerEvent();
entry.callback.AddListener(call);
et.delegates.Add(entry);
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerExit;
call = new UnityAction<BaseEventData>(pointerExit);
entry.callback = new EventTrigger.TriggerEvent();
entry.callback.AddListener(call);
et.delegates.Add(entry);
}
private void pointerEnter(BaseEventData eventData) {
print("pointer enter");
}
private void pointerExit(BaseEventData eventData) {
print("pointer exit");
}
}
Also... the other method I can find when poking around the forums and documentations is to add event handlers via interface implementations such as:
public class TooltipTrigger : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
public string value;
public void OnPointerEnter(PointerEventData data) {
Debug.Log("Enter!");
}
public void OnPointerExit(PointerEventData data) {
Debug.Log("Exit!");
}
}
Neither of these methods seems to be working for me.
Second method (implementation of IPointerEnterHandler and IPointerExitHandler interfaces) is what you're looking for. But to trigger OnPointerEnter and OnPointerExit methods your scene must contain GameObject named "EventSystem" with EventSystem-component (this GameObject created automatically when you add any UI-element to the scene, and if its not here - create it by yourself) and components for different input methods (such as StandaloneInputModule and TouchInputModule).
Also Canvas (your button's root object with Canvas component) must have GraphicRaycaster component to be able to detect UI-elements by raycasting into them.
I just tested code from your post and its works just fine.