C# unity custom editor multiple different components but same base class

I’m making a strategy game , there are a lot of different space ship type , but all base on one abstract class .

public abstract class SpaceShip : MonoBehaviour , IHPSystem

example :

public class BasicShip : SpaceShip

public class CaptureShip : SpaceShip

public class EnergyBlasterShip : SpaceShip

I created a custom editor for SpaceShip

    [CustomEditor(typeof(SpaceShip) , true)]
    [CanEditMultipleObjects]
    public class SpaceShipCustomEditor : Editor
    {

        public override void OnInspectorGUI()
        {
            EditorGUILayout.HelpBox("This is a custom editor. If anything gone wrong , please go to non custom editor and try.", MessageType.Info);

            // . . . . . 


        }



    }

But when i select two different type of space ship , my custom editor said :

components that are only on some of the selected objects cannot be multi-edited , 

How can i edit different space ship simultaneously ? I only want to edit fields on the base class , not the fields of inherit class.

That’s not possible. Unity uses the SerializedObject class which represents the serialized data of a class instance or multiple instances. However when you have two ore more different classes they can’t be edited together as their serialized data might not match.

Unity actually encourages to use object composition over inheritance. That means you could create a general SpaceShip class and each ship type will have that component attached. Any ship-type specific behaviour would simply be in a seperate behaviour. That would mean your “CaptureShip” class would also just derive from MonoBehaviour and you would attach it next to the SpaceShip behaviour.

Inheritance limit the flexibility and straight-jacket you. You should focus on actual “features” you want to implement. For example if you implement a “CaptureAbility” behaviour you can attach this component to any ship and give it the ability to capture other ships. Just think about a car. You can attach a tow hitch to a car so it now has the ability to tow a trailer. Using inheritance in such a case makes not much sense. Having a “Car” class and a derived “Cabriolet” and a derived “TwoCar” class would make it impossible to have a cabriolet with a tow hitch. If features are seperated into behaviours that’s not a problem. The functionality of a cabriolet would just be encapsulated by a RagTop behaviour and the tow hitch has it’s own behaviour. For certain management purposes those behaviours could implement interfaces when you need to handle certain aspects into a generic manner.

Simply use inheritance:

	[CustomEditor(typeof(MonoA))]
    public class MyCustomEditorA : Editor
    {
        public override void OnInspectorGUI()
        {
            // Your codes
        }
    }

    [CustomEditor(typeof(MonoB))]
    public class MyCustomEditorB : MyCustomEditorA
    {
    }

In your case i would use RequireComponent like so:
.
.
.
[ Scripts folder ]

// c#
using UnityEngine;

interface IHPSystem {
    void Hit( int damage );
}

public class SpaceShipSuper : MonoBehaviour
{
    public int speed = 333;        
    public Color color = Color.red;
}

[RequireComponent( typeof( SpaceShipSuper ) )]
public abstract class SpaceShip : MonoBehaviour, IHPSystem
{
    public int health = 200;

    public void Hit( int damange ) 
    {
        this.health -= damange;
    }
    public void Apply()
    {
        SpaceShipSuper superS = GetComponent<SpaceShipSuper>();
        GetComponent<Renderer>().material.color = superS.color;
    }
}

public class SphereShip : SpaceShip {}
public class CubeShip : SpaceShip {}
public class CapsuleShip : SpaceShip {}

[ Editor folder ]

using UnityEditor;
using UnityEngine;
using System.Collections.Generic;

// Hide sub class inspector fields 
[CustomEditor(typeof(SpaceShip) , true)]
public class SpaceShipCustomEditor : Editor
{
    public override void OnInspectorGUI() { }
}

[CustomEditor(typeof(SpaceShipSuper) , true)]
[CanEditMultipleObjects]
public class SpaceShipSuperCustomEditor : Editor
{
    List<SpaceShip> spaceShips;
    List<SpaceShipSuper> superSpaceShips;

    public void OnEnable()
    {
        spaceShips = new List<SpaceShip>();
        superSpaceShips = new List<SpaceShipSuper>(); 

        foreach( var target in targets )
        {
            SpaceShipSuper superS = ( SpaceShipSuper ) target;
            SpaceShip s = superS.GetComponent<SpaceShip>();

            spaceShips.Add( s );
            superSpaceShips.Add( superS );
        }
    }

    public override void OnInspectorGUI() 
    {
        if( this.DrawDefaultInspector() )
        {
            for( var i = 0; i < spaceShips.Count; ++i )
            {
                SpaceShipSuper superS = superSpaceShips[ i ];
                SpaceShip s = spaceShips[ i ];

                // #1 update in editor ( my prefered method )
                UpdateBaseClass( superS, s );

                // #2 Or update inside space ship class
                // s.Apply();
            }
        }
    }

    void UpdateBaseClass( SpaceShipSuper superS, SpaceShip s )
    {
        // regular variables like `SpaceShipSuper.speed` are updated automaticlly
        
        // do manual update if necessery 
        s.GetComponent<Renderer>().material.color = superS.color;
    }
}

Not sure what’s your problem but I think that in your OnInspectorGUI() there should be something like this…

public override void OnInspectorGUI ()
{
	// you missed this.
	var editedObject = (SpaceShip)target;
	
	// the rest of your code...

}