Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Komak57 · Mar 29, 2014 at 06:00 PM · componentclassinterfacepropertydrawerserializedproperty

PropertyDrawer dynamic subdrawer

In this project, I wanted to create a drawer within a drawer that changed based on what kind of object was attached. For this example, I'll say that I want 'MyCollider : MonoBehaviour' to contain 'public BuffEffect Debuff;' which requests a MonoScript from the user that only allows classes that implement an desired interface. This interface will be called 'Effect', and contain methods desired by the game engine.

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 public class MyCollider : MonoBehaviour {
     [SerializeField]
     public BuffEffect Debuff;
 }
 
 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 
 [System.Serializable]
 public class BuffEffect : System.Object {
     [SerializeField]
     public Texture Icon;
     [SerializeField]
     public Object Buff;
 }
 
 using UnityEngine;
 using System.Collections;
 using UnityEditor;
 
 [CanEditMultipleObjects]
 [CustomPropertyDrawer(typeof(BuffEffect))]
 public class BuffDrawer : PropertyDrawer {
     public Object script;
     private SerializedProperty _script;
     public Texture Icon = EditorGUIUtility.whiteTexture;
     private SerializedProperty _Icon;
     //private float width;
     private string lasterror = "";
 
     // Here you must define the height of your property drawer. Called by Unity.
     public override float GetPropertyHeight (SerializedProperty prop,
                                              GUIContent label) {
         int height = 0;
         if (lasterror != "")
             height += 16;
         if (script != null) {
             //height += 16;
             //height += 64;
         }
         return base.GetPropertyHeight (prop, label) + height;
     }
     
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         if (_script == null) {
             if ((_script = property.FindPropertyRelative("Buff")) != null)
                 script = _script.objectReferenceValue as Object;
         }
         if (_Icon == null) {
             if ((_Icon = property.FindPropertyRelative("Icon")) != null)
                 Icon = _Icon.objectReferenceValue as Texture;
         }
         //script = property.objectReferenceValue as MonoScript;
         //Icon = property.FindPropertyRelative("Icon").objectReferenceValue as Texture;
         Rect pos = position;
         pos.height = 16;
         RequestScript(pos, property);
         DisplayError(pos, property);
         if (script != null) {
             //property.objectReferenceValue = script as Object;
             //RequestIcon(pos, property);
         }
     }
     public void RequestScript(Rect position, SerializedProperty prop) {
 
         EditorGUI.LabelField(position, "Effect: ");
         EditorGUI.BeginChangeCheck ();
         //position.x += 100;
         //position.width -= 100;
         EditorGUI.indentLevel = 8;
         Object value = EditorGUI.ObjectField(position, script as Object, typeof(MonoScript), false) as MonoScript;
 
         if (EditorGUI.EndChangeCheck()) {
             try {
                 if (typeof(Effect).IsAssignableFrom((value as MonoScript).GetClass())) {
                     script = value;
                     _script.objectReferenceValue = script as Object;
                 } else
                     lasterror = value.name+" does not implement <Effect>";
                 lasterror = "";
             } catch (System.Exception e) {
                 Debug.LogError("Script Load Error: "+e.ToString());
                 lasterror = e.ToString();
                 script = null;
             }
         }
     }
     public void RequestIcon(Rect position, SerializedProperty prop) {
         EditorGUI.indentLevel = 0;
         position.y += 16;
         //EditorGUI.DrawPreviewTexture(new Rect(position.x,position.y, 64, 64), Icon);
         GUIStyle style = new GUIStyle();
         if (Icon != null)
             style.normal.background = Icon as Texture2D;
         else
             style.normal.background = EditorGUIUtility.whiteTexture;
         EditorGUI.LabelField(new Rect(position.x,position.y, 64, 64), GUIContent.none, style);
 
         position.y += 64;
         EditorGUI.BeginChangeCheck ();
         Texture value = EditorGUI.ObjectField(new Rect(position.x,position.y, 64, 16), Icon as Object, typeof(Texture), false) as Texture;
         if (EditorGUI.EndChangeCheck()) {
             try {
                 Icon = value;
                 _Icon.objectReferenceValue = Icon as Object;
             } catch (System.Exception e) {
                 Debug.LogError("Texture Load Error: "+e.ToString());
                 lasterror = e.ToString();
                 Icon = null;
             }
         }
     }
     public void DisplayError(Rect position, SerializedProperty prop) {
         if (lasterror != "") {
             EditorGUI.indentLevel = 2;
             position.y += 16;
             EditorGUI.LabelField(position, "ERR: "+lasterror);
         }
     }
 }

Got it? Great. Now, the code has the functionality to display the disired Icon, but we want to let our new subdrawer take care of that. So next on the list is to create us a buff. Let's say we want a Poison buff to take effect, and by buff, I mean debuff.

 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 
 
 public interface Effect {
     string Name();
     void SetTarget(Transform transform);
     void StartEffect();
     void Tick(float Elapsed);
     void EndEffect();
 }
 
 using UnityEngine;
 using System.Collections;
 
 [System.Serializable]
 public class Poison : Object, Effect {
     [SerializeField]
     public Texture Icon;
     [SerializeField]
     public float Damage = 1f;
     [SerializeField]
     public float Interval = 1f;
     [SerializeField]
     public float Duration = 10f;
     private float elapsed = 0;
     private float totalelapsed = 0;
     public Transform target;
 
     #region Effect implementation
     public string Name() {
         return "Poison";
     }
     public void SetTarget(Transform transform) {
         target = transform;
     }
     public void StartEffect ()
     {
         Icon = Resources.Load("effect/poison", typeof(Texture)) as Texture;
     }
 
     public void Tick (float Elapsed)
     {
         elapsed += Elapsed;
         // loop all ticks, but no more than allowed
         while (elapsed >= Interval && (totalelapsed+elapsed < Duration)) {
             elapsed -= Interval;
             totalelapsed += Interval;
             target.GetComponent<SurvivalManager>().Blood -= Damage;
         }
         totalelapsed += elapsed;
         elapsed = 0;
         if (totalelapsed >= Duration) {
             // signal buff for deletion
             PlayerController plyr = target.GetComponent<PlayerController>();
             if (plyr != null)
                 plyr.ClearBuff((Effect)this);
         }
     }
 
     public void EndEffect ()
     {
     }
 
     #endregion
 
 
 }

Keep in mind that all of these scripts need to be separated in order for them to be accessible via all other scripts. A quick read through should inform you that this is a rather basic buff process. We can script different buffs that all call a start, tick, and end effects. Next we need to generate our drawer to print the preferred editable variables into the component editor.

This code needs assistance

 using UnityEngine;
 using System.Collections;
 using UnityEditor;
 
 [CanEditMultipleObjects]
 [CustomPropertyDrawer(typeof(Poison))]
 public class PoisonDrawer : PropertyDrawer {
     public Texture Icon = null;
     public float Damage = 0;
     public float Interval = 0;
     public float Duration = 0;
     
     private SerializedProperty _Icon;
     private SerializedProperty _Damage;
     private SerializedProperty _Interval;
     private SerializedProperty _Duration;
     
     // Here you must define the height of your property drawer. Called by Unity.
     public override float GetPropertyHeight (SerializedProperty prop,
                                              GUIContent label) {
         int height = 80;
         return base.GetPropertyHeight (prop, label) + height;
     }
     
     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
     {
         if (_Icon == null) {
             if ((_Icon = property.FindPropertyRelative("Icon")) != null)
                 Icon = _Icon.objectReferenceValue as Texture;
         }
         if (_Damage == null) {
             if ((_Damage = property.FindPropertyRelative("Damage")) != null)
                 Damage = _Damage.floatValue;
         }
         //script = property.objectReferenceValue as MonoScript;
         //Icon = property.FindPropertyRelative("Icon").objectReferenceValue as Texture;
         Rect pos = position;
         pos.height = 16;
         //property.objectReferenceValue = script as Object;
         RequestIcon(pos, property);
         RequestFloat(ref pos, "Damage", ref Damage, ref _Damage, property);
         RequestFloat(ref pos, "Interval", ref Interval, ref _Interval, property);
         RequestFloat(ref pos, "Duration", ref Duration, ref _Duration, property);
     }
     public void RequestFloat(ref Rect position,string Variable, ref float Value, ref SerializedProperty _Value, SerializedProperty prop) {
         EditorGUI.indentLevel = 5;
         position.y += 16;
         //position.x = 70;
         EditorGUI.LabelField(position, Variable+": ");
         position.x += 70;
         position.width -= 70;
         EditorGUI.BeginChangeCheck ();
         string value = EditorGUI.TextField(position, Value.ToString());
         
         if (EditorGUI.EndChangeCheck()) {
             try {
                 Value = float.Parse(value);
                 _Value.floatValue = Value;
             } catch (System.Exception e) {
                 Debug.LogError("Float Set Error: "+e.ToString());
                 Value = 0;
             }
         }
         position.x -= 70;
         position.width += 70;
     }
     public void RequestIcon(Rect position, SerializedProperty prop) {
         EditorGUI.indentLevel = 0;
         position.y += 16;
         //EditorGUI.DrawPreviewTexture(new Rect(position.x,position.y, 64, 64), Icon);
         GUIStyle style = new GUIStyle();
         if (Icon == null)
             style.normal.background = EditorGUIUtility.whiteTexture;
         else
             style.normal.background = Icon as Texture2D;
         
         EditorGUI.LabelField(new Rect(position.x,position.y, 64, 64), GUIContent.none, style);
         
         position.y += 64;
         EditorGUI.BeginChangeCheck ();
         Texture value = EditorGUI.ObjectField(new Rect(position.x,position.y, 64, 16), Icon as Object, typeof(Texture), false) as Texture;
         if (EditorGUI.EndChangeCheck()) {
             try {
                 Icon = value as Texture;
                 _Icon.objectReferenceValue = Icon as Object;
             } catch (System.Exception e) {
                 Debug.LogError("Texture Load Error: "+e.ToString());
                 Icon = null;
             }
         }
     }
 }

This code currently relates to a Poison : Effect, but the serialized object relates to an Effect type. Not sure how I can subdrawer it, and I keep getting System.NullReferenceException on the SerializedProperty's in ALL cases in Poison. I'm wondering if this has anything to do with it implementing an interface, and how to get around it, while still having access to the methods within.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

0 Replies

· Add your reply
  • Sort: 

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

20 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

[JS] [Editor] SerializedObject from a custom class. 0 Answers

SerializedObject.FindProperty returning null 2 Answers

How to apply script saved in variable 2 Answers

Draw custom Property Field in Custom Editor 1 Answer

AttributeDrawer: Draw parent property? (the one [AttributeName] is attached to) 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges