Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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
1
Question by TSI25 · May 01, 2016 at 06:57 PM · propertydrawerserializedobjectcustominspector

How do I property Drawer?

So What I'm trying to do is sort of complicated and very specific I'll try to provide an appropriate amount of context. Essentially I have a Sound Collection class that has a big list of sounds and parameters that those sounds should be played with (volume, fade in, pitch, stuff like that).

Entirely separately I have an Interactible Object class which should play one or more sounds when interacted with. that class has a list of serialized classes (Controlled Sound) which define what type of sound the sound is, and what the name of the sound is to play (so it can be looked up in the sound collection by name).

Thats all well and good. Heres the problem I'm trying to solve.

I want to create a custom inspector/property drawer for that Sound Controller class that will let me select the type of sound the controlled sound is, and then a drop down of all the sounds in the sound collection from which to select a sound from (so the user doesnt have to type the exact name of the sound into the field.)

Here is the serialized class as well as my not-functioning attempt at the property drawer. I get the following error when trying to select a sound from the dropdown

ArgumentException: Getting control 0's position in a group with only 0 controls when doing Repaint Aborting

What am I doing wrong here? I've been studying the property drawer from the unity manual but it and the video tutorial seem very vague.

 using UnityEngine;
 using System.Collections;
 
 namespace Sol
 {
     [System.Serializable]
     public class ControlledSound 
     {
         [HideInInspector]
         public SoundType soundType;
 
         [HideInInspector]
         public string soundName;
     }
 }

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 using UnityEngine;
 using UnityEditor;
 using System.Collections;
 using System.Collections.Generic;
 
 namespace Sol
 {
     [CustomPropertyDrawer(typeof(ControlledSound))]
     public class ControlledSoundDrawer : PropertyDrawer
     {
         private SoundType selectedSoundType = SoundType.None;
         private string selectedSoundName = "";
 
         private SoundManager cachedSoundManager;
 
         private SoundManager CachedSoundManager
         {
             get { return (cachedSoundManager != null) ? cachedSoundManager : cachedSoundManager = GameManager.Get<SoundManager>(); }
         }
 
         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
         {
             Rect rect1 = new Rect(position.x, position.y, position.width / 2, position.height);
             selectedSoundType = (SoundType)EditorGUI.EnumPopup(rect1, selectedSoundType);
 
             EditorGUILayout.Space();
 
             int selectedIndex = 0;
             string[] options = new string[1];
             foreach(SoundCollection soundCollection in CachedSoundManager.collections)
             {
                 if(soundCollection.soundType == selectedSoundType)
                 {
                     List<string> soundNameList = soundCollection.GetSoundNames();
                     options = new string[soundNameList.Count];
                     for(int i = 0; i < soundNameList.Count; i++)
                     {
                         options[i] = soundNameList[i];
                         if (options[i] == selectedSoundName) selectedIndex = i;
                     }
                 }
             }
             Rect rect2 = new Rect(position.x + position.width / 2, position.y, position.width / 2, position.height);
             selectedSoundName = options[EditorGUI.Popup(rect2, selectedIndex, options)];
 
             DrawControlledSound(selectedSoundType, selectedSoundName);
         }
 
 
         public ControlledSound DrawControlledSound(SoundType type, string soundName)
         {
             ControlledSound newControlledSound = new ControlledSound();
             newControlledSound.soundType = type;
             newControlledSound.soundName = soundName;
             return newControlledSound;
         }
     }
 }

Comment
Add comment · Show 6
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
avatar image incorrect · May 01, 2016 at 08:10 PM 1
Share

Here is a bit more about property drawers: Custom Data, an introduction to serialized classes and property drawers by Catlike Coding.

Why are you using [HideInInspector] attribute? You don't need it in this case.

avatar image incorrect · May 01, 2016 at 08:15 PM 1
Share

Also you handle property drawer in a wierd way.

          private SoundType selectedSoundType = SoundType.None;
          private string selectedSoundName = "";     
          private Sound$$anonymous$$anager cachedSound$$anonymous$$anager;

Those references in a property drawer class act wierd: they are shared over all instances of a class being drawn. So if you have several ControlledSounds you still will have a single ControlledSoundDrawer.

And what is that for?!

          private Sound$$anonymous$$anager CachedSound$$anonymous$$anager
          {
              get { return (cachedSound$$anonymous$$anager != null) ? cachedSound$$anonymous$$anager : cachedSound$$anonymous$$anager = Game$$anonymous$$anager.Get<Sound$$anonymous$$anager>(); }
          }
avatar image TSI25 incorrect · May 01, 2016 at 09:00 PM 0
Share

Thanks, these posts have gotten me most of the way there i think? all that remains is creating a drop-down list of all the sounds in the sound collection that correspond to the selected type. For instance if they have the '$$anonymous$$usic' enum selected, i want the 'sound name' options to only be populated with music clips names from the sound collection (which incidentally is where getting a reference to the sound manager comes in handy. Game$$anonymous$$anager.get() is actually a lot cheaper than GameObject.FindObjectOfType() since it is looking up and instance of the sound manager singleton from a dictionary.)

Problem I'm running into is I can't really seem to compare the serialized property to the actual enum or string value in the serialized class. I'll keep messing with it and see what i can come up with.

Thanks for your help so far

avatar image incorrect · May 01, 2016 at 08:16 PM 0
Share

Also have you tried commenting parts of your code to find out were exactly an error occurs?

avatar image incorrect · May 01, 2016 at 10:28 PM 0
Share

Btw, u never actually assign selectedSoundType anywhere, so it stays in property drawer class and never assigned to an instance of a property.

avatar image incorrect incorrect · May 01, 2016 at 10:36 PM 0
Share

I mean that you need to use EditorGUI.PropertyField() to draw GUI element that gets and sets value.

Other approach is something like this:

             property.FindPropertyRelative("text").stringValue = EditorGUI.TextArea(contentPosition, property.FindPropertyRelative("text").stringValue, style);

1 Reply

· Add your reply
  • Sort: 
avatar image
3

Answer by troien · May 02, 2016 at 10:55 AM

A few things here are weird as @incorrect already mentioned.

The first thing you should do is to get rid of "selectedSoundType " and "selectedSoundName".

You should get these values from the SerializedProperty which is a parameter of the OnGUI method.

like so:

     SerializedProperty name = property.FindPropertyRelative("soundName");
 
     // You can display it using this
     EditorGUI.PropertyField(position, name);
     // Or use something like this
     name.stringValue = EditorGUI.TextField(position, name.stringValue);
     // Or in your case: (although your current code should break whenever options.Length == 0)
     name.stringValue = options[EditorGUI.Popup(rect2, selectedIndex, options)];

Also, the manual has some good examples.

Secondly, what is this "DrawControlledSound()" supposed to do? It clearly doesn't draw the controlled sound... It just creates a new one?

Also, get rid of:

 EditorGUILayout.Space();

As EditorGUILayout is not supported when doing PropertyDrawers (probably the reason for the error you have). Besides you are already using Rects to draw your properties anyway, they ignore this Layout as you hardcode the positions...

Depending on your implemetation I think your CachedSoundManager approach should work, as long as it never returns null. However, note that soundName can still be empty, your editor behaviour here assumes that your index is 0 then and you select the first option. To prevent weird errors, you should make sure your editor and playmode code make the same asumptions ;)

Comment
Add comment · Show 9 · Share
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
avatar image incorrect · May 02, 2016 at 10:59 AM 0
Share

Are you sure that SerializedProperty name = property.FindPropertyRelative("soundName"); will work? Is it possible to cast from a generic SerializedProperty to some type?

avatar image troien incorrect · May 02, 2016 at 11:08 AM 0
Share

I'm pretty sure it will work, I'm not sure I understand your question here though.

property is the SerializedProperty of an instance of ControlledSound.

ControlledSound has a field called "soundName".

So I can call "property.FindPropertyRelative("soundName")" to get the SerializedProperty of the field "soundName".

To access the content of the field, which is of type string. I can then use "name.stringValue".

avatar image TSI25 troien · May 02, 2016 at 03:01 PM 0
Share

So I can call "property.FindPropertyRelative("soundName")" to get the SerializedProperty of the field "soundName".

To access the content of the field, which is of type string. I can then use "name.stringValue".

Is there a way to retrieve the enum value from property.FindPropertyRelative("soundType") ?

Show more comments
Show more comments
avatar image Bunny83 · May 02, 2016 at 11:27 AM 0
Share

Well summarized.

Yes, PropertyDrawers are a bit nasty as they only work on the serialized data, not on the actual class instance. You can't directly access the actual class instance, only it's serialized data by using the property that the PropertyDrawer gets from the system.

Of course the "FindPropertyRelative" should be done once in OnEnable.

avatar image troien Bunny83 · May 02, 2016 at 11:31 AM 0
Share

Of course the "FindPropertyRelative" should be done once in OnEnable.

In normal occasions you are absolutely right. But not when talking about CustomPropertyDrawers. The only way to get the 'base' SerializedProperty here is through the parameter of the OnGUI method which you can override.

EDIT: Just checked, OnEnable doesn't even get called on a PropertyDrawer ;) It is a plain old C# class that doesn't inherrit from UnityEngine.Object. And in the constructor, you don't have access to any SerializedProperty as I mentioned :p

avatar image Bunny83 troien · May 03, 2016 at 01:43 AM 0
Share

Yes, you're right ^^ PropertyDrawers actually don't have an OnEnable callback. So maybe it's possible to cache them anyways, but i never tried that. It won't work if Unity is re-using the PropertyDrawer instance for other properties as well.

Show more comments

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

43 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 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 avatar image avatar image avatar image

Related Questions

PropertyDrawer inside an array 1 Answer

Get SerializedProperty from outside class for use in popup (PropertyDrawer) 1 Answer

Draw custom Property Field in Custom Editor 1 Answer

Undo a instantiated gameObject Editor Script C# 1 Answer

Saving changes to mixed CustomInspector 1 Answer


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