- Home /
An Instantiated UI Button doesn't work; clicking does nothing.
I'm trying to make a system where the player can attach different "modules" to "nodes on their vehicle. So I want the player to be able to enter the inventory, showing all their modules, click a module they want to attach, at which point their ship is displayed, with images highlighting each available attachment node. These images will be buttons which, once clicked, attach the chosen module to that attachment point. I have a script which instantiates the buttons as children of some empty "attach point" game objects which are in turn children of the player's vehicle. So the buttons show up fine, but I can't interact with them. I click them but nothing happens. I've checked that there's an EventSystem in the hierarchy and that the buttons are set to interactable.
Here are some pictures of the component setup on the button prefab:
The code which instantiates the buttons (which is working) is
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using Assets._Custom._Scripts._Helpers;
using Assets._Custom._Scripts._ShipScripts;
namespace Assets._Custom._Scripts._GUIScripts
{
public class ShipImageDisplay : MonoBehaviour
{
private GameObject shipObject;
private ShipData playerShipInfo;
public GameObject shipDisplayPoint;
public GameObject nodeImagePrefab; //the prefab that will be instantiated at each available attachment node.
public Module chosenModule;
private void OnEnable()
{
shipObject = GameObject.Find("PlayerShip001");
playerShipInfo = shipObject.GetComponent<Ship>().data;
int numberOfAvailableNodes = playerShipInfo.shipAvailableAttachPoints.GetLength(0);
Debug.Log (numberOfAvailableNodes);
for (int i = 0; i < numberOfAvailableNodes; i++)
{
GameObject nodeDisplay = Instantiate(nodeImagePrefab) as GameObject;
nodeDisplay.transform.position = playerShipInfo.shipAvailableAttachPoints[i].position;
nodeDisplay.transform.SetParent(playerShipInfo.shipAvailableAttachPoints[i], false);
nodeDisplay.GetComponentInChildren<NodeBtnScript>().nodeIndex = i;
nodeDisplay.GetComponentInChildren<NodeBtnScript>().module = chosenModule;
}
Messenger.Broadcast(Broadcasters.ModuleChosenEvent.ToString());
}
}
}
The code on the NodeBtn script is
using System;
using System.Collections;
using Assets._Custom._Scripts._ShipScripts;
using Assets._Custom._Scripts._Helpers;
namespace Assets._Custom._Scripts._GUIScripts
{
public class NodeBtnScript : MonoBehaviour
{
public int nodeIndex; //the index (in the available nodes array) of the node this image is highlighting.
public Module module; //the module we're attaching.
private Button thisBtn;
private void Awake()
{
thisBtn = GetComponent<Button>();
thisBtn.onClick.AddListener(Click);
}
public void Click()
{
/*For some reason, Messenger broadcasting was messing up, so I'll try to fix that later on.*/
Messenger<Module, int>.Broadcast(Broadcasters.ModuleAttachEvent.ToString(), module, nodeIndex);
Debug.Log ("Node chosen");
}
}
}
Any advice and help you could provide would be much appreciated.
Answer by AlwaysSunny · Mar 09, 2015 at 04:25 AM
When you instantiate a button, it may lose the reference upon which any assigned OnClick events depend. (If that reference is part of the prefab which contains the button, I suppose it would remain intact.)
My favorite way around this involves some custom call-backs. My runtime-instantiated buttons have a script which invokes my logic on their mouse-related events and calls a parent managerial script to act upon those events.
Here are the interfaces you can support for buttons:
IPointerEnterHandler,
IPointerExitHandler,
IPointerDownHandler,
IPointerUpHandler,
IPointerClickHandler,
IDragHandler,
IDropHandler,
IScrollHandler,
IUpdateSelectedHandler,
ISelectHandler,
IDeselectHandler,
IMoveHandler
And a samlpe from my button script:
public class UIButtonActual : MonoBehaviour, IPointerEnterHandler
public void OnPointerEnter( PointerEventData ped ) {
// do whatever here, e.g. call a managerial class and pass "this" to know which button was pressed.
// of course there are other ways to handle this situation, but I like this approach myself.
}
So could I make it inherit from IPointerClickHandler, and then use OnPointerClick to execute whatever I need to do when the player clicks the button?
It's not "inheritance," but "implementation". You inherit from a class, but you implement an interface.
And yes, that's exactly correct! There are other ways to achieve this, but I believe knowing this interface approach is very useful.
Your answer
Follow this Question
Related Questions
how can i clone ui button ? 2 Answers
Is there a way to Instantiate a button prefab as a child of a Canvas? 2 Answers
Pressing space calls the wrong function 1 Answer
Changing UI Buttons color 3 Answers
Unity UI Button 1 Answer