Custom Inspector

To create a custom inspector, you can

// Declare type of Custom Editor
[CustomEditor(typeof(TankController))]
public class TankControllerEditor : Editor 
{
    // OnInspector GUI is called whenever the object is selected or changed
    public override void OnInspectorGUI()
    {
        base.DrawDefaultInspector(); // shows the default editor
    }
}

Elements

Button

Docu

if (GUILayout.Button("This is my button"))
{
    // Do something
}

Updating Fields

There are various ways, some more hacky but easier ones.

To serialize data you should use SerializedObject and SerializedProperty. This functionalities make sure you have a prober undo-state and flush all changes out. Example for Arrays:

[CreateAssetMenu(fileName = "SerializedArrayTester", menuName = "DEBUG")]
public class SerializedArrayTester : ScriptableObject
{
    [SerializeField]
    public float[] speed = {0f, 1f, 2f, 3f};
}

[CustomEditor(typeof(SerializedArrayTester))]
public class SerializedArrayTesterEditor : Editor
{
    SerializedProperty m_Speed;

    public override void OnInspectorGUI()
    {
        serializedObject.Update();
        m_Speed = serializedObject.FindProperty("speed");
        for (int x = 0; x < m_Speed.arraySize; x++) 
        {
            // get array element at x
            SerializedProperty property = m_Speed.GetArrayElementAtIndex(x); 
            // Edit this element's value.
            // in this case limit the float's value to a positive value.
            property.floatValue = Mathf.Max (0,property.floatValue); 
        }
        // draw property with it's children
        EditorGUILayout.PropertyField(m_Speed,true);
        serializedObject.ApplyModifiedProperties();
    }
}

Dirty Mode

Add an Undo.RecordObject action before changing any values. This will mark everything dirty and add an undo state:

EditorGUI.BeginChangeCheck();
float areaOfEffect = SomeAction();
if (EditorGUI.EndChangeCheck())
{
    // Like this:
    Undo.RecordObject(target, "Changed Area Of Effect");
    t.areaOfEffect = areaOfEffect;
}

Very dirty Mode

In editor mode you need to set it dirty and force the asset database to save all open files:

EditorUtility.SetDirty(theAsset);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();

Tutorials

Brackeys Tutorial