Search Unity

Change model's material in a loop

Discussion in 'Scripting' started by Joseph_, Nov 28, 2015.

  1. Joseph_

    Joseph_

    Joined:
    Nov 28, 2015
    Posts:
    7
    Hello,

    I am trying to create a sequence of lights in C#. I've been doing a lot of research but I've hit a wall trying to create a logical representation of this (learning C#).

    My objective is for a model's material to be swapped from one to another through a loop of 90 models until the user hits mouse button to stop the sequence.

    I apologize as my code might look like garbage, right now I have set up a coroutine to toggle one material, but I cannot understand how to toggle a material that is used by an external model. This script is attached to the first model.

    Also, to be clear, there are no real lights that need to be swapped, simply a material changing from one to another to create a sequence of lights effect.

    Thanks a lot for any help!



    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class gameplay : MonoBehaviour
    5. {
    6.  
    7.     public Material materialGlass;
    8.     public Material materialGlowDefault;
    9.     public GameObject modelLight01;
    10.     public GameObject modelLight02;
    11.     public bool sequenceActive = false;
    12.  
    13.     void Start()
    14.     {
    15.         Time.timeScale = 1f;
    16.         Application.targetFrameRate = 60;
    17.     }
    18.  
    19.     void Update()
    20.     {
    21.         if(Input.GetMouseButton(0))
    22.         {
    23.             if(sequenceActive == false)
    24.                {
    25.                 StartCoroutine(LightSequence());
    26.             }
    27.             else
    28.             {
    29.                 sequenceActive = false;
    30.             }
    31.         }
    32.         //Cursor.visible = false;
    33.         //Screen.lockCursor = true;
    34.     }
    35.  
    36.     IEnumerator LightSequence()
    37.     {
    38.         sequenceActive = true;
    39.         GetComponent<Renderer>().material = materialGlowDefault;
    40.         yield return new WaitForSeconds(0.1f);
    41.         GetComponent<Renderer>().material = materialGlass;
    42.         sequenceActive = false;
    43.     }
    44.     //end wait
    45. }
     
  2. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    Not tested, so there may be some errors in it, but this is the first approach I would think of:

    Code (CSharp):
    1. public Material materialDefault; // Glass
    2. public Material materialGlowing;
    3. public GameObject[] lightModels;
    4. bool sequenceRunning = false;
    5.  
    6. void OnMouseDown () {
    7.     if (!sequenceRunning) {sequenceRunning = true;}
    8.     else {sequenceRunning = false;}
    9. }
    10.  
    11. void Start () {
    12.     StartCoroutine(LightSequence());
    13. }
    14.  
    15. float animationSpeed = 0.1f;
    16. int currentModel = 0;
    17. IEnumerator LightSequence () {
    18.     while (sequenceRunning) {
    19.         lightModels[currentModel].GetComponent<Renderer>().material = materialDefault;
    20.         lightModels[currentModel+1].GetComponent<Renderer>().material = materialGlowing;
    21.         currentModel++;
    22.         yield return new WaitForSecond(animationSpeed);
    23.         if (currentModel == lightModels.length) {currentModel = 0;}
    24.     }
    25.     StartCoroutine(LightSequence());
    26. }
    27.  
    EDIT:
    Edited the line
    Code (CSharp):
    1. lightModels[currentModel+1].GetComponent<Renderer>().material = materialGlowing;
     
    Joseph_ likes this.
  3. Joseph_

    Joseph_

    Joined:
    Nov 28, 2015
    Posts:
    7
    Thanks for your reply! I'll give that a shot.
     
    Shushustorm likes this.
  4. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    Also, depending on the platform you are targeting, you might want to create one material that can be changed so that both states, "Glass" and "GlowDefault", can be represented. Then, you would only have to change some value(s) of the material rather than changing the material all the time, which may be somewhat costly performance-wise.
     
    Joseph_ likes this.
  5. Joseph_

    Joseph_

    Joined:
    Nov 28, 2015
    Posts:
    7
    Thanks for that tip as well.

    I tried out your solution and I am receiving a StackOverflowException. I played around with it and I was able to get the materials to activate on their respective models on Start (without animation), but the coroutine seems to be the issue.

    I played with the coroutine but I am quite new to this and even by researching solutions it is still a lot of trial and error for me. I'll continue working on it but thanks again for your help!
     
  6. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    I see. Maybe you should add that line before starting the coroutine again:

    Code (CSharp):
    1. IEnumerator LightSequence () { []
    2.     yield return null; // << new line
    3.     StartCoroutine(LightSequence());
    4. }
    5.  
    Because in the script that I posted, as long as sequenceRunning = false, the coroutine will immediately jump back and repeat itself, which is probably a bad idea. "yield return null" will make LightSequence wait for the next frame.
    If it won't work then either, I may be actually testing the script when I got some time to do so.
     
    Joseph_ likes this.
  7. Joseph_

    Joseph_

    Joined:
    Nov 28, 2015
    Posts:
    7
    Hey Shushustorm wanted to say thanks a lot for your help! I was unable to get the while loop to work but I ended up trying a slightly different approach and was able to get it to work. Probably not as efficient though.

    There's a wait for MouseDown in Update, which kicks off the coroutine.

    Code (CSharp):
    1.  float animationSpeed = 0.0001f;
    2.     public int currentModel = 0;
    3.     IEnumerator LightSequence()
    4.     {
    5.         Debug.Log("While");
    6.         //while (sequenceRunning)
    7.         {
    8.             Debug.Log("Sequence Running");
    9.             lightModels[currentModel].GetComponent<Renderer>().material = materialDefault;
    10.             lightModels[currentModel + 1].GetComponent<Renderer>().material = materialGlowing;
    11.             currentModel++;
    12.             yield return new WaitForSeconds(animationSpeed);
    13.  
    14.             if (currentModel == 89)
    15.             {
    16.                 lightModels[currentModel].GetComponent<Renderer>().material = materialDefault;
    17.                 currentModel = 0;
    18.              
    19.              
    20.                 lightModels[currentModel].GetComponent<Renderer>().material = materialGlowing;
    21.                 yield return new WaitForSeconds(animationSpeed);
    22.             }
    23.         }
    24.         if (sequenceRunning == true)
    25.         {
    26.             StartCoroutine(LightSequence());
    27.         }
    28.     }
    Thanks again Shushustrom :)
     
    Shushustorm likes this.
  8. Shushustorm

    Shushustorm

    Joined:
    Jan 6, 2014
    Posts:
    1,084
    @Joseph_
    I'm glad I could help! :D
    Also, your solution doesn't seem very inefficient! At least not that I know of!
     
    Joseph_ likes this.