无法解释的变量增加
Unexplained increasement of variable
在我用来更改按钮设置的循环之一中,我还使用了 AddListener 函数,而不是检查器中的函数。我有 5 个项目,范围为 0-4 "i",但是当我通过它应该调用的函数打印 "i" 时,无论我按什么按钮,它总是记录 5,这很奇怪因为 "i" 从来没有达到 5。有什么想法吗?
P.s。我使用 CustomEditor 在检查器中显示 2 个按钮 "Preview Layout" 和 "Delete Preview"。
代码:
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();
}
}
}
}
}
您正在访问一个闭包,因此 for
循环中的被调用函数仅在一段时间后,当循环处于另一个循环中并且 i
时才获得 i
值已经修改。
另一件事。当你写:
for(int i = 0; i < 5; i++)
代码执行 for
循环内的语句,i
值为 0、1、2、3、4, 但最后一个值i
的 5。事实上,当 4 的循环结束时,i
递增,检查“i < 5
”,结果为 false,因此循环退出。
您在 for
循环中引用了 i
:
tempButton.GetComponent().onClick.AddListener(() => OnGivenRate(i));
并且您使用匿名方法调用OnGivenRate(i)
。这段代码将存在于 for
循环的范围之外,但它可以访问 i
变量。当 () => OnGivenRate(i)
匿名方法引用该变量时,它很可能具有 i=5
(当退出 for
循环时)。
你的错误在这里:
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));
您的代码是多余的。在 for 循环之前,您已经将按钮作为数组,然后将其转换为游戏对象(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) );
在我用来更改按钮设置的循环之一中,我还使用了 AddListener 函数,而不是检查器中的函数。我有 5 个项目,范围为 0-4 "i",但是当我通过它应该调用的函数打印 "i" 时,无论我按什么按钮,它总是记录 5,这很奇怪因为 "i" 从来没有达到 5。有什么想法吗?
P.s。我使用 CustomEditor 在检查器中显示 2 个按钮 "Preview Layout" 和 "Delete Preview"。
代码:
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();
}
}
}
}
}
您正在访问一个闭包,因此 for
循环中的被调用函数仅在一段时间后,当循环处于另一个循环中并且 i
时才获得 i
值已经修改。
另一件事。当你写:
for(int i = 0; i < 5; i++)
代码执行 for
循环内的语句,i
值为 0、1、2、3、4, 但最后一个值i
的 5。事实上,当 4 的循环结束时,i
递增,检查“i < 5
”,结果为 false,因此循环退出。
您在 for
循环中引用了 i
:
tempButton.GetComponent().onClick.AddListener(() => OnGivenRate(i));
并且您使用匿名方法调用OnGivenRate(i)
。这段代码将存在于 for
循环的范围之外,但它可以访问 i
变量。当 () => OnGivenRate(i)
匿名方法引用该变量时,它很可能具有 i=5
(当退出 for
循环时)。
你的错误在这里:
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));
您的代码是多余的。在 for 循环之前,您已经将按钮作为数组,然后将其转换为游戏对象(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) );