(Unity)创建一个按钮,为其指定一个委托(或Action),并为该委托提供一个引用参数

时间:2017-03-03 11:05:04

标签: c# unity3d parameters reference delegates

我创建了一堆静态函数来帮助我在需要时设置UI元素,例如初始化构建单元菜单或资源图(这是用于策略游戏)。我一直在使用委托和动作来允许我按下按钮初始化函数来订阅按钮的onClick事件。到目前为止,这已经很好了。

但是,当我有一个可以更改其文本值的文本字段时 - 例如,玩家想要购买多少项的输入数字 - 代表不会参考。因此,如果我打开菜单,菜单的输入字段将为0.0,并调用函数Buy(0.0f)。但是,如果我将输入字段中的值更改为3.0,则该按钮仍会调用Buy(0.0f),因为这是创建它时的值。

奇怪的是,直到最近我才遇到这个问题。看,我的普通按钮与输入字段预制件有一个按钮,一个输入字段与该按钮联系。据我所知,这意味着Button可以处理Field的文本作为参考,因为它总是知道输入字段的位置。这是创建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;
 }

这很有效。如果我更改输入字段中的值,该按钮将使用更新的值调用该函数。

但是我也使用了一大堆引用ONE输入字段的按钮,因为我想让玩家购买很多不同的东西,但只需要指定一个数量。这显然是两个预制件:一个Button和一个InputField。所以我用InitButton()函数完成了同样的事情:

此过载包含一个具有两个参数的函数:交易商品和交易数量。

 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;
 }

我尝试过很多东西。使用'ref'似乎不是一个选项,因为匿名函数,例如我正在处理onClick的函数不能使用refs。我目前正在给按钮提供一个自定义脚本来存储我需要的所有数据,但我希望这将是凌乱的,而且非常通用。

这似乎是一件很常见的事情。还有更好的方法吗?

1 个答案:

答案 0 :(得分:1)

自己解决了。原来我是非常愚蠢的。

问题实际上是Menu类以及它是如何用按钮填充的。

 // 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 = MenuElements.InitField(transform,
                 new Vector3(205.0f, -40.0f));

             // Call the Button initialiser.
             GameObject buyButton = MenuElements.TestInitButton(transform,
                 new Vector3(205.0f, -60.0f - 30.0f * g),
                 "Buy",
                 market.BuyFrom,
                 SGoods.Instance.goodsList[g],
                 buyQuantity.GetComponent<InputField>());
         }

我在for循环中放置了buyQuantity输入字段的变量,即使我只想创建其中一个。然后我只是用for循环的每个循环覆盖它。出于这个原因,所有按钮都忽略了对输入字段的更改,除了最后一个...

奇怪的是,重新输入此代码后,我现在收到一条错误警告我这件事。我很确定这就是我以前的方式,而且没有错误。

无论如何,我猜一旦Unity有一个对象SOMEWHERE的引用,它可以将它作为引用类型。我刚刚在for循环之外声明了引用:

 GameObject buyQuantity = MenuElements.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 = MenuElements.TestInitButton(transform,
         new Vector3(205.0f, -60.0f - 30.0f * g),
         "Buy",
         market.BuyFrom,
         SGoods.Instance.goodsList[g],
         buyQuantity.GetComponent<InputField>());
 }

最终,这并不需要任何花哨的东西。没有ref关键字,没有疯狂的lambda函数。只是一个普通的匿名代表工作正常。由Menu类保存的InputField似乎已经足够了。

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;
 }