Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Thomas-Slade · Mar 05, 2017 at 03:28 PM · c#buttonreferenceinputfielddelegate

Creating a button, giving it a delegate (or Action), and giving that delegate a reference parameter

Hi. I've created a bunch of static functions to help me set up UI elements when I need them, things like initialising a build-units menu or a resources graph (this is for a strategy game). I've been using delegates and Actions to allow me to hand the button initialiser a function to subscribe to the button's onClick event. So far, this has worked fine. However, when I have a text field that can change its text value - for example, an input number for how many items the player wants to buy - the delegate doesn't take a reference. So if I open a menu, the menu's input field will be 0.0, and call the function Buy(0.0f). But if I change the value in the input field to 3.0, the button still calls Buy(0.0f), because that's what the value was when it was created.

Strangely, though, I wasn't having this problem until recently. See, my normal Button-with-an-Input-Field prefab has a Button, and an Input Field childed to that Button. From what I can tell, this means the Button can handle the Field's text as a reference, since it always knows where the Input Field is. Here's the function for creating a FieldButton:

 // Function to quickly set up a numeric field button.
     // This accomodates a function with a single float parameter.
     public delegate void floatDel(float f);
     public static GameObject InitFieldButton(Transform parent, Vector3 position, string text, floatDel function)
     {
         GameObject newElement = GameObject.Instantiate(fieldButton);
         // Establish UI transform data.
         newElement.transform.SetParent(parent);
         newElement.transform.localPosition = position;
         newElement.transform.GetChild(0).GetComponent<Text>().text = text;
 
         // Assign the argued function to the button, with the input text as a parameter to that function.
         newElement.GetComponent<Button>().onClick.AddListener(delegate { function(float.Parse(newElement.GetComponentInChildren<InputField>().text)); });
         return newElement;
     }

This works. If I change the value in my input field, the button will call the function with the updated value.

But I'm also using a whole bunch of buttons that reference ONE input field, for when I want to let the player buy lots of different things, but only need to specify one quantity. This is obviously two prefabs: a Button, and an InputField. So I'm doing exactly the same thing with my InitButton() function:

  // This overload accomodates a function with two parameters: a trading good, and a quantity to be traded.
     public delegate void purchaseDel(Good g, float f);
     public static GameObject InitButton(Transform parent, Vector3 position, string text, purchaseDel function, Good good, GameObject inputField)
     {
         GameObject newElement = GameObject.Instantiate(button);
         // Establish UI transform data.
         newElement.transform.SetParent(parent);
         newElement.transform.localPosition = position;
         newElement.GetComponentInChildren<Text>().text = text;
 
         // Assign the function delegated to this button.
         newElement.GetComponent<Button>().onClick.AddListener(delegate { function(good, float.Parse(inputField.GetComponent<InputField>().text)); });
         return newElement;
     }

I've tried quite a few things. Using 'ref' doesn't seem to be an option, because anonymous functions such as the one I'm handing to onClick can't take refs. I'm currently looking at giving the button a custom script to store all the data I need, but I expect this will be messy and really non-generic.

This seems like a pretty common thing to want to do. Is there a better way?

Comment
Add comment · Show 1
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Thomas-Slade · Mar 03, 2017 at 11:26 AM 0
Share

Solved it myself. Turned out I was being immensely stupid.

The issue was actually the $$anonymous$$enu class and how it was populating itself with buttons.

 // Create a buy button for every type of good that can be bought.
 for (int g = 0; g < SGoods.Instance.goodsList.Count - 1; g++)
         {
             // Get the text input for how much the player wants to buy.
             GameObject buyQuantity = $$anonymous$$enuElements.InitField(transform,
                 new Vector3(205.0f, -40.0f));
 
             // Call the Button initialiser.
             GameObject buyButton = $$anonymous$$enuElements.TestInitButton(transform,
                 new Vector3(205.0f, -60.0f - 30.0f * g),
                 "Buy",
                 market.BuyFrom,
                 SGoods.Instance.goodsList[g],
                 buyQuantity.GetComponent<InputField>());
         }

I placed the variable for the buyQuantity input field inside the for loop, even though I only wanted to create one of it. Then I was just overwriting it with every loop of the for-loop. For this reason, all buttons ignored changes to the input field, except the last one ...

Strangely, after re-typing this code, I now get an error warning me about exactly this. I'm pretty sure this is how I had it before, and there were no errors.

Anyway, I guess once Unity has a reference of the object SO$$anonymous$$EWHERE, it can keep it as a reference type. I just declared that reference outside the for loop:

 GameObject buyQuantity = $$anonymous$$enuElements.InitField(transform,
                 new Vector3(205.0f, -40.0f));
 
         for (int g = 0; g < SGoods.Instance.goodsList.Count - 1; g++)
         {
             // Call the Button initialiser.
             GameObject buyButton = $$anonymous$$enuElements.TestInitButton(transform,
                 new Vector3(205.0f, -60.0f - 30.0f * g),
                 "Buy",
                 market.BuyFrom,
                 SGoods.Instance.goodsList[g],
                 buyQuantity.GetComponent<InputField>());
         }

Ultimately this doesn't need anything fancy. No ref keywords, no crazy lambda functions. Just a regular anonymous delegate works fine. The InputField being held by the $$anonymous$$enu class seems to be sufficient.

 public static GameObject TestInitButton(Transform parent, Vector3 position, string text, purchaseDel function, Good good, InputField inputField)
     {
         GameObject newElement = GameObject.Instantiate(button);
         newElement.transform.SetParent(parent);
         newElement.transform.localPosition = position;
         newElement.GetComponentInChildren<Text>().text = text;
 
         // Assign the function delegated to this button.
         newElement.GetComponent<Button>().onClick.AddListener(() => function.Invoke(good, float.Parse(inputField.text)));
         return newElement;
     }

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

6 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Button Listener Takes Reference But I Want to Pass Value? 1 Answer

Button onClick delegate with toggling method assignment 0 Answers

Problem with onClick.AddListener 1 Answer

Multiple Cars not working 1 Answer

Distribute terrain in zones 3 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges