Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Resolved Custom Editor for list of derivated class

Discussion in 'Scripting' started by TomatoDit, Oct 17, 2022.

  1. TomatoDit


    Nov 7, 2015
    Hello, I would like to create some design pattern with the right custom editor behaviour.

    Below the design pattern (applied to a SpellBook example) :
    Code (CSharp):
    1. using System.Collections.Generic;
    3. [CreateAssetMenu(menuName = "MainClass")]
    4. public class SpellBook : ScriptableObject
    5. {
    6.     [SerializeField] List<Spell> spells;
    7. }
    9. public class Spell
    10. {
    11.     [SerializeField] List<Effect> effects;
    12. }
    14. public abstract class Effect
    15. {
    16.     [SerializeField] string effectName;
    17. }
    19. public class EffectA : Effect
    20. {
    21.     [SerializeField] int specificIntField;
    22. }
    24. public class EffectB : Effect
    25. {
    26.     [SerializeField] float specificFloatField;
    27. }
    What I want to do :
    - Create the scriptable object from contextual menu (this is done thanks to CreateAssetMenu)
    - Being able to add a spell to the spellbook (normal behaviour from Unity Editor)
    - Being able to add an effect to the spell and either ideally chosing the type of effect I add when I click on the "+" button, either add a none-specific effect and afterwards through a field set the effect type*
    - As soon as the element is created with the right type the specific field(s) from the effect should appear and be changeable (specificIntField or specificFloatField from above example).

    *type, I did not create an enum or a effect-list somehow which I assume is needed for what I want. As it may be changing the answer and I do not have limitation on this, let me know what you would suggest.

    Note : the spells and the effects have to be reorderable. The number of different effect classes is limited (less than 10). SpellBook has to be a ScriptableObject, Spell/Effect may be if needed ScriptableObjects.

    I want to create a custom editor for those classes. I do not know on which class(es) I need to create the custom Editor(s) and/or PropertyDrawer(s).

    I know this kind of design pattern is very common and this kind of question has been asked by the past but I am having a hard time finding the right approach even reading posts from the past (from this forum and other similar ones).
    I hope everything is clear, do not hesitate if you need some more details

    Thanks in advance :)
  2. orionsyndrome


    May 4, 2014
    Creating PropertyDrawers for complex info is too much work. Usually you do that for atomic data, like any new type you might introduce or an old type you'd like to expand on, and then you make a standard inspector editor using EditorGUILayout components. You can download the source code for property drawer from this thread, where I make a custom enumeration class. You might as well find the actual solution useful.

    I've done a fair share of these, and it's a lot of work before you have a reliable toolkit/library which you can refer to for compound methods, otherwise your editor code is going to turn out very bloated, messy, and overwhelming at a certain point.

    I typically make editors that are also able to hijack the scene view to enable some sort of in-scene edit mode, and there is a lot of hassle to do that properly, because you want to safe guard against usual shortcuts and modes of interaction already in there, and you want to make sure that all coordinates are converted, that you have access to a proper camera, that F still works, that multi-selection works, oh man, this topic is too deep.

    If your inspector is already reliable as it is, and you just want to add buttons to it, there are simple things you can do to make this work. For example, you can introduce a context menu item.

    Or you can make a shortened version of custom editor that just adds a button, without having to reimplement the rest of it.
    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    4. [CustomEditor(typeof(MyThing))]
    5. public class MyThingEditor : UnityEditor.Editor { // I forgot why I fully qualify this, but there was some reason
    7.   public override void OnInspectorGUI() {
    8.     base.OnInspectorGUI();
    10.     var target = (MyThing);
    12.     if(GUILayout.Button( ... )) {
    13.       target.Something();
    14.     }
    15.   }
    17. }
    Very simple stuff.

    I have made a lot of handy tools when I make proofs of concept or want to test something out. In everyday monkey-business of mine I typically desire some functionalities but not entire custom editors. For this I've made a SceneViewPanel, like a floater which can show data and buttons as a live console (edit: well not a console, but a panel, as the name already suggests, I'm a dummy), very easily customizable, living in the scene view itself, and I intend to release that as a tutorial in a couple of days.
    Last edited: Oct 17, 2022
  3. TomatoDit


    Nov 7, 2015
    I am aware that I can create custom button, the problem is more in the representation of the data from derivated class and what function should I call to display what I want.

    For the example, I just added a string spellName to Spell class, when I add an element to the SpellBook, only the field spellName is serialized. This is due to the fact by Spell -> Effects is a list of abstract elements.
    (I forgot to mention I add [Serializable] above Spell and Effect classes)

    If I remove the abstract from Effect, of course the effect list now appear but I will only have non-specific elements although all my Effect are different with different fields

    The thing I want is to be able to add to the spell a new Effect and selecting the right derivatedClass when I add the Effect. As mentioned in the first post, it is ok to me to add a non-specific Effect first then move a field (Enum or other) and its type and fields are updated immediately after.

    For example something like this :
  4. orionsyndrome


    May 4, 2014
    So you basically want a custom list inspector. I've done that before. If your entire editor is custom you can get away without having a dedicated property drawer. If not, here's a tutorial on custom lists, but that's before Unity introduced the little +/- tabs and I don't really know how to do that yet. For the little selection menu, you can do a Popup, and in fact, everything you do here is reasonable and doable, you just have to expect wasting a lot of time making it.

    Earlier this year I've made several editors for very simple administration of some damage types that also allows testing them; just simple ScriptableObject editors but with some logic on top, to funnel the author into certain constraints. The logic and the editor code itself was very simple, but you wouldn't believe the size of the library that drove it. I'm not claiming the size of it has to be that big, but that's how anything custom in Unity works, just prepare for a lot of mineshaft work when it comes to serialized properties, GUI styles, formatting, enabling undo, multi-selection, overriding typical behavior, and everything else that doesn't pop to mind when you think about the logic you'd like to actually work on.

    That's partially the reason behind my first post, I'm kind of trying to talk you out, in between the lines, or at least allow you to consider some easier ways out, shortcuts even, because unless you're paid to do this, there is a real potential you'll burn yourself out. Editors are hardcore to make properly and you need at least a year of working with them to get a good grip on them and build up any confidence.

    Anyway, what you're trying to achieve is pretty normal.
    TomatoDit likes this.
  5. TomatoDit


    Nov 7, 2015
    I totally understand what I have to do, I thought I could avoid recoding a list but I will be able to custom it to my needs !
    I understood what you told between the lines but I was not sure to be clear but I was indeed :)
    orionsyndrome likes this.
  6. spiney199


    Feb 11, 2021
    Also worth noting you should be using SerializeReference to serialise polymorphic classes, rather than regular SerializeField.
    orionsyndrome and TomatoDit like this.
  7. akareactor


    Apr 6, 2015
    Have you achieved any success in this way? I got to exactly the same problem