Any clues on this Unity code not working? - c#

I have this function here to duplicate a UIPanel(Prefab) and its components. It duplicates, then succesfully assigns a objectname and enable/disable the Interactable setting of the button component. But the part where it sets the ''OnClick'' on my button does not work. I dont understand as I can play with other options of this button component...
Yes the function Click exists.
Yes I included my using using System.Collections.Generic; using UnityEngine.UI;
Any Help? Thanks.
public Text Title;
public Button Btn;
public void CreateEventSquares(string keyval, string valueval)
{
string Event = (keyval);
string Link = (valueval);
Debug.Log("Event :" + Event + "Link is :" + Link);
NewPanel = Instantiate(Event0);
NewPanel.transform.SetParent(EventPanel.transform);
NewPanel.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f);
NewPanel.name = keyval;
//Sets the button action to trigger Click function (Not Working)
Btn = NewPanel.GetComponent<Button>();
Btn.onClick.AddListener(Click);
Btn.interactable = false;
//Sets the title same as keyval (This Works)
Title = NewPanel.GetComponentInChildren<Text>();
Title.text = keyval;
}
Click
public void Click()
{
//do This
}

I've re-created this in Unity with your code and it's working perfectly fine for me! I think it's highly likely you are being thrown by the fact that the function is not showing up in the on click portion of the button in the Unity Inspector.
Try putting a debug.log in your click function and see what happens.
If you wanted to still define functions in the inspector you would have to use a Unity Event and then subscribe it to the onclick.
[Serializable]
public class ButtonClickedEvent : UnityEvent { }
[SerializeField]
public ButtonClickedEvent m_OnClick = new ButtonClickedEvent();
Btn.onClick.AddListener(Click);
void Click ()
{
m_OnClick.Invoke();
}

Related

How do I get the text from a button after clicking it?

I am trying to get the text of the button clicked to compare it to a string.
public void clicked()
{
Debug.Log("Timer done");
secondsLeft = 0;
timerOn = false;
questionUI.SetActive(false);
Paused = false;
//turns off the question screen
string answer = GetComponentInChildren<TMPro.TMP_Text>().ToString();
if (questions[0] == answer)
{
PlayerQuests.points += 1000;
}
}
This is the code I made which the button runs after being clicked. This should get the text inside the button which is a TextMeshProUGUI and compare it to another string.
If you add using TMPro; at the top of your class, you should be able to get the text of the TextMeshProUGUI component by writing:
var answer = GetComponentInChildren<TextMeshProUGUI>().text;

Only use button every few seconds in Unity [duplicate]

This question already has answers here:
How to make the script wait/sleep in a simple way in unity
(7 answers)
Closed 1 year ago.
I am trying to make it so that a button in Unity that can only be pressed and activated every few seconds. I have an array with famous quotes and when I press my button it does display these quotes, but if you press the button continuously then it will cycle through all of the quotes without the ability to be thoughtful and read through them. Now, I have tried something like below, but it did nothing (as in the button still was able to be pressed and no effect was apparent):
Bool pressed;
void Function () {
if (pressed) {
//code to call array
}
}
I thought that a simple combination of bool and an if loop would be sufficient, but this does not work. Am I close to this concept? What would need to be changed to get this operating?
There are a few things you can do to achieve this. Your current code is never toggling the bool pressed. You can just catch the unwanted button presses with a localized bool like you are doing now, but there might be a better approach.
I would consider changing the intractability of the button to also show the end-user that they can not press the button for a short amount of time.
using UnityEngine.UI;
// button that will be pressed
[SerializeField] private Button btn = null;
// time until the button is re-enabled after being pressed
private float timeToWait = 5f;
// reference to the coroutine that toggles our button
private Coroutine buttonDisabled = null;
private void Start()
{
// you can either add the OnClick in the inspector, or you can programmatically add it here
btn.onClick.AddListener(ClickButton);
}
public void ClickButton()
{
// if we have an ongoing coroutine do not start another
if(buttonDisabled != null)
return;
// start our coroutine to re-enable the button
buttonDisabled = StartCoroutine(DisableButtonForSeconds(timeToWait));
// put any other code needed here such as displaying a new quote
}
private IEnumerator DisableButtonForSeconds(float seconds)
{
// disable the button
btn.interactable = false;
// wait for our amount of time to re-enable
yield return new WaitForSeconds(seconds);
// re-enable the button
btn.interactable = true;
// reset our reference to be called again
buttonDisabled = null;
}
Assign the button in the editor to btn and set whatever time you want to wait between button presses for timeToWait. You will also need to assign the button's OnClick to ClickButton, either in code or in the inspector. With this solution the function will not be able to be called as the button itself will be grayed out and not be able to be clicked. After the timeToWait duration is over, the button will reactivate. Place whatever other code you need in the ClickButton method that drives your quote cycling.
Edit: Here is similar code but using an Invoke instead of Coroutine as requested
using UnityEngine.UI;
// button that will be pressed
[SerializeField] private Button btn = null;
// time until the button is re-enabled after being pressed
private float timeToWait = 5f;
private void Start()
{
// you can either add the OnClick in the inspector, or you can programmatically add it here
btn.onClick.AddListener(ClickButton);
}
public void ClickButton()
{
// do not start the function if we are already in the process
if (IsInvoking("ReEnableButton"))
return;
// disable our button interactability
btn.interactable = false;
// call our function ReenableButton in timeToWait seconds
Invoke("ReEnableButton", timeToWait);
}
private void ReEnableButton()
{
// re-enable the button
btn.interactable = true;
}
Edit 2: There are a few ways you can go about having one button disable all four buttons. In general practice, you do not want these buttons knowing about each other, so it would be a good idea to make a button manager that handles the OnClick now and sends the event to all Buttons it knows about.
using UnityEngine.UI;
public class ButtonManager : MonoBehaviour
{
// assign these button references in the inspector by dragging them into the list
[SerializeField] private List<YourButtonScript> YourButtons = new List<YourButtonScript>();
private void Start()
{
// assign the onClick of each button here INSTEAD of the Start() in the button class
// you can still have an onClick in the Button itself, but do NOT have it do the disabling
foreach(YourButtonScript btn in YourButtons)
{
// assign our onClick to callback to disable all buttons
btn.SetDisableOnclick(DisableAllButtons);
}
}
public void DisableAllButtons()
{
foreach(YourButtonScript btn in YourButtons)
btn.DisableButton();
}
}
public class YourButtonScript : MonoBehaviour
{
using UnityEngine.UI;
// button that will be pressed
[SerializeField] private Button btn = null;
// time until the button is re-enabled after being pressed
private float timeToWait = 5f;
// reference to the coroutine that toggles our button
private Coroutine buttonDisabled = null;
public delegate void DisableButtonCallback();
public void SetDisableOnclick(DisableButtonCallback callback)
{
// add the callback to the manager
btn.onClick.AddListener(delegate{callback();});
}
private void Start()
{
// do NOT call the ClickButton anymore from here as the manager handles it
// if you have other button specific data put it in HandleSpecificButtonQuoteData() now
btn.onClick.AddListener(HandleSpecificButtonQuoteData);
}
private void HandleSpecificButtonQuoteData()
{
// put whatever code here that is specific to JUST this button
// if it handles the quotes or what not, display it here
}
public void DisableButton()
{
// if we have an ongoing coroutine do not start another
if(buttonDisabled != null)
return;
// start our coroutine to re-enable the button
buttonDisabled = StartCoroutine(DisableButtonForSeconds(timeToWait));
}
private IEnumerator DisableButtonForSeconds(float seconds)
{
// disable the button
btn.interactable = false;
// wait for our amount of time to re-enable
yield return new WaitForSeconds(seconds);
// re-enable the button
btn.interactable = true;
// reset our reference to be called again
buttonDisabled = null;
}
}
Let me know if you have issues with this.

How do I close a UI element when a tap occurs outside of it?

There are a lot of solutions to this problem floating around, but none of them seem to work properly for me. I have a button that opens a list of selections in a UI element, and I want it to close when clicked outside of it. I currently have this:
private void OnEnable()
{
EventSystem.current.SetSelectedGameObject(gameObject);
}
public void OnDeselect(BaseEventData eventData)
{
//Close the Window on Deselect only if a click occurred outside this panel
if (!mouseIsOver)
gameObject.SetActive(false);
}
public void OnPointerEnter(PointerEventData eventData)
{
mouseIsOver = true;
EventSystem.current.SetSelectedGameObject(gameObject);
}
public void OnPointerExit(PointerEventData eventData)
{
mouseIsOver = false;
EventSystem.current.SetSelectedGameObject(gameObject);
}
Which works fine on a PC, but unfortunately due to there not being an actual pointer on mobile, it closes the panel even if clicked inside. I have tried using something like this:
foreach (Touch touch in Input.touches)
{
int id = touch.fingerId;
if (EventSystem.current.IsPointerOverGameObject(id))
{
isClicked = true;
}
if (Input.GetMouseButtonDown(0))
{
// Check if the mouse was clicked over a UI element
if (EventSystem.current.IsPointerOverGameObject())
{
isClicked = true;
}
}
}
But that has not worked either. This seems like an incredibly simple problem and I don't understand why I can't find a simple solution to it.
I recomend using Unity technic when dropdown is created.
Look at this link
The main idea is to create blocker behind your button and the other ui elements.
Your open panel funcion should look something like this:
private RectTransform gameObjectTransform;
public void OpenPanel()
{
CreateBlocker();
gameObjectTransform = transform.parent;
transform.SetParent(GetComponentInParent<Canvas>());
transform.SetAsLastSibling();
}
Create blocker method like in docs:
private GameObject currentBlocker;
public void CreateBlocker()
{
GameObject gameObject = new GameObject("Blocker");
RectTransform rectTransform = gameObject.AddComponent<RectTransform>();
rectTransform.SetParent(ChatGraphics.Instance.mainCanvas, false);
rectTransform.anchorMin = (Vector2)Vector3.zero;
rectTransform.anchorMax = (Vector2)Vector3.one;
rectTransform.sizeDelta = Vector2.zero;
Canvas canvas = gameObject.AddComponent<Canvas>();
canvas.overrideSorting = true;
canvas.sortingLayerID = component.sortingLayerID;
canvas.sortingOrder = component.sortingOrder - 1;
gameObject.AddComponent<GraphicRaycaster>();
gameObject.AddComponent<Image>().color = Color.clear;
gameObject.AddComponent<Button>().onClick.AddListener(delegate { Hide(); });
currentBlocker = gameObject;
}
And last is Hide method which should be called every time (even if blocker does not triggered)
public void Hide()
{
if (currentBlocker != null)
Destroy(currentBlocker);
if (gameObjectTransform != null)
transform.SetParent(gameObjectTransform);
// Your code to hide your panel
}

why is OnPointerExit not called if the object the pointer is over is deactivated

I have a UI element with OnPointerEnter and OnPointerExit which work as I would expect while moving the mouse but if I have the mouse over the UI element and I press a different key to remove the UI element without moving the mouse away the OnPointerExit code is not called
using UnityEngine;
using UnityEngine.EventSystems;
public class UIInteractions : MonoBehaviour,IPointerEnterHandler,IPointerExitHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
Debug.Log("Pointer has entered " + gameObject.name);
PlayerControls.instance.UItarget = this;
}
public void OnPointerExit(PointerEventData eventData)
{
Debug.Log("Pointer has exited " + gameObject.name);
PlayerControls.instance.UItarget = null;
}
this is the code from another scrip which toggles the object with the UIInteractions script
if (Input.GetKeyDown("j"))
{
Journal.instance.ToggleJournal();
journalOpen = Journal.instance.journalPrefab.activeSelf;
if(journalOpen == false)
{
UItarget = null;
}
}
the ToggleJournal method is
public void ToggleJournal()
{
journalPrefab.SetActive(!journalPrefab.activeSelf);
}
if anybody has any insight as to why the OnPointerExit is not called when the ToggleJournal method is used or any potential workaround that would be much appreciated, thanks
FIX Example:
If you have a script that implements from OnPointerEnter and OnPointerExit, attached to an object in the scene.
what ever logic you have in the OnPointerExit, you can either execute that code or method in the
OnDestroy()
OnDisable()
aswell as in the OnpointerExit
e.g. i had this problem with a tooltip being showed. and i had the hide tooltip method being executed in the OnPointerExit.
when the object was deleted the tooltip stayed up, which looked stupid.
so i also called that hide method in destory and disable, so it will always get called
SO IN YOUR CASE I WOULD
create a method called
private void UpdatePlayerControls()
{
Debug.Log("Pointer has exited " + gameObject.name);
PlayerControls.instance.UItarget = null;
}
and the execute this method from OnPointerExit(), OnDisable() and OnDestroy()
Hope it helped
Ricky
A few ways to do it.
The simplest would be to add a UI element on the background that simply sets the UITarget to null on OnPointerEnter, instead of using OnPointerExit.
Another way is to also check that the current instance is the target on OnDisable, and if so, set it to null.

handle ngui's button event programmatically in unity

I'm transferring from Unity's UI to NGUI.
Formerly, I could add listeners to button with following scripts:
button.GetComponentInChildren<Button>().onClick.AddListener(() =>
{
//codes that could be triggered by click the button.
});
But when changing to NGUI, I can't make it work by:
EventDelegate.Set(go.GetComponent<UIButton>().onClick, OnButtonClickActive);
private void OnButtonClickActive()
{
Debug.Log("active button clicked");
}
Finally, I made this work by adding a custom event to OnClick with the following script (using UIEventListener):
UIEventListener.Get(go).onClick += OnButtonClickActive;
And the event handler is defined as follows:
private void OnButtonClickActive(GameObject go)
{
int level;
int.TryParse(go.GetComponentInChildren<UILabel>().text, out level);
Debug.Log(level + "active button clicked");
ApplicationModel.CurrentLevel = ApplicationModel.Levels[level - 1];
SceneManager.LoadScene("PlayScene");
}
Note that, it might be a little bit silly to pass the parameter (level info) with the UILable component in the gameobject. If there is other more elegant way, please let me know.

Categories

Resources