通过脚本在 运行 时间实例化按钮正确声明了 onClick 函数

Making Buttons Instantiated at run-time via script have onClick functions declared correctly

在我的程序中,我在 运行 时间实例化按钮。这些按钮被实例化为 canvas。他们应该 引用一个主要对象,一辆汽车。按钮应该指汽车的所有部分。当我点击一个按钮时,我希望相机放大正确的汽车部件。这些按钮具有与汽车零件相同的文本。由于它们是在 运行 时从资产中的按钮预制件实例化的,所以我不知道如何对其进行编码,如果我点击它,它会放大正确的部分。

我的第一个尝试是将一个脚本附加到按钮预制件上,当点击它时它会读取自己的文本(因为它从另一个脚本中的所有子项中获取文本)然后它会读取所有子项文本,并且比较并找到按钮文本与子部分文本相同的位置,然后它会抓住那个子游戏对象,它会引用该游戏对象的变换并将相机变换到该位置。

我可以变换相机和所有东西,因为我已经通过使用光线投射来点击汽车部件来完成此操作,但我只是不知道如何将 onClick 分配给按钮。代码需要可重用,所以我试图将它设计为可以用于任何对象的地方...


Picture of the inspector car and parts Picture of inspector and script that is instantiating the buttons

下面是我解决这个问题的尝试。此脚本附加到按钮预制件...

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ifClickedZoomy : MonoBehaviour
{
    public GameObject objofparts;
    public GameObject cm;

    Button btn;
    string nameofobj;

    void start()
    {


        btn = this.GetComponent<Button>();
        btn.onClick.AddListener(going);
        btn.name = nameofobj;

        var temp = objofparts.gameObject.transform.Find(nameofobj);

        //btn.onClick.AddListener(delegate { Zoomy(temp); });
    }

    public void Zoomy(Transform target)
    {

        cm.transform.position = target.transform.position + Vector3.one * 0.5f;
        cm.transform.LookAt(target);
    }

    void going()
    {
        /*
        if(btn.GetComponentInChildren<Text>().text == "SkyCar")
        {
            GameObject target = GameObject.Find("SkyCar");
            Vector2 targpos = target.transform.position;

            cm.transform.position = targpos;
        }
        */
    }

}

您的按钮预制件可能已将脚本附加到按钮对象。在此脚本中,您将有一个名为 ClickBehaviour() 的 public 方法,该方法预 linked 到 The OnClick。

这个脚本应该有一个 delegate 方法,可以从一个名为 SetClickBehaviour 的方法外部设置。

委托基本上是一个存储具有特定签名的函数的变量。所以委托 void 正在存储一个 void 函数。您可以将委托设置为特定函数。当您像调用真正的函数一样调用委托时,它实际上调用了它存储的函数。通过这种方式,您可以通过更改存储在委托中的函数来交换在运行时实际调用的函数。

因此,您将实例化按钮预制件,然后通过调用 newButton.SetClickBehaviour(methodToCall);

将行为发送给 link

所以 SetClickBehaviour 负责更改委托中存储的内容..

然后在ClickBehaviour 内部,您将调用外部设置的委托方法。

所以 ClickBehaviour 实际上调用了该委托。

按钮生成器

using UnityEngine;

public class ButtonSpawner : MonoBehaviour
{

    // Button Prefab
    [SerializeField] 
    RuntimeButton RuntimeButtonPrefab = default;

    // Where to create instantiated buttons
    [SerializeField] 
    Canvas ParentCanvas = default;

    void Start()
    {
        //Create Buttons
        RuntimeButton newRuntimeButton1 = Instantiate(RuntimeButtonPrefab, ParentCanvas.transform);
        newRuntimeButton1.SetClickBehaviour(MethodThatButton1ShouldDo);

        RuntimeButton newRuntimeButton2 = Instantiate(RuntimeButtonPrefab, ParentCanvas.transform);
        newRuntimeButton2.SetClickBehaviour(MethodThatButton2ShouldDo);
    }

    //These methods could be anything and from any other scripts
    public void MethodThatButton1ShouldDo()
    {
        Debug.Log("Method for Button1 Worked!");
    }

    public void MethodThatButton2ShouldDo()
    {
        Debug.Log("Method for Button2 Worked!");;
    }


}

按钮脚本

using System;
using UnityEngine;

//This class is attached to button
public class RuntimeButton : MonoBehaviour
{
    //An action is just a kind of delegate.
    Action clickBehaviour;

    //This is called from another script to define what method gets called when button is clicked.
    public void SetClickBehaviour(Action someMethodFromSomewhereElse)
    {
        clickBehaviour = someMethodFromSomewhereElse;
    }

    // This is linked in button inspector to the OnClick event.
    public void ClickBehaviour()
    {
        clickBehaviour.Invoke();
    }

} 

在行动:

还有一件事要注意:在你更新的问题中,你的脚本负责几件事:让按钮听,找到一个部分,听点击,移动相机,和 "going"。对于一个脚本来说,这太过分了!将这些想法分解成不同的脚本是个好主意,每个脚本只有一个主要职责。我发现这通常有助于诊断问题,并可以提出可能的解决方案。在我的示例脚本中,RuntimeButton 仅负责捕获 OnClick 并调用其委托。它不关心代表做什么。

ButtonSpawner 只负责生成按钮。在实际程序中,传递给按钮的两个方法肯定会驻留在不同的脚本中,因为它们与生成按钮无关。我只是为了这个最小的例子把它们放在那里。