Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Switching between two textures using a switch, multiple parts of a model.

Discussion in 'General Graphics' started by ErisC, May 5, 2016.

  1. ErisC

    ErisC

    Joined:
    May 5, 2016
    Posts:
    6
    Trying to switch between a regular texture and the default-material texture using a switch or toggle. Been trying to figure it out all day and I'm stumped.

    The biggest problem is that I don't know how to select the different parts of the models that need to be changed. For example, a model (parent) of a person but the parts that can only be changed are legs, arms, head, and body (children), since the model is connected to the animation.

    Can anyone help me with this?
     
  2. cblarsen

    cblarsen

    Joined:
    Mar 10, 2007
    Posts:
    266
    I am a little confused if you are simply having trouble using the Unity Editor, or if you are trying to write a script that does what you want in play-mode.

    If you are in edit mode:
    The default material cannot actually have its texture changed. To be able to do that, you have to make a new material, and then you can change its texture. A simple way to do that is to drag a texture onto an object with the default material on. A new material named after the texture will be created automatically, and used on the object.

    If you are trying to script it:
    Create a new c# script called TextureSwitcher, and Copy/Paste this to it:
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class TextureSwitcher : MonoBehaviour
    6. {
    7.     public Texture2D texture1 = null;
    8.     public Texture2D texture2 = null;
    9.  
    10.     public bool useTexture2 = false;
    11.    
    12.     bool oldSetting = false;
    13.     Renderer[] renderers = null;
    14.  
    15.  
    16.     void Start()
    17.     {
    18.         renderers = GetComponentsInChildren<Renderer>();
    19.         UpdateAllMaterials();
    20.     }
    21.  
    22.     void UpdateAllMaterials()
    23.     {
    24.         Texture textureToSet = texture1;
    25.         if ( useTexture2 )
    26.             textureToSet = texture2;
    27.         for ( int i = 0; i < renderers.Length; i++ )
    28.             renderers[i].material.mainTexture = textureToSet;
    29.         oldSetting = useTexture2;
    30.     }
    31.  
    32.     void Update ()
    33.     {
    34.         if (useTexture2 != oldSetting)
    35.             UpdateAllMaterials();
    36.     }
    37. }
    38.  
    Put the script on the parent object, and assign two different textures to texture1 and texture2. The script will then allow you to change textures on all the renderers below the parent object at runtime by changing the value of "useTexture2" from false to true or back.
     
    ErisC likes this.
  3. ErisC

    ErisC

    Joined:
    May 5, 2016
    Posts:
    6
    My apologies, working on it all day and trying to figure out different approaches (hence all the frustrations) mentally exhausted me. Yep, it was supposed to be during run time.

    The problem with what I'm having about the first texture (default) is that the names are different for each material per part, so I was trying to write scripts that involved either saving the original values using the undo and returning that save state once the switch is off. Wrote a code that had a revert prefab call after turning the switch off but the models I may get might not even be prefabbed in the first place.

    Thanks though, going to check out your code to see if I can get any ideas to work!
     
  4. cblarsen

    cblarsen

    Joined:
    Mar 10, 2007
    Posts:
    266
    The only command that works with prefabs at runtime is Instantiate()
    The various prefab manipulation and undo commands are for editor scripts. And they will not work in your finished game, since the finished game doesn't include the editor code.
    This version of the script will remember the old textures, instead of having a "texture1" to revert to:

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. public class TextureSwitcher : MonoBehaviour
    7. {
    8.     public Texture texture2 = null;
    9.  
    10.     public bool useTexture2 = false;
    11.     bool oldSetting = false;
    12.  
    13.     Renderer[] renderers = null;
    14.     List<Texture> rememberTextures = new List<Texture>();
    15.  
    16.  
    17.     void Start()
    18.     {
    19.         renderers = GetComponentsInChildren<Renderer>();
    20.         rememberTextures.Clear();
    21.         for ( int i = 0; i < renderers.Length; i++ )
    22.             rememberTextures.Add( renderers[i].material.mainTexture );
    23.         UpdateAllMaterials();
    24.     }
    25.  
    26.     void UpdateAllMaterials()
    27.     {
    28.          for ( int i = 0; i < renderers.Length; i++ )
    29.         {
    30.             Texture textureToSet = texture2;
    31.             if ( !useTexture2 )
    32.                 textureToSet = rememberTextures[i];
    33.             renderers[i].material.mainTexture = textureToSet;
    34.         }
    35.         oldSetting = useTexture2;
    36.     }
    37.  
    38.     void Update ()
    39.     {
    40.         if (useTexture2 != oldSetting)
    41.             UpdateAllMaterials();
    42.     }
    43. }
    44.  
     
    ErisC likes this.
  5. ErisC

    ErisC

    Joined:
    May 5, 2016
    Posts:
    6
    Tried using the above script and it would only change a part of the body (this was PQChan's open mouth part). What I'm trying to do is cycle between the two materials (Was able to change the materials in runtime using just a simple button with a skinnedmeshrenderer field) by pressing the button. I'm really new to coding and unity in general so is there a way to
    1) Select the parts from the parent (PQ-chan prefab)
    2) Save the material for the parts (like putting it on the list like the "List<Texture>" line), so the list would contain the remembered material and the secondary material (gray).
    3) On button press load the secondary material.
    4) On button press load the remembered material.

    Also, if the part (PQ-Chan's skirt) has two material types, is there a way just to have the button apply all three levels of materials and just nulling the extra applications if there are one or two fields?

    Thank you for helping me.
     
  6. cblarsen

    cblarsen

    Joined:
    Mar 10, 2007
    Posts:
    266
    I am not familiar with PQChan.

    That is what the line
    Code (csharp):
    1. renderers = GetComponentsInChildren<Renderer>();
    does. "renderers" becomes a array of all the renderers below the parent object. (If the script is placed on the parent object)
    Yes, you would simply make a list of materials instead of textures. And instead of renderers.material.mainTexture, you would have to save each of the materials in renderers.materials
    Yes to the first question, but I am not sure what you mean by "extra applications"

    If the stuff you are making is not too secret, could you maybe make a unitypackage with your example, so I can make something that works on it?

    You are welcome. Helping noobs is probably good for my spirit or something.
     
  7. ErisC

    ErisC

    Joined:
    May 5, 2016
    Posts:
    6
    The asset package is too big so I can only send the test scene itself. The model is under the assets store (Query-Chan without the sounds), all I did was load the model into the scene, added a button with a change material (left to be scripted, currently just changing the body part on mouse down and back on mouse up).

    For the multiple applications thing, it's hard for me to word properly (guess materials applied is more apt) since I'm a noob but it's probably easier once you have the assets loaded. Using the model, she has about ten to twelve parts and one of them (skirt) has two different material used, the script I'm trying to figure out is saving and applying three levels of the materials.

    For example, since the skirt has two levels of materials, the third would just not be there and won't really do much. But for the future, if I loaded a model with three levels of materials, it would still work.
     

    Attached Files:

  8. cblarsen

    cblarsen

    Joined:
    Mar 10, 2007
    Posts:
    266
    Ok, I downloaded PQChan, and got it to work with this code:
    - Put the code below in a C# script called MaterialSwitcher
    - put the script on the top PQChan object.
    - link a material to the "otherMaterial" field
    - Link your button to the PQChan object and the last function in MaterialSwitcher called "SwitchSetting"
    That should be it.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4.  
    5. public class MaterialSwitcher : MonoBehaviour
    6. {
    7.     public Material otherMaterial = null;
    8.  
    9.     public bool useOther = false;
    10.  
    11.     bool oldSetting = false;
    12.  
    13.     Renderer[] renderers = null;
    14.     Dictionary<Renderer, Material[]> rememberMaterials = new Dictionary< Renderer, Material[]>();
    15.  
    16.     void Start()
    17.     {
    18.         renderers = GetComponentsInChildren<Renderer>();
    19.         rememberMaterials.Clear();
    20.         for ( int i = 0; i < renderers.Length; i++ )
    21.         {
    22.             Material[] materials = renderers[i].materials;
    23.             rememberMaterials[renderers[i]] = materials;
    24.         }
    25.         UpdateAllMaterials();
    26.     }
    27.  
    28.     void UpdateAllMaterials()
    29.     {
    30.         for ( int i = 0; i < renderers.Length; i++ )
    31.         {
    32.             Renderer r = renderers[i];
    33.             Material[] materials = null;
    34.             if (useOther)
    35.             {
    36.                 materials = r.materials;
    37.                 for ( int j = 0; j < materials.Length; j++ )
    38.                 {
    39.                     materials[j] = otherMaterial;
    40.                 }
    41.             } else {
    42.                 materials = rememberMaterials[r];
    43.             }
    44.             r.materials = materials;
    45.         }
    46.         oldSetting = useOther;
    47.     }
    48.  
    49.     void Update ()
    50.     {
    51.         if (useOther != oldSetting)
    52.             UpdateAllMaterials();
    53.     }
    54.  
    55.  
    56.     // Link the button to this function
    57.     public void SwitchSetting()
    58.     {
    59.         useOther = !useOther;
    60.     }
    61. }
    62.  
     
    ErisC likes this.
  9. ErisC

    ErisC

    Joined:
    May 5, 2016
    Posts:
    6
    You, sir, are absolutely amazing. Thank you!

    This has been stumping me for a week, trying so many different ways to try to do it. Will definitely learn from this.

    Owe you a beer (or drink of choice)!
     
    cblarsen likes this.
  10. cblarsen

    cblarsen

    Joined:
    Mar 10, 2007
    Posts:
    266
    Duly noted :)