Search Unity

Script to re-tint an object causing material to be lost when I stop test-running my scene.

Discussion in 'Scripting' started by AndrewGrayGames, Oct 25, 2014.

  1. AndrewGrayGames

    AndrewGrayGames

    Joined:
    Nov 19, 2009
    Posts:
    3,821
    In my current project, I have some code for an object that follows the mouse cursor around. I've added a script hook that takes a boolean value, that causes the object to either take a 'castable' value (if you can cast the spell that materializes the full effect), or a 'not castable' color (if you are unable to cast the spell for some reason.)

    Code (csharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4.  
    5. public class SpellTargetingGhost : DebuggableBehavior
    6. {
    7.     #region Variables
    8.  
    9.     public float effectDepth = 6.5f;
    10.     public string colorProperty = "_TintColor";
    11.     public Color castableColor;
    12.     public Color notCastableColor;
    13.  
    14.     private Material _objectMaterial;
    15.  
    16.     #endregion Variables
    17.  
    18.     #region Engine Hooks
    19.  
    20.     public void Start()
    21.     {
    22.         _objectMaterial = renderer.materials[0];
    23.     }
    24.  
    25.     public void Update()
    26.     {
    27.         Vector3 pos = Input.mousePosition;
    28.         pos.z = effectDepth;
    29.         pos = Camera.main.ScreenToWorldPoint(pos);
    30.  
    31.         transform.position = pos;
    32.     }
    33.  
    34.     public void OnDestroy()
    35.     {
    36.         DestroyImmediate(_objectMaterial);
    37.     }
    38.  
    39.     #endregion Engine Hooks
    40.  
    41.     #region Methods
    42.  
    43.     public void TintByCastability(bool canCast)
    44.     {
    45.         Color newColor = canCast ? castableColor : notCastableColor;
    46.         renderer.material.SetColor(colorProperty, newColor);
    47.     }
    48.  
    49.     #endregion Methods
    50. }
    Also, the shader I'm using has a _TintColor property on it. This is a modified version of the Particles/Additive Shader....

    Code (csharp):
    1.  
    2. Shader "Particles/Additive-Backculled" {
    3.     Properties {
    4.        _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5)
    5.        _MainTex ("Particle Texture", 2D) = "white" {}
    6.        _InvFade ("Soft Particles Factor", Range(0.01,3.0)) = 1.0
    7.     }
    8.      
    9.     Category {
    10.        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    11.        Blend SrcAlpha One
    12.        AlphaTest Greater .01
    13.        ColorMask RGB
    14.        Cull Back Lighting Off ZWrite Off Fog { Color (0,0,0,0) }
    15.        BindChannels {
    16.            Bind "Color", color
    17.            Bind "Vertex", vertex
    18.            Bind "TexCoord", texcoord
    19.        }
    20.      
    21.        // ---- Fragment program cards
    22.        SubShader {
    23.            Pass {
    24.      
    25.                CGPROGRAM
    26.                #pragma vertex vert
    27.                #pragma fragment frag
    28.                #pragma fragmentoption ARB_precision_hint_fastest
    29.                #pragma multi_compile_particles
    30.      
    31.                #include "UnityCG.cginc"
    32.      
    33.                sampler2D _MainTex;
    34.                fixed4 _TintColor;
    35.      
    36.                struct appdata_t {
    37.                    float4 vertex : POSITION;
    38.                    fixed4 color : COLOR;
    39.                    float2 texcoord : TEXCOORD0;
    40.                };
    41.      
    42.                struct v2f {
    43.                    float4 vertex : POSITION;
    44.                    fixed4 color : COLOR;
    45.                    float2 texcoord : TEXCOORD0;
    46.                    #ifdef SOFTPARTICLES_ON
    47.                    float4 projPos : TEXCOORD1;
    48.                    #endif
    49.                };
    50.      
    51.                float4 _MainTex_ST;
    52.      
    53.                v2f vert (appdata_t v)
    54.                {
    55.                    v2f o;
    56.                    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    57.                    #ifdef SOFTPARTICLES_ON
    58.                    o.projPos = ComputeScreenPos (o.vertex);
    59.                    COMPUTE_EYEDEPTH(o.projPos.z);
    60.                    #endif
    61.                    o.color = v.color;
    62.                    o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);
    63.                    return o;
    64.                }
    65.      
    66.                sampler2D _CameraDepthTexture;
    67.                float _InvFade;
    68.      
    69.                fixed4 frag (v2f i) : COLOR
    70.                {
    71.                    #ifdef SOFTPARTICLES_ON
    72.                    float sceneZ = LinearEyeDepth (UNITY_SAMPLE_DEPTH(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos))));
    73.                    float partZ = i.projPos.z;
    74.                    float fade = saturate (_InvFade * (sceneZ-partZ));
    75.                    i.color.a *= fade;
    76.                    #endif
    77.      
    78.                    return 2.0f * i.color * _TintColor * tex2D(_MainTex, i.texcoord);
    79.                }
    80.                ENDCG
    81.            }
    82.        }  
    83.      
    84.        // ---- Dual texture cards
    85.        SubShader {
    86.            Pass {
    87.                SetTexture [_MainTex] {
    88.                    constantColor [_TintColor]
    89.                    combine constant * primary
    90.                }
    91.                SetTexture [_MainTex] {
    92.                    combine texture * previous DOUBLE
    93.                }
    94.            }
    95.        }
    96.      
    97.        // ---- Single texture cards (does not do color tint)
    98.        SubShader {
    99.            Pass {
    100.                SetTexture [_MainTex] {
    101.                    combine texture * primary
    102.                }
    103.            }
    104.        }
    105.     }
    106. }
    I've noticed some 'interesting' behavior going on.

    First, the first time I use a spell, the material in the prefab for the ghost object in question gets replaced not with an instance of the material, but with 'Type Mismatch'. As soon as I stop running the game, the prefab's material becomes 'Missing.' I can re-assign the saved material, and re-run the game, but every time I stop running, my prefab's material gets nuked again. More interestingly, I don't get a console message alerting me to why the prefab's material has become 'Type Mismatch', during or after execution. The material on the prefab's mesh renderer merely ceases to be.

    I did some re-reading on the renderer.material and renderer.materials property, and it seems that it should be instantiating a new instance of the material whenever I call these properties.

    What's going on with this code? Do I need to submit a bug, or have I managed to overlook something?
     
    Last edited: Oct 25, 2014
  2. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    I get no problems using your script + shader:

    The only edit to the script being:
    Code (CSharp):
    1. bool clicked;
    2. //... Update () { ...
    3.         if ( Input.GetMouseButtonDown( 0 ) ) {
    4.             clicked = !clicked;
    5.             TintByCastability( clicked );
    6.         }
     
  3. AndrewGrayGames

    AndrewGrayGames

    Joined:
    Nov 19, 2009
    Posts:
    3,821
    I wonder if this is related to the fact that I'm instantiating the game object from a prefab? Unlike your example, the ghost effect does not exist in the object hierarchy, until it is brought into being.
     
  4. hpjohn

    hpjohn

    Joined:
    Aug 14, 2012
    Posts:
    2,190
    Still not getting your problem

    The fault must be elsewhere in script that you have not posted (maybe the base class [DebuggableBehavior] )
     
  5. AndrewGrayGames

    AndrewGrayGames

    Joined:
    Nov 19, 2009
    Posts:
    3,821
    DebuggableBehavior only adds a wrapper and some exposed fields for debugging purposes.

    Code (csharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4.  
    5. public abstract class DebuggableBehavior : MonoBehaviour
    6. {
    7.     #region Enumerations
    8.  
    9.     public enum LogLevel
    10.     {
    11.         Information,
    12.         Warning,
    13.         LogicError,
    14.     }
    15.  
    16.     #endregion Enumerations
    17.  
    18.     #region Variables / Properties
    19.  
    20.     public bool DebugMode = false;
    21.     public bool ShowTimestamps = false;
    22.  
    23.     #endregion Variables / Properties
    24.  
    25.     #region Methods
    26.  
    27.     public void DebugMessage(string message, LogLevel level = LogLevel.Information)
    28.     {
    29.         if(! DebugMode)
    30.             return;
    31.  
    32.         if(ShowTimestamps)
    33.             message = DateTime.Now.ToString("HH:mm:ss") + ": " + message;
    34.  
    35.         switch(level)
    36.         {
    37.             case LogLevel.Information:
    38.                 Debug.Log(message);
    39.                 break;
    40.  
    41.             case LogLevel.Warning:
    42.                 Debug.LogWarning(message);
    43.                 break;
    44.  
    45.             case LogLevel.LogicError:
    46.                 Debug.LogError(message);
    47.                 break;
    48.  
    49.             default:
    50.                 throw new Exception("Unexpected log level! " + level.ToString()
    51.                                    + Environment.NewLine + " message: " + message);
    52.         }
    53.     }
    54.  
    55.     #endregion Methods
    56. }
    This has nothing to do with my issues, as you can see.

    Further I have linked you the entire behavior at this point. The only other things that aren't linked are the code that calls this code, and those are unimportant as those are only hitting methods on the given behavior.