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 _= soundNameList*;*_
 <em>_if (options *== selectedSoundName) selectedIndex = i;*_</em>
 <em>_*}*_</em>
 <em>_*}*_</em>
 <em>_*}*_</em>
 <em>_*Rect rect2 = new Rect(position.x + position.width / 2, position.y, position.width / 2, position.height);*_</em>
 <em>_*selectedSoundName = options[EditorGUI.Popup(rect2, selectedIndex, options)];*_</em>
 
 <em>_*DrawControlledSound(selectedSoundType, selectedSoundName);*_</em>
 <em>_*}*_</em>
 
 
 <em>_*public ControlledSound DrawControlledSound(SoundType type, string soundName)*_</em>
 <em>_*{*_</em>
 <em>_*ControlledSound newControlledSound = new ControlledSound();*_</em>
 <em>_*newControlledSound.soundType = type;*_</em>
 <em>_*newControlledSound.soundName = soundName;*_</em>
 <em>_*return newControlledSound;*_</em>
 <em>_*}*_</em>
 <em>_*}*_</em>
 <em>_*}*_</em>

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 :wink: