Instantly share code, notes, and snippets.
Last active
February 8, 2019 09:18
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save Jakob-PB/b04fbbedb066fb3974b27201dc1cbb3c to your computer and use it in GitHub Desktop.
A small Unity editor utility for removing, restoring, or adjusting the default naming scheme for duplicate game objects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* MIT License | |
* | |
* Copyright (c) 2019 Jakob Bjerkness | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
* SOFTWARE. | |
* | |
**/ | |
using UnityEngine; | |
using UnityEditor; | |
using System.Linq; | |
using System.Collections.Generic; | |
/// <summary> | |
/// A small Unity editor utility for removing, restoring, or adjusting the default naming scheme for duplicate game objects. | |
/// </summary> | |
/// | |
public class RenameDuplicatesMenuItems | |
{ | |
/// <summary> | |
/// Stores data about a game object name along with the number of game objects with that name. | |
/// </summary> | |
/// | |
private class GameObjectName | |
{ | |
public string name; | |
public int count; | |
public GameObjectName(string name, int count) | |
{ | |
this.name = name; | |
this.count = count; | |
} | |
} | |
private static List<GameObjectName> gameObjectNameList; | |
/// <summary> | |
/// Appends a new duplication number (e.g. "GameObject (3)") for every game object in the scene based on position in the hierarchy. | |
/// ^^^ | |
/// <para> Position in the hierarchy is from top to bottom without regard for parent/child relationships. | |
/// In other words, the ordering is based on a flattened version of the hierarchy. Every copy of a game object | |
/// will have a unique number appended. </para> | |
/// </summary> | |
/// | |
[MenuItem("Tools/Recalculate Duplicate Numbering/Based On Hierarchy Position (Unique)")] | |
private static void renameDuplicates_Hierarchy() | |
{ | |
gameObjectNameList = new List<GameObjectName>(); | |
foreach (GameObject gObj in getSortedTopLevelGameObjects()) | |
{ | |
recursivelyNameDuplicates(gObj, false, false); | |
} | |
} | |
/// <summary> | |
/// Appends a new duplication number (e.g. "GameObject (3)") for every game object in the scene based on position among siblings. | |
/// ^^^ | |
/// <para> Position among siblings is from top to bottom relative to their parent. Compared to the | |
/// "Based On Hierarchy Position" option this method may create several game objects with | |
/// the same duplication number in the scene. However, sibling game objects will always be | |
/// uniquely numbered. </para> | |
/// </summary> | |
/// | |
[MenuItem("Tools/Recalculate Duplicate Numbering/Based On Sibling Position")] | |
private static void renameDuplicates_Sibling() | |
{ | |
gameObjectNameList = new List<GameObjectName>(); | |
foreach (GameObject gObj in getSortedTopLevelGameObjects()) | |
{ | |
recursivelyNameDuplicates(gObj, false, true); | |
} | |
} | |
/// <summary> | |
/// Removes the duplication numbering (e.g. "GameObject (3)") from every game object in the scene. | |
/// ^^^ | |
/// </summary> | |
/// | |
[MenuItem("Tools/Remove Duplicate Numbering")] | |
private static void removeDuplicateNumbering() | |
{ | |
gameObjectNameList = new List<GameObjectName>(); | |
foreach (GameObject gObj in getSortedTopLevelGameObjects()) | |
{ | |
recursivelyNameDuplicates(gObj, true, false); | |
} | |
} | |
/// <summary> | |
/// Returns an enumerable list that contains all the top level game objects | |
/// in the hierarchy in order of their position in the hierarchy. | |
/// </summary> | |
/// | |
private static IEnumerable<GameObject> getSortedTopLevelGameObjects() | |
{ | |
var topLevelGameObjects = GameObject.FindObjectsOfType<GameObject>().Where(gameObj => gameObj.transform.parent == null); | |
return topLevelGameObjects.OrderBy(gameObj => gameObj.transform.GetSiblingIndex()).ToArray(); | |
} | |
/// <summary> | |
/// Renames duplicates according to parameter options. | |
/// </summary> | |
/// | |
private static void recursivelyNameDuplicates(GameObject parent, bool removeNumbering, bool useSiblingPosition) | |
{ | |
if (parent.transform.childCount > 0) | |
{ | |
// Add all the children of the parent to a list | |
List<GameObject> children = new List<GameObject>(); | |
foreach (Transform child in parent.transform) | |
{ | |
children.Add(child.gameObject); | |
} | |
// Sort the children list by order in the hierarchy | |
var sortedChildren = children.OrderBy(gameObj => gameObj.transform.GetSiblingIndex()).ToArray(); | |
// Decide whether to maintain a global or sibling only game object name count | |
List<GameObjectName> nameList; | |
if (useSiblingPosition) | |
{ | |
nameList = new List<GameObjectName>(); | |
} | |
else | |
{ | |
nameList = gameObjectNameList; | |
} | |
// Rename the children based on the sorted order | |
foreach (GameObject gObj in sortedChildren) | |
{ | |
// Cut the duplication number off if the game object name has one | |
if (gObj.name.EndsWith(")")) | |
{ | |
int stringIndex = gObj.name.IndexOf("("); | |
// Remove extra spaces between name and iteration number | |
for (int i = stringIndex; i > 0; i--) | |
{ | |
if (gObj.name.ElementAt(i) == ' ') | |
{ | |
stringIndex = i; | |
} | |
else | |
{ | |
if (gObj.name.ElementAt(i) == '(') continue; | |
else break; | |
} | |
} | |
gObj.name = gObj.name.Substring(0, stringIndex); | |
} | |
if (!removeNumbering) | |
{ | |
// Append the correct iteration number to the name | |
bool listContainsName = false; | |
foreach (GameObjectName gameObjectName in nameList) | |
{ | |
if (gameObjectName.name == gObj.name) | |
{ | |
gameObjectName.count += 1; | |
gObj.name += " (" + gameObjectName.count + ")"; | |
listContainsName = true; | |
} | |
} | |
if (!listContainsName) | |
{ | |
nameList.Add(new GameObjectName(gObj.name, 0)); | |
//nameList.Add(new GameObjectName(gObj.name, 1)); | |
//gObj.name += " (1)"; | |
} | |
} | |
recursivelyNameDuplicates(gObj, removeNumbering, useSiblingPosition); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment