【问题标题】:Unity Changing material color on multiple gameobjectsUnity 在多个游戏对象上更改材质颜色
【发布时间】:2021-10-30 11:08:57
【问题描述】:

我创建了一个右键菜单,当我将鼠标放在该菜单中的按钮上时,我想让对象改变材质颜色。

这是代码:

Color[] startCo;

public void OnPointerEnter(PointerEventData eventData)
{
    GameObject[] objects = GameObject.FindGameObjectsWithTag(myMenu.selected.title);

    for (int i = 0; i < startCo.Length; i++)
    {
        startCo[i] = objects[i].gameObject.GetComponent<MeshRenderer>().material.color;
    }

    foreach (GameObject obj in objects)
    {
        obj.gameObject.GetComponent<MeshRenderer>().material.color = Color.red;
    }
}

public void OnPointerExit(PointerEventData eventData)
{
    GameObject[] objects = GameObject.FindGameObjectsWithTag(myMenu.selected.title);

    for (int i = 0; i < objects.Length; i++)
    {
        objects[i].gameObject.GetComponent<MeshRenderer>().material.color = startCo[i];
    }
}

使用第一个 for 循环它根本不起作用,但是没有它,当我将鼠标放在按钮上时,它会使材质颜色变为红色,但不会将其改回原始颜色。

我的问题是,有没有更好的方法来使用 foreach 来保存原始颜色?

【问题讨论】:

  • 你说“根本不起作用”是什么意思。它是否显示构建错误?乍一看,我认为objects[i].gameObject 确实没有意义。这甚至可以编译吗? objects[i] 已经是游戏对象,所以 objects[i].GetComponent&lt;MeshRenderer&gt; 应该可以正常工作。
  • @ThomasHilbert,“根本不起作用”,我的意思是它甚至不会将材质颜色更改为红色(不使用它,它会改变它)。 “objects[i].gameObject” 以两种方式编译,我刚刚检查过了。但我不知道如何将材质颜色更改为原始颜色。
  • 如果是这种情况,您的 for 循环中最有可能出现错误 null reference error
  • 第一次调用 OnPointerEnter 时,startCo 为 null 或 startCo.Length 为零。
  • 您可能希望在您的OnPointerEnter 处理程序中将for (int i = 0; i &lt; startCo.Length; i++) 更改为for (int i = 0; i &lt; objects.Length; i++)。但是,您需要先分配数组。 (我在 C++ 领域已经有一段时间了;不确定这里的 C# 机制。)

标签: c# unity3d


【解决方案1】:

试试这个版本。它基本上与您的版本完全相同,但请确保在使用之前初始化颜色数组(这可能是您的主要问题)。它还保留颜色被替换的对象列表的副本,这对于创建具有匹配标签的新对象或删除现有对象很重要。最后,它添加了一些保护措施,使其更加健壮,以防您也想在其他地方使用 ReplaceColors()RestoreColors()

GameObject[] objectsWithReplacedColors;
Color[] originalColors;

public void OnPointerEnter(PointerEventData eventData)
{
    ReplaceColors(GameObject.FindGameObjectsWithTag(myMenu.selected.title), Color.red);
}

public void OnPointerExit(PointerEventData eventData)
{
    RestoreColors();
}

private void ReplaceColors(GameObject[] forObjects, Color withColor)
{
    if (objectsWithReplacedColors != null)    // if there are already objects with replaced colors, we have to restore those first, or their original color would be lost
        RestoreColors();

    objectsWithReplacedColors = forObjects;
    originalColors = new Color[objectsWithReplacedColors.Length];

    for (int i = 0; i < objectsWithReplacedColors.Length; i++)
    {
        originalColors[i] = objects[i].GetComponent<MeshRenderer>().material.color;
        objectsWithReplacedColors[i].GetComponent<MeshRenderer>().material.color = withColor;
    }
}

private void RestoreColors()
{
    if (objectsWithReplacedColors == null)
        return;

    for (int i = 0; i < objectsWithReplacedColors.Length; i++)
    {
        if (objectsWithReplacedColors[i])    // check if the objects still exists (it may have been deleted since its color was replaced)
            objectsWithReplacedColors[i].GetComponent<MeshRenderer>().material.color = originalColors[i];
    }

    objectsWithReplacedColors = null;
    originalColors = null;
}

【讨论】:

    【解决方案2】:

    我的猜测是,每次使用GameObject.FindGameObjectsWithTag 调用这些方法时,您都会找到对象,而且我很确定FindGameObjectsWithTag 返回的这些对象的顺序未指定,因此每次调用此方法时它都会更改。这个问题最终会为您提供不同对象的不同原始颜色。我的建议是在Startonce 中获取objects 并为数组分配颜色。然后在OnPointerExitfunction 中使用这个数组。

    除此之外,您的代码和逻辑对我来说似乎还不错。

    【讨论】:

    • Kanet,你在 Start 中获取对象是什么意思?我的意思是,我明白你在说什么,但是当我单击鼠标时,会显示带有三个按钮的菜单(每个按钮用于另一个对象)。这个“myMenu.selected”意味着当我悬停一个按钮时,选择了一个“类”对象,当我悬停另一个按钮时,选择了另一个“类”对象等等。
    • 是的,但这并不重要,因为您可能对不同的“类”对象有不同的标签。你在这里做的也是不好的做法,会给你带来问题。 myMenu.selected.title 应该处理您正在谈论的那个问题。但是对于 1 个按钮和该按钮的对象,您最初知道要突出显示哪些对象,因此无需在每次悬停该按钮时更新 objects
    猜你喜欢
    • 1970-01-01
    • 2022-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-09
    • 1970-01-01
    • 2022-11-24
    相关资源
    最近更新 更多