Material Colour Change, Loop

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

public class WhiteSheep : MonoBehaviour
{
public GameObject sheep;
public GameObject trees;

void Start()
{
    int colour = getRandomInt(0, 2);

    if (colour == 1)
    {
        Renderer rend = GetComponent<Renderer>();
        //so we can access the colour

        rend.material.shader = Shader.Find("_Color");
        rend.material.SetColor("_Color", Color.red);

        rend.material.shader = Shader.Find("Specular");
        rend.material.SetColor("_SpecColor", Color.red);
        //Find the Specular shader and change its Color to red
    }

    else if (colour == 0)
    {
        Renderer rend = GetComponent<Renderer>();
        //so we can access the colour

        rend.material.shader = Shader.Find("_Color");
        rend.material.SetColor("_Color", Color.blue);

        rend.material.shader = Shader.Find("Specular");
        rend.material.SetColor("_SpecColor", Color.blue);
    }
}

private int getRandomInt(int start, int end)
{
    float fRandom = Random.Range(start, end);
    int iRandom = Mathf.FloorToInt(fRandom);
    return iRandom;
}

private void OnCollisionEnter(Collision collision)
{
    if (collision.collider.CompareTag ("Player"))
    {

        foreach (GameObject tree in trees)
        {
            Renderer rend = tree.GetComponent<Renderer>();

            rend.material.shader = Shader.Find("_Color");
            rend.material.SetColor("_Color", Color.magenta);

            rend.material.shader = Shader.Find("Specular");
            rend.material.SetColor("_SpecColor", Color.magenta);
        }
    }
}

}

I want to make the colour change to be repeated by using a loop, but when I add a loop into the void Start, it happens too quick that I can’t observe it happening. Is there any way slowing down the loop in Unity?

3 solutions :

  1. Make Start a Coroutine
  2. Make Start call InvokeRepeating
  3. Use Update to increment a timer, using Time.deltaTime

Some suggestions :

  1. Keep a reference to the Renderer, don’t use GetComponent each time
  2. Shader.Find returns a Shader to pass a Material constructor, not a property, not sure what you intend to do with Shader.Find("_Color");
  3. Accessing a Renderer’s material returns a copy, thus creating a new instance. It’s better if you can replace the material with a reference you already have.
  4. Random.Range already has an int version, no need to floor the result.

You can go with something in the lines of :

public class MaterialSwitch : MonoBehaviour
{
    Material[] materials = new Material[2];
    Renderer _renderer;

    void Awake ()
    {
        _renderer = GetComponent<Renderer>();
    }

    IEnumerator Start ()
    {
        WaitForSeconds wait = new WaitForSeconds(1f);
        while (true)
        {
            yield return wait;
            _renderer.material = materials[Random.Range(0,2)];
        }
    }
}

The code in Start will be executed during a single frame, that’s why you can’t see anything.

What you want to do is use a Coroutine. Create a coroutine, put your loop inside and in each iteration of your loop write a ‘yield return’. Note that you can’t coroutines like normal methods, you have to use StartCoroutine instead.

Example code:

private void Start()
{
	StartCoroutine(ChangeColors());
}

private IEnumerator ChangeColors()
{
	foreach(Color c in colors)
	{
		tree.color = c;
		yield return new WaitForSeconds(1);
	}
}