Search Unity

SpriteRenderer variable changes via code are overwritten when object is instance of a prefab

Discussion in '2D' started by PiecesOfLogic, Oct 24, 2014.

  1. PiecesOfLogic

    PiecesOfLogic

    Joined:
    Aug 1, 2013
    Posts:
    2
    In my 2D project, I've been attempting to use code to tint sprites by programatically changing the SpriteRenderer's color variable. I've created custom editors to do this so that I can see the changes in edit mode as well as play mode.

    I've noticed a weird quirk that seems specific to the SpriteRenderer component: If the object is an instance of a prefab, then any SpriteRenderer variables revert when entering play mode. I've found two ways around this behavior:

    1. Break the prefab connection. Once this is done, the changes made persist between edit mode and play mode.
    2. Manually change the variable directly on the SpriteRenderer component itself. Once this is done, the new value becomes the "default", and this is the value it will revert to when entering play mode.

    Is this a bug, or am I missing something with Serialization? I've been looking all around, but can't find a way to make this work - I'm not able to reproduce this behavior with other components, so perhaps SpriteRenderer's serialization is handled differently?

    Below is some sample code (Test and TestEditor) which should reproduce the problem. Create a GameObject and attach Test, SpriteRenderer, and BoxCollider, and make it a prefab.

    1. Change the Sprite Color in Test and the change should also apply to the SpriteRenderer, until you hit play.
    2. Change either of the Collider items in Test, and those will be reflected in the Box Collider. Unlike the SpriteRenderer, these will persist when you hit play.

    If anyone can let me know what I'm missing, I'd very much appreciate it.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class Test : MonoBehaviour {
    5.  
    6.     [SerializeField, HideInInspector]
    7.     private Color _spritecolor;
    8.  
    9.     [SerializeField, HideInInspector]
    10.     private bool _colliderenabled;
    11.  
    12.     [SerializeField, HideInInspector]
    13.     private Vector2 _collidersize;
    14.  
    15.     private SpriteRenderer _parentsr;
    16.     private BoxCollider2D _collider;
    17.  
    18.     public Color SpriteColor
    19.     {
    20.         get { return _spritecolor; }
    21.         set
    22.         {
    23.             _spritecolor = value;
    24.             if (_parentsr == null) _parentsr = GetComponent<SpriteRenderer>();
    25.             _parentsr.color = _spritecolor;
    26.         }
    27.     }
    28.  
    29.     public bool ColliderEnabled
    30.     {
    31.         get { return _colliderenabled; }
    32.         set {
    33.             if (_collider == null) _collider = GetComponent<BoxCollider2D>();
    34.             _colliderenabled = value;
    35.             _collider.enabled = value;
    36.         }
    37.     }
    38.  
    39.     public Vector2 ColliderSize
    40.     {
    41.         get { return _collidersize; }
    42.         set
    43.         {
    44.             if (_collider == null) _collider = GetComponent<BoxCollider2D>();
    45.             _collidersize = value;
    46.             _collider.size = value;
    47.         }
    48.     }
    49.     // Use this for initialization
    50.     void Start () {
    51.         if (_parentsr == null) _parentsr = GetComponent<SpriteRenderer>();
    52.         if (_collider == null) _collider = GetComponent<BoxCollider2D>();
    53.     }
    54. }
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEditor;
    4.  
    5. [CustomEditor(typeof(Test))]
    6. public class TestEditor : Editor {
    7.  
    8.     public override void OnInspectorGUI()
    9.     {
    10.         base.OnInspectorGUI();
    11.  
    12.         Test myTarget = (Test)target;
    13.  
    14.         Color c = EditorGUILayout.ColorField("Sprite Color", myTarget.SpriteColor);
    15.         bool ce = EditorGUILayout.Toggle("Collider Enabled?", myTarget.ColliderEnabled);
    16.         Vector2 size = EditorGUILayout.Vector2Field("Collider Size", myTarget.ColliderSize);
    17.  
    18.         if (GUI.changed)
    19.         {
    20.             EditorUtility.SetDirty(myTarget);
    21.             myTarget.SpriteColor = c;
    22.             myTarget.ColliderEnabled = ce;
    23.             myTarget.ColliderSize = size;
    24.         }
    25.     }
    26. }
     
  2. PiecesOfLogic

    PiecesOfLogic

    Joined:
    Aug 1, 2013
    Posts:
    2
    It looks like the SpriteRenderer component isn't marked dirty when the changes are made. Therefore, changes to that component aren't overriding the prefab version.

    A simple workaround is to manually mark the SpriteRenderer component dirty in the Editor code:

    Code (CSharp):
    1.         if (GUI.changed)
    2.         {
    3.             EditorUtility.SetDirty(myTarget);
    4.             EditorUtility.SetDirty(myTarget.GetComponent<SpriteRenderer>()); // New line
    5.             myTarget.SpriteColor = c;
    6.             myTarget.ColliderEnabled = ce;
    7.             myTarget.ColliderSize = size;
    8.         }
    Perhaps this is intended or expected behavior, but it doesn't seem like it - I don't to have to treat any other modified components the same way.

    If someone could provide some color on this, that would be appreciated. Otherwise, I'll give this a little bit of time before submitting an issue.
     
  3. sharksharkshark

    sharksharkshark

    Joined:
    Apr 27, 2015
    Posts:
    13
    Just wanted to hop in really quick and say thank for posting your solution to this. I ran into more or less this exact same issue on a project and a quick google gave me exactly what I needed from your post.
     
  4. Tomer-Barkan

    Tomer-Barkan

    Joined:
    Jul 31, 2012
    Posts:
    150
    Aren't you missing a line?

    Code (CSharp):
    1. EditorApplication.SaveAssets();
     
  5. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Yes, this is a bug. We'll get it fixed for the next release!