Skip to content

Instantly share code, notes, and snippets.

@Jakob-PB
Last active February 8, 2019 09:18
Show Gist options
  • Save Jakob-PB/b04fbbedb066fb3974b27201dc1cbb3c to your computer and use it in GitHub Desktop.
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.
/**
* 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