首页 > 解决方案 > 奇怪,为什么在更改门颜色时会更改所有门而不是每个门的颜色?

问题描述

此脚本使用 ExecuteAlways 附加到空 GameObject,因此它也可以在编辑模式和运行时模式下工作:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[ExecuteAlways]
public class DoorsLockManager : MonoBehaviour
{
    public List<HoriDoorManager> Doors = new List<HoriDoorManager>();
    public List<bool> DoorsLockState = new List<bool>();

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (Doors.Count == 0 || DoorsLockState.Count == 0)
        {
            Doors = FindObjectsOfType<HoriDoorManager>().ToList();
            DoorsLockState = new bool[Doors.Count].ToList();
        }

        for (int i = 0; i < DoorsLockState.Count; i++)
        {
            Doors[i].lockState = DoorsLockState[i];
        }
    }
}

在这个脚本中,这个脚本也附加到每个门上,我正在使用 ExecuteAlways 属性:

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

[ExecuteAlways]
public class HoriDoorManager : MonoBehaviour
{
    public DoorHori door1;
    public DoorHori door2;
    public bool lockState;

    private List<Material> doorsRenderers = new List<Material>();

    private void Start()
    {
        doorsRenderers.Add(door1.transform.GetChild(0).GetComponent<Renderer>().sharedMaterial);
        doorsRenderers.Add(door2.transform.GetChild(0).GetComponent<Renderer>().sharedMaterial);
        doorsRenderers[0].shader = Shader.Find("Unlit/ShieldFX");
        doorsRenderers[1].shader = Shader.Find("Unlit/ShieldFX");
    }

    void OnTriggerEnter()
    {
        if (lockState == false)
        {
            if (door1 != null)
            {
                door1.OpenDoor();
            }

            if (door2 != null)
            {
                door2.OpenDoor();
            }
        }
    }

    private void Update()
    {
        if(lockState == false)
        {
            doorsRenderers[0].SetColor("_MainColor", Color.green);
        }
        else
        {
            doorsRenderers[0].SetColor("_MainColor", Color.red);
        }
    }
}

问题是当我在 List DoorsLockState 的第一个脚本中更改标志的状态时,它不会更改门的颜色,只有一个标志会更改所有门的颜色。

它确实改变了锁定状态,但我希望每扇门也会根据锁定状态改变颜色。锁定 = 红色 解锁 = 绿色。

DoorHori 类:

using UnityEngine;
using System.Collections;

public class DoorHori : MonoBehaviour {

    public float translateValue;
    public float easeTime;
    public OTween.EaseType ease;
    public float waitTime;

    private Vector3 StartlocalPos;
    private Vector3 endlocalPos;

    private void Start(){
        StartlocalPos = transform.localPosition;    
        gameObject.isStatic = false;
    }

    public void OpenDoor(){
        OTween.ValueTo( gameObject,ease,0.0f,-translateValue,easeTime,0.0f,"StartOpen","UpdateOpenDoor","EndOpen");
        GetComponent<AudioSource>().Play();
    }

    private void UpdateOpenDoor(float f){       
        Vector3 pos = transform.TransformDirection( new Vector3( 1,0,0));
        transform.localPosition = StartlocalPos + pos*f;

    }

    private void UpdateCloseDoor(float f){      
        Vector3 pos = transform.TransformDirection( new Vector3( -f,0,0)) ;

        transform.localPosition = endlocalPos-pos;

    }

    private void EndOpen(){
        endlocalPos = transform.localPosition ;
        StartCoroutine( WaitToClose());
    }

    private IEnumerator WaitToClose(){

        yield return new WaitForSeconds(waitTime);
        OTween.ValueTo( gameObject,ease,0.0f,translateValue,easeTime,0.0f,"StartClose","UpdateCloseDoor","EndClose");
        GetComponent<AudioSource>().Play();
    }
}

标签: c#unity3d

解决方案


您正在编辑所有门之间共享的 sharedMaterial

doorsRenderers.Add(door1.transform.GetChild(0).GetComponent<Renderer>().sharedMaterial);
//Notice the ".shadredMaterial" at the end here

而不是材质的实例。

通过使用doorsRenderers[0].shader = Shader.Find("Unlit/ShieldFX");,您还可以直接访问着色器,这意味着对其所做的任何更改都将反映在该材质的每个实例上。

doorsRenderers[0].SetColor("_MainColor", Color.green);还直接访问材质和底层着色器,而不是材质实例(默认情况下,Unity 将在运行时编辑材质时创建材质实例)

有两种方法可以解决此问题:

  • 使用两种不同的材料(一种红色,一种绿色)并将材料换成所需的材料。
  • 获取 GameObjects 的 Renderer 而不是(共享)材质,并改为使用Renderer.material.color属性应用颜色更改。这将创建一个材质实例,仅更改该实例的颜色。

解决方案二看起来像这样:

private List<Renderer> doorsRenderers = new List<Renderer>();

private void Start()
{
    doorsRenderers.Add(door1.transform.GetChild(0).GetComponent<Renderer>());
    doorsRenderers.Add(door2.transform.GetChild(0).GetComponent<Renderer>());
}

private void ChangeDoorColour()
{
    if(lockState == false)
    {
        doorsRenderers[0].material.color = Color.green;
    }
    else
    {
       doorsRenderers[0].material.color = Color.red;
    }
}

顺便说一句:您通过在更新循环中设置门的每一帧的材质颜色,这不是一件好事,只能在门的状态发生变化时进行。所以我把它放在一个单独的函数ChangeDoorColour()中,只有在门的状态发生变化时才应该调用它(所以可能在OpenDoor()andCloseDoor()方法中)。


推荐阅读