Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Property Drawer to replace Reference

Discussion in 'UI Toolkit' started by MilanMatthes, May 13, 2020.

  1. MilanMatthes

    MilanMatthes

    Joined:
    Jun 26, 2017
    Posts:
    7
    Hello and welcome to my first question asked on this Forum. So far I managed to learn a lot from the other answers on here, but with this problem, I find no solution, so I thought I'd ask you helpful lot :D

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3.  
    4. [System.Serializable]
    5. public class Pet
    6. {
    7.     public string species;
    8.  
    9.     public Pet(string species)
    10.     {
    11.         this.species = species;
    12.     }
    13.     public static Pet[] availablePets = new Pet[4]{new Pet("dog"), new Pet("cat"), new Pet("fish"), new Pet("bird")};
    14. }
    15.  
    16. public class Person : MonoBehaviour
    17. {
    18.     public Pet myPet;
    19. }
    20.  
    21. [CustomPropertyDrawer(typeof(Pet))]
    22. public class PetDrawer : PropertyDrawer
    23. {
    24.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    25.     {
    26.         string[] speciesNames = new string[4]{"dog", "cat", "fish", "bird"};
    27.         int petIndex = 0;
    28.         petIndex = EditorGUI.Popup(position, selectedIndex, speciesNames);
    29.         //SOMETHING = Pet.availablePets[petIndex];
    30.     }
    31. }
    So, what you see above is a rudimentary broken down version of the problem. And what I need help with is the word SOMETHING.
    What I want to achieve is that in the Inspector for Person when the field for "Pet" is drawn, I want a dropdown menu from which I select dog, cat, fish or bird and then the value of myPet should be set to the reference of that pet from the list of Pets. And I don'd know what to write instead of SOMETHING. I find myself unable to just set the value myPet from a CustomPropertyDrawer for Pet. Is there any way it can be done? Workarounds would be appreciated too.
    One Workaround that I will not accept is to write a custom editor for Person. It shall not be bound to Person, but should work the same in a Shop class, as well as in an array or list of pets. They should all be the dropdown list. However, if there is any other way to do this than with custom property drawers, I am open to that too.

    I look forward to your help. :)

    (Honestly, I made bad experiences with Forums and asking people. People will usually question why you do things this way or that way. Please just trust me that I know that I want an answer to this specific questions. and if the answer is "it's impossible" that is also fine. But let's not talk about alternatives please. I know what I want.)
     
    Last edited: May 14, 2020
  2. MilanMatthes

    MilanMatthes

    Joined:
    Jun 26, 2017
    Posts:
    7
    So, after some more try and error I found out that
    Code (CSharp):
    1. property.objectReferenceValue = (UnityEngine.Object)Pet.availablePets[selectedIndex];
    kinda works. But Pet has to inherit from UnityEngine.Objet in order for this to work. In other words it must be a ScriptableObject or a MonoBehaviour. if I don't cast to UnityEngine.Object, I cannot set that value.

    I am not too happy with making pets ScriptableObjects for several reasons.
    1) As it seems, ScriptableObjects cannot be created with new and thus cannot have a constructor that takes arguments.
    2) They scare me. I didn't go too deep into them, but they always do strange things to my projects. when I inherit from ScriptableObject and later decide to remove the inheritance, I get a lot of errors. with ScriptableObjects I am never sure if they get properly collected by the garbage collector when I don't reference them any more. all in all, they act weird when you don't create them as assets in the project folder.
    3) why is that necessary? when Pet doesn't inherit from ScriptableObject, myPet still has a value. and because Pet is Serializable, that means the value does get saved... right?? so why can I not modify that value via propertydrawer?
     
  3. MilanMatthes

    MilanMatthes

    Joined:
    Jun 26, 2017
    Posts:
    7
    Alright. More research and attempts later and I found
    Code (CSharp):
    1. fieldInfo.SetValue(property.serializedObject.targetObject, Pet.availablePets[selectedIndex]);
    This does work on a simple
    Pet myPet
    field in Person. however, when Person has
    Pet[] pets
    instead, I get the ArgumentException:
    Object type 'Pet' cannot be converted to type 'Pet[]';

    and if I do this:
    Code (CSharp):
    1. public class PetInfo
    2. {
    3.      Pet pet;
    4.      int amount;
    5. }
    6.  
    7. public class Person : MonoBehaviour
    8. {
    9.      public PetInfo myPet;
    10. }
    I get the ArgumentException:
    Field myPet defined on type Person+PetInfo is not a field on the target object which is of type Person.

    I didn't expect it would be so hard to have a PropertyDrawer for a reference to a class allow you to set that reference.