Search Unity

Drawing a sprite in editor window

Discussion in 'Scripting' started by zoran404, Jul 30, 2016.

  1. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    I have a bunch of ScriptableObjects that hold references to Spriters I need in game.
    I'd like to have a preview of the selected sprite when viewing the scriptable object.

    Is there an attribute that I can put on the Sprite field or other built in way to do this?

    If not I think I will make a property drawer that would use GUI.DrawTexture.
     
  2. jister

    jister

    Joined:
    Oct 9, 2009
    Posts:
    1,749
  3. zoran404

    zoran404

    Joined:
    Jan 11, 2015
    Posts:
    520
    Here's my code to display sprite's texture below the property.
    Simply put the PropertyDrawer inside an Editor folder and the PropertyAttribute somewhere next to it (not inside an Editor folder).
    Now you can put [PreviewSprite] above any Sprite field and have your sprite image preview.

    Code (CSharp):
    1. using UnityEngine;
    2. namespace UnityEditor {
    3. [CustomPropertyDrawer(typeof(PreviewSpriteAttribute))]
    4. public class PreviewSpriteDrawer : PropertyDrawer
    5. {
    6.     const float imageHeight = 100;
    7.  
    8.     public override float GetPropertyHeight(SerializedProperty property,
    9.                                             GUIContent label)
    10.     {
    11.         if (property.propertyType == SerializedPropertyType.ObjectReference &&
    12.             (property.objectReferenceValue as Sprite) != null)
    13.         {
    14.             return EditorGUI.GetPropertyHeight(property, label, true) + imageHeight + 10;
    15.         }
    16.         return EditorGUI.GetPropertyHeight(property, label, true);
    17.     }
    18.  
    19.     static string GetPath(SerializedProperty property)
    20.     {
    21.         string path = property.propertyPath;
    22.         int index = path.LastIndexOf(".");
    23.         return path.Substring(0, index + 1);
    24.     }
    25.  
    26.     public override void OnGUI(Rect position,
    27.                                 SerializedProperty property,
    28.                                 GUIContent label)
    29.     {
    30.         //Draw the normal property field
    31.         EditorGUI.PropertyField(position, property, label, true);
    32.  
    33.         if (property.propertyType == SerializedPropertyType.ObjectReference)
    34.         {
    35.             var sprite = property.objectReferenceValue as Sprite;
    36.             if (sprite != null)
    37.             {
    38.                 position.y += EditorGUI.GetPropertyHeight(property, label, true) + 5;
    39.                 position.height = imageHeight;
    40.                 //EditorGUI.DrawPreviewTexture(position, sprite.texture, null, ScaleMode.ScaleToFit, 0);
    41.                 GUI.DrawTexture(position, sprite.texture, ScaleMode.ScaleToFit);
    42.             }
    43.         }
    44.     }
    45. }
    46. }
    Code (CSharp):
    1. namespace UnityEngine {
    2. public class PreviewSpriteAttribute : PropertyAttribute
    3. {
    4.     public PreviewSpriteAttribute() {}
    5. }
    6. }
     
  4. TommySKD

    TommySKD

    Joined:
    Jul 23, 2014
    Posts:
    25
    This is great but it shows the entire spritesheet when it contains multiple sprites instead of showing only the selected one.
    I fixed it by using GUI.DrawTextureWithTexCoords but this method doesn't have the ScaleMode argument for some reason so I just replicated the ScaleToFit functionality myself.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace UnityEditor
    4. {
    5.     [CustomPropertyDrawer(typeof(PreviewSpriteAttribute))]
    6.     public class PreviewSpriteDrawer : PropertyDrawer
    7.     {
    8.         const float imageHeight = 100;
    9.  
    10.         public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    11.         {
    12.             if (property.propertyType == SerializedPropertyType.ObjectReference &&
    13.                 (property.objectReferenceValue as Sprite) != null)
    14.             {
    15.                 return EditorGUI.GetPropertyHeight(property, label, true) + imageHeight + 10;
    16.             }
    17.             return EditorGUI.GetPropertyHeight(property, label, true);
    18.         }
    19.  
    20.         static string GetPath(SerializedProperty property)
    21.         {
    22.             string path = property.propertyPath;
    23.             int index = path.LastIndexOf(".");
    24.             return path.Substring(0, index + 1);
    25.         }
    26.  
    27.         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    28.         {
    29.             //Draw the normal property field
    30.             EditorGUI.PropertyField(position, property, label, true);
    31.  
    32.             if (property.propertyType == SerializedPropertyType.ObjectReference)
    33.             {
    34.                 var sprite = property.objectReferenceValue as Sprite;
    35.                 if (sprite != null)
    36.                 {
    37.                     position.y += EditorGUI.GetPropertyHeight(property, label, true) + 5;
    38.                     position.height = imageHeight;
    39.  
    40.                     //GUI.DrawTexture(position, sprite.texture, ScaleMode.ScaleToFit);
    41.                     DrawTexturePreview(position, sprite);
    42.                 }
    43.             }
    44.         }
    45.  
    46.         private void DrawTexturePreview(Rect position, Sprite sprite)
    47.         {
    48.             Vector2 fullSize = new Vector2(sprite.texture.width, sprite.texture.height);
    49.             Vector2 size = new Vector2(sprite.textureRect.width, sprite.textureRect.height);
    50.  
    51.             Rect coords = sprite.textureRect;
    52.             coords.x /= fullSize.x;
    53.             coords.width /= fullSize.x;
    54.             coords.y /= fullSize.y;
    55.             coords.height /= fullSize.y;
    56.  
    57.             Vector2 ratio;
    58.             ratio.x = position.width / size.x;
    59.             ratio.y = position.height / size.y;
    60.             float minRatio = Mathf.Min(ratio.x, ratio.y);
    61.  
    62.             Vector2 center = position.center;
    63.             position.width = size.x * minRatio;
    64.             position.height = size.y * minRatio;
    65.             position.center = center;
    66.  
    67.             GUI.DrawTextureWithTexCoords(position, sprite.texture, coords);
    68.         }
    69.     }
    70. }
     
    Last edited: May 6, 2017
    AnaLuyza, rscopic, tokar_dev and 17 others like this.
  5. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,735
    At risk of being accused of necroing, I just had to say thanks for this, saved me a load of time and works perfectly.
     
  6. spritefun

    spritefun

    Joined:
    May 23, 2014
    Posts:
    1
    Yeah I really don't wanna necro this thread, but can someone explain how the PreviewSpriteAttribute class works? It just has an empty method, I don't understand lol. Is that class even necessary?
    Other than that this worked perfectly for me and I now have sprite previews in my scriptable object editors.
     
  7. MegamaDev

    MegamaDev

    Joined:
    Jul 17, 2017
    Posts:
    77
    Might as well join you in necroing to explain~

    PreviewSpriteAttribute defines the attribute that you tag the field with to invoke the PropertyDrawer. The empty method is a constructor, which doesn't do anything because the attribute doesn't need any parameters to work. If you wanted to store, say, a user-defined height value, you would read that in through the constructor like so:
    Code (CSharp):
    1. namespace UnityEngine {
    2.     public class PreviewSpriteAttribute : PropertyAttribute
    3.     {
    4.         public int _height;
    5.         public PreviewSpriteAttribute(int height = 100)
    6.         {
    7.             _height = height;
    8.         }
    9.     }
    10. }
    And then to read the height in the Drawer methods:
    Code (CSharp):
    1. var attr = attribute as PreviewSpriteAttribute;
    2. int height = attr._height;
     
  8. CodeLane_Wouter

    CodeLane_Wouter

    Joined:
    Nov 16, 2020
    Posts:
    1
    Bunny83 likes this.
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,006
    Right. Though keep in mind that there's also GUI.DrawTexture with many overloads. It's essentially a wrapper around Graphics.DrawTexture but does already the event check. Though an additional check does not hurt. Actually avoiding the method call could even be better ^^