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.

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 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.

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);
	}

}