【问题标题】:Unexplained increasement of variable无法解释的变量增加
【发布时间】:2016-09-24 10:21:08
【问题描述】:

在我用来更改按钮设置的循环之一中,我还使用了 AddListener 函数,而不是 Inspector 中的那个。我有 5 个项目,给出从 0 到 4 的“i”范围,但是当我通过它应该调用的函数打印“i”时,它总是记录 5,无论我按下什么按钮,这很奇怪,因为“i”甚至从未达到 5。有什么想法吗?

附:我使用 CustomEditor 在检查器中显示 2 个按钮“预览布局”和“删除预览”。

代码:

using UnityEngine;
using System.Collections;
using UnityEditor;
using UnityEngine.UI;

public class RateMeManager : MonoBehaviour {

    public GameObject rateMeCanvas;
    public Sprite emptyStar, fullStar, button;
    public float spriteWidth, spriteHeight, spritePadding;

    [HideInInspector]
    public GameObject currentCanvas, tempButton;

    void Start () {
        RemovePreview();
        GenerateStars();
    }

    // Update is called once per frame
    public void GenerateStars () {
        RectTransform myRectTransform;
        if (currentCanvas != null)
        {
            GameObject temp;
            temp = currentCanvas;
            DestroyImmediate(temp);
        }
        currentCanvas = Instantiate(rateMeCanvas, Vector3.zero, Quaternion.identity) as GameObject;
        GameObject subCanvas = currentCanvas.transform.FindChild("subCanvas").gameObject;
        myRectTransform = subCanvas.GetComponent<RectTransform>();
        myRectTransform.sizeDelta = new Vector2((5*spriteWidth) + (4*spritePadding), spriteHeight);
        myRectTransform.anchoredPosition = Vector2.zero;
        Button[] buttons = subCanvas.GetComponentsInChildren<Button>();
        float[] positions = new float[] {((2*spriteWidth)+(2*spritePadding))*-1, ((1 * spriteWidth) + (1 * spritePadding)) * -1 , 0, ((1 * spriteWidth) + (1 * spritePadding)), ((2 * spriteWidth) + (2 * spritePadding))};
        for (int i = 0; i < buttons.Length; i++)
        {
            Debug.Log(i);
            tempButton = buttons[i].gameObject;
            tempButton.GetComponent<Button>().image.sprite = emptyStar;
            myRectTransform = buttons[i].GetComponent<RectTransform>();
            myRectTransform.sizeDelta = new Vector2(spriteWidth, spriteHeight);
            myRectTransform.anchoredPosition = new Vector2(positions[i], 0);
            tempButton.GetComponent<Button>().onClick.AddListener(() => OnGivenRate(i));
        }
    }

    public void RemovePreview()
    {
        DestroyImmediate(currentCanvas);
    }

    private void OnGivenRate(int stars)
    {
        Debug.Log("pressed star: " + stars);
    }

    public class RateMeEditor
    {
        [CustomEditor(typeof(RateMeManager))]
        public class button : Editor
        {
            public override void OnInspectorGUI()
            {
                base.OnInspectorGUI();

                RateMeManager myScript = (RateMeManager)target;
                if (GUILayout.Button("Preview Layout"))
                {
                    myScript.GenerateStars();
                }
                if (GUILayout.Button("Delete Preview"))
                {
                    myScript.RemovePreview();
                }
            }
        }
    }
}

【问题讨论】:

  • 请调试并检查您的buttons.Length是否为5。
  • 喜欢“增加”这个词
  • @JoeBlow 直到你说出来才注意到这一点。不知道为什么,但我觉得这很有趣
  • 对,很漂亮!
  • 我是一名游戏开发者,我必须要有创意 :p 无论如何,问题解决了。谢谢大家的建议!

标签: c# unity3d custom-controls


【解决方案1】:

你的错误在这里:

tempButton.GetComponent<Button>().onClick.AddListener(() => OnGivenRate(i));

在将i 传递给 OnGivenRate 或使用闭包之前,您必须将其存储在变量中。 因为在循环结束时i 等于 5。这就是为什么当您单击按钮时 i 显示 5。

这样做:

var rate = i;
tempButton.GetComponent<Button>().onClick.AddListener(() => OnGivenRate(rate));

Action<int> OnGivenRateClosure(int rate)
{
    return () => OnGivenRate(rate);
}

tempButton.GetComponent<Button>().onClick.AddListener(OnGivenRateClosure(i));

【讨论】:

  • 谢谢!就像我对其他建议的评论一样,它在使用 Java 的 Android Studio 中以这种方式工作,所以这就是为什么我没有让它在这里工作(也许这是他们故意放在 Android Studio 中的一些东西?)无论如何,只添加这个循环,现在一切正常! int rate = i; tempButton.GetComponent&lt;Button&gt;().onClick.AddListener(() =&gt; OnGivenRate(rate));
  • 很高兴能帮到你:)
  • 这里有点话题,但是如何在这样的评论中添加空白行?我无法很好地格式化代码哈哈...
  • 我不认为你可以 =/ 我也很新,所以我对它的工作原理一无所知,抱歉 ^^"
  • 别担心!享受你的声望点,呵呵,你赚到了 =)
【解决方案2】:

您在for-loop 上引用了i

tempButton.GetComponent().onClick.AddListener(() => OnGivenRate(i));

你使用匿名方法调用OnGivenRate(i)。这段代码将位于您的for-loop 范围之外,但它可以访问i 变量。当变量被() =&gt; OnGivenRate(i)匿名方法引用时,很可能有i=5(当for循环退出时)。

【讨论】:

  • 有道理,谢谢!出于某种原因,在 Android Studio(使用 Java)中,它的工作方式与我在 OP 中的工作方式相同。每天都学到新东西 =)
  • Java 可能处理与 C# 不同的闭包。这将是一个有趣的问题。 ;)
【解决方案3】:

您正在访问一个闭包,因此 for 循环内的被调用函数仅在一段时间后才获得 i 值,此时循环处于另一个循环中并且 i 已被修改。

另一件事。当你写:

for(int i = 0; i < 5; i++)

代码执行for循环内的语句,i的值为0、1、2、3、4,,但i的最后一个值为5 。事实上,当 4 的循环结束时,i 递增,检查“i &lt; 5”,结果为假,因此循环退出。

【讨论】:

  • 查看this post,类似。
  • 谢谢,这是有道理的,但是它在 Android Studio 中是这样工作的,因此它在这里不起作用。很好的解释!
【解决方案4】:

您的代码是多余的。在 for 循环之前,您已经将按钮作为数组,然后将其转换为 gameObject(tempButton),然后再次转换为按钮......这看起来像一个参考问题。只需用下面的代码替换你的 for 循环。

要显示 0 到 4:

for (int i = 0; i < buttons.Length; i++)
{
    buttons[i].image.sprite = emptyStar;
    myRectTransform = buttons[i].GetComponent<RectTransform>();
    myRectTransform.sizeDelta = new Vector2(spriteWidth, spriteHeight);
    myRectTransform.anchoredPosition = new Vector2(positions[i], 0);
    buttons[i].onClick.AddListener(() => OnGivenRate(i));
}

要显示 1 到 5:

for (int i = 0; i < buttons.Length; i++)
{
    buttons[i].image.sprite = emptyStar;
    myRectTransform = buttons[i].GetComponent<RectTransform>();
    myRectTransform.sizeDelta = new Vector2(spriteWidth, spriteHeight);
    myRectTransform.anchoredPosition = new Vector2(positions[i], 0);
    buttons[i].onClick.AddListener(() => OnGivenRate(i+1));
}

注意区别:buttons[i].onClick.AddListener(() => OnGivenRate(i+1));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-07
    • 1970-01-01
    相关资源
    最近更新 更多