Fold/unfold gameobject from code

Is there some way to do something like

Selection.unfold = true;

Or something like that? I basically want to, in code, unfold my currently selected gameobject (it would have children of course).

Cheers!

Here you go :slight_smile:

public static void Collapse(GameObject go, bool collapse)
{
	// bail out immediately if the go doesn't have children
	if (go.transform.childCount == 0) return;

	// get a reference to the hierarchy window
	var hierarchy = GetFocusedWindow("Hierarchy");

	// select our go
	SelectObject(go);

	// create a new key event (RightArrow for collapsing, LeftArrow for folding)
	var key = new Event { keyCode = collapse ? KeyCode.RightArrow : KeyCode.LeftArrow, type = EventType.keyDown };

	// finally, send the window the event
	hierarchy.SendEvent(key);
}

public static void SelectObject(Object obj)
{
	Selection.activeObject = obj;
}

public static EditorWindow GetFocusedWindow(string window)
{
	FocusOnWindow(window);
	return EditorWindow.focusedWindow;
}

public static void FocusOnWindow(string window)
{
	EditorApplication.ExecuteMenuItem("Window/" + window);
}

You might ask, why focus on the hierarchy window, and then select the go? Well, giving focus to the hierarchy window is the only way I found to get a reference to it, If there was an easier way, I would go for it.

You can also find a child of the object you want to expand, and ping it using

EditorGUIUtility.PingObject(go) 

This will expand everything for you.

For those who are still trying to find a good solution, based on a lot of research, I’ve created a utility with these methods:

- IsExpanded(GameObject go)
- GetExpandedGameObjects()
- SetExpanded(GameObject go, bool expand)
- SetExpandedRecursive(GameObject go, bool expand)

You can find my script here, I hope you guys make good use of it:

You now have to use EditorCoroutines.

Here is the result of what you can do with this:

Firstly, install:

  1. Window > Package Manager Download
  2. Editor, Coroutines Package

Here is what worked for me. You call it like this:

EditorWindowControl.EditorCoroutineUtility.StartCoroutineOwnerless(CollapseObjectInHierarchy(myObjectNameInScene, false);

Then here are the two scripts needed:

using UnityEditor;
using UntiyEngine;
using Unity.EditorCoroutines.Editor;

public class EditorWindowControl {

	private static IEnumerator CollapseObjectInHierarchyWindow(string gameObjectName, bool collapse)
	{
		GameObject obj = GameObject.Find(gameObjectName);

		if (obj != null)
		{
			yield return EditorCoroutineUtility.StartCoroutineOwnerless(CollapseInHierarchyWindow(obj, collapse));
		}
		else
		{
			EditorUtility.DisplayDialog("Error", "No '" + gameObjectName + "' GameObject in Scene!", "OK");
		}

		yield return true;
	}

	private static IEnumerator CollapseInHierarchyWindow(GameObject go, bool collapse)
	{

		FocusUnityEditorWindow(SelectWindowType.Hierarchy);

		yield return new EditorWaitForSeconds(0.05f);

		Selection.activeGameObject = null;

		while (Selection.activeGameObject != null)
			yield return false;

		Selection.activeGameObject = go;

		while (Selection.activeGameObject != go)
			yield return false;

		if (collapse == false)
		{
			SimulatePCControl.RightArrow();
		}
		else
		{
			SimulatePCControl.LeftArrow();
		}

		yield return true;
	}
	
	
	private enum SelectWindowType { Inspector, ProjectBrowser, Game, Console, Hierarchy, Scene };

	private static IEnumerator FocusUnityEditorWindow(SelectWindowType swt)
	{
		System.Type unityEditorWindowType = null;
		EditorWindow editorWindow = null;

		switch (swt)
		{
			case SelectWindowType.Inspector:
				unityEditorWindowType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.InspectorWindow");
				break;
			case SelectWindowType.ProjectBrowser:
				unityEditorWindowType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.ProjectBrowser");
				break;
			case SelectWindowType.Game:
				unityEditorWindowType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.GameView");
				break;
			case SelectWindowType.Console:
				unityEditorWindowType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.ConsoleView");
				break;
			case SelectWindowType.Hierarchy:
				unityEditorWindowType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow");
				break;
			case SelectWindowType.Scene:
				unityEditorWindowType = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.SceneView");
				break;
		}

		editorWindow = EditorWindow.GetWindow(unityEditorWindowType);

		while (editorWindow == null)
		{
			yield return false;
		} 

		yield return true;

	}
	
}

Also make a file for this class:

public class SimulatePCControl
{

	[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
	public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
	//Mouse actions

	[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
	public static extern void keybd_event(uint bVk, uint bScan, uint dwFlags, uint dwExtraInfo);

	[DllImport("user32.dll")]
	[return: MarshalAs(UnmanagedType.Bool)]
	internal static extern bool GetCursorPos(ref Win32Point pt);

	[StructLayout(LayoutKind.Sequential)]
	internal struct Win32Point
	{
		public Int32 X;
		public Int32 Y;
	};
	public static Vector2 GetMousePosition()
	{
		Win32Point w32Mouse = new Win32Point();
		GetCursorPos(ref w32Mouse);
		return new Vector2(w32Mouse.X, w32Mouse.Y);
	}

	private const int MOUSEEVENTF_LEFTDOWN = 0x02;
	private const int MOUSEEVENTF_LEFTUP = 0x04;
	private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
	private const int MOUSEEVENTF_RIGHTUP = 0x10;
	private const int MOUSEEVENTF_MIDDLEDOWN = 0x20;
	private const int MOUSEEVENTF_MIDDLEUP = 0x40;

	[DllImport("user32.dll")]
	static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);

	[DllImport("user32.dll")]
	public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

	public enum ClickType { Left, Middle, Right }

	public static void Click(ClickType clickType, int _X, int _Y)
	{
		//Call the imported function with the cursor's current position
		uint X = (uint)_X;
		uint Y = (uint)_Y;

		switch (clickType)
		{
			case ClickType.Left:
				mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, X, Y, 0, 0);
				break;
			case ClickType.Middle:
				mouse_event(MOUSEEVENTF_MIDDLEDOWN | MOUSEEVENTF_MIDDLEUP, X, Y, 0, 0);
				break;
			case ClickType.Right:
				mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, X, Y, 0, 0);
				break;
		}
		
	}

	public static IEnumerator DoubleClick(ClickType clickType)
	{
		switch (clickType)
		{
			case ClickType.Left:
				Click(ClickType.Left, (int)GetMousePosition().x, (int)GetMousePosition().y);
				yield return new EditorWaitForSeconds(0.1f);
				Click(ClickType.Left, (int)GetMousePosition().x, (int)GetMousePosition().y);
				break;
			case ClickType.Middle:
				Click(ClickType.Middle, (int)GetMousePosition().x, (int)GetMousePosition().y);
				yield return new EditorWaitForSeconds(0.1f);
				Click(ClickType.Middle, (int)GetMousePosition().x, (int)GetMousePosition().y);
				break;
			case ClickType.Right:
				Click(ClickType.Right, (int)GetMousePosition().x, (int)GetMousePosition().y);
				yield return new EditorWaitForSeconds(0.1f);
				Click(ClickType.Right, (int)GetMousePosition().x, (int)GetMousePosition().y);
				break;
		}
	}

	private const int VK_LEFT   = 0x25;
	private const int VK_RIGHT  = 0x27;
	private const int VK_UP     = 0x26;
	private const int VK_DOWN   = 0x28;

	public static void Key(string keyName)
	{
		uint keyCode = 0x00;

		switch (keyName.ToLower())
		{
			case "a":
				keyCode = 0x41;
				break;
			case "b":
				keyCode = 0x42;
				break;
			case "c":
				keyCode = 0x43;
				break;
			case "d":
				keyCode = 0x44;
				break;
			case "e":
				keyCode = 0x45;
				break;
			case "f":
				keyCode = 0x46;
				break;
			case "g":
				keyCode = 0x47;
				break;
			case "h":
				keyCode = 0x48;
				break;
			case "i":
				keyCode = 0x49;
				break;
			case "j":
				keyCode = 0x4A;
				break;
			case "k":
				keyCode = 0x4B;
				break;
			case "l":
				keyCode = 0x4C;
				break;
			case "m":
				keyCode = 0x4D;
				break;
			case "n":
				keyCode = 0x4E;
				break;
			case "o":
				keyCode = 0x4F;
				break;
			case "p":
				keyCode = 0x50;
				break;
			case "q":
				keyCode = 0x51;
				break;
			case "r":
				keyCode = 0x52;
				break;
			case "s":
				keyCode = 0x53;
				break;
			case "t":
				keyCode = 0x54;
				break;
			case "u":
				keyCode = 0x55;
				break;
			case "v":
				keyCode = 0x56;
				break;
			case "w":
				keyCode = 0x57;
				break;
			case "x":
				keyCode = 0x58;
				break;
			case "y":
				keyCode = 0x59;
				break;
			case "z":
				keyCode = 0x5A;
				break;
			default:
				Debug.Log("INVALID KEYNAME: " + keyName);
				return;
		}
		
		keybd_event(keyCode, 0, 0, 0);
		keybd_event(keyCode, 0, 0x0002, 0);

	}

	public const int VK_CONTROL = 0x11; //Control key code
	public const int KEYEVENTF_KEYUP = 0x0002; //Key up flag
	public const int VK_CONTROLKEY = 0x11; //Control key code
	public const int Z = 0x5A;

	public static void Shortcuts_Undo()
	{
		keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
		keybd_event(Z, 0, KEYEVENTF_KEYUP, 0);

		keybd_event(VK_CONTROL, 0x9e, 0, 0);
		keybd_event(Z, 0x9e, 0, 0);
		keybd_event(Z, 0x9e, KEYEVENTF_KEYUP, 0);
		keybd_event(VK_CONTROL, 0x9e, KEYEVENTF_KEYUP, 0);
	}

	public static void LeftArrow()
	{
		keybd_event(VK_LEFT, 0, 0, 0);
		keybd_event(VK_LEFT, 0, 0x0002, 0);
	}

	public static void RightArrow()
	{
		keybd_event(VK_RIGHT, 0, 0, 0);
		keybd_event(VK_RIGHT, 0, 0x0002, 0);
	}

	public static void UpArrow()
	{
		keybd_event(VK_UP, 0, 0, 0);
		keybd_event(VK_UP, 0, 0x0002, 0);
	}

	public static void DownArrow()
	{
		keybd_event(VK_DOWN, 0, 0, 0);
		keybd_event(VK_DOWN, 0, 0x0002, 0);
	}

}