Undo SetSiblingIndex for root objects?

I’ve created a script to naturally sort gameobjects by their name in the hierarchy, and I cannot seem to figure out how to use the built-in Undo system when root objects have SetSiblingIndex called on them.

Here’s what I’ve got so far:

using UnityEditor;
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
 
namespace Zapdot
{
    public static class EditorTools
    {
        [MenuItem("Tools/Utility/Sort Hierarchy #h", false)]
        public static void SortObjects()
        {
            List<Transform> objsToSort = new List<Transform>();
            Transform rootParent = null;
            int minIndex = int.MaxValue;
            if (Selection.transforms.Length > 1)
            {
                rootParent = Selection.transforms[0].parent;
                foreach (Transform t in Selection.transforms)
                {
                    minIndex = Mathf.Min(minIndex, t.GetSiblingIndex());
                    objsToSort.Add(t);
                }
                if (rootParent != null)
                    Undo.RegisterCompleteObjectUndo(rootParent, "Sort Hierarchy");
                else
                    Undo.RegisterCompleteObjectUndo(objsToSort.ToArray(), "Sort Hierarchy");
            }
            else if (Selection.activeTransform.childCount > 0)
            {
                // since we're sorting all of the children, our minIndex would be 0.
                minIndex = 0;
                rootParent = Selection.activeTransform;
                for (int i = 0; i < rootParent.childCount; ++i)
                    objsToSort.Add(rootParent.GetChild(i));
                Undo.RegisterCompleteObjectUndo(rootParent, "Sort Hierarchy");
            }
 
            objsToSort.Sort(new NaturalNameComparer());
            for (int i = 0; i < objsToSort.Count; ++i)
                objsToSort*.SetSiblingIndex(minIndex + i);*

}
[MenuItem(“Tools/Utility/Sort Hierarchy #h”, true)]
public static bool SortObjectsValidate()
{
return Selection.transforms.Length > 1 || (Selection.transforms.Length == 1 && Selection.activeTransform.childCount > 1);
}
}

public class NaturalNameComparer : IComparer
{
private static readonly Regex _re = new Regex(@“(?<=\D)(?=\d)|(?<=\d)(?=\D)”);

public int Compare(Transform obj1, Transform obj2)
{
string x = obj1.name.ToLower();
string y = obj2.name.ToLower();
if (string.Compare(x, 0, y, 0, Mathf.Min(x.Length, y.Length)) == 0)
{
if (x.Length == y.Length)
return 0;
return x.Length < y.Length ? -1 : 1;
}
var a = _re.Split(x);
var b = _re.Split(y);
int i = 0;
while (true)
{
int r = PartCompare(a_, b*);
if (r != 0)
return r;
++i;
}
}*_

private static int PartCompare(string x, string y)
{
int a, b;
if (int.TryParse(x, out a) && int.TryParse(y, out b))
return a.CompareTo(b);
return x.CompareTo(y);
}
}
}
To reproduce:
* Add this editor script to an empty project
* Create two gameobjects as root objects in a scene, “b” and “a”
* Select them both
* Run the function (shift h)
* Try to undo – the objects don’t reorder themselves to their original position.

I am doing something similar (SetAsLastSibling, etc.), and I found a way that worked for me was for every Transform I was modifying the sibling index for, call Undo.SetTransformParent (t, t.parent, “Name of group operation”) before making the change (Unity takes a current snapshot then diffs at the end of frame); Unity doc here: Unity - Scripting API: Undo.SetTransformParent. I was able to undo 4 objects at once, from one Undo using this. I think Unity treats these sibling index changes as parent changes, which according to the Unity docs, isn’t supported by the typical Undo “record”/“register” functions. I hope this severely late info helps someone. :slight_smile: