Skip to content

Instantly share code, notes, and snippets.

@ZimM-LostPolygon
Last active June 3, 2024 07:38
Show Gist options
  • Save ZimM-LostPolygon/7e2f8a3e5a1be183ac19 to your computer and use it in GitHub Desktop.
Save ZimM-LostPolygon/7e2f8a3e5a1be183ac19 to your computer and use it in GitHub Desktop.
Unity asset GUIDs regenerator
// Drop into Assets/Editor, use "Tools/Regenerate asset GUIDs"
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using UnityEditor;
namespace UnityGuidRegenerator {
public class UnityGuidRegeneratorMenu {
[MenuItem("Tools/Regenerate asset GUIDs")]
public static void RegenerateGuids() {
if (EditorUtility.DisplayDialog("GUIDs regeneration",
"You are going to start the process of GUID regeneration. This may have unexpected results. \n\nMAKE A PROJECT BACKUP BEFORE PROCEEDING!",
"Regenerate GUIDs", "Cancel")) {
try {
AssetDatabase.StartAssetEditing();
string path = Path.GetFullPath(".") + Path.DirectorySeparatorChar + "Assets";
UnityGuidRegenerator regenerator = new UnityGuidRegenerator(path);
regenerator.RegenerateGuids();
}
finally {
AssetDatabase.StopAssetEditing();
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
}
}
}
}
internal class UnityGuidRegenerator {
private static readonly string[] kDefaultFileExtensions = {
"*.meta",
"*.mat",
"*.anim",
"*.prefab",
"*.unity",
"*.asset",
"*.guiskin",
"*.fontsettings",
"*.controller",
};
private readonly string _assetsPath;
public UnityGuidRegenerator(string assetsPath) {
_assetsPath = assetsPath;
}
public void RegenerateGuids(string[] regeneratedExtensions = null) {
if (regeneratedExtensions == null) {
regeneratedExtensions = kDefaultFileExtensions;
}
// Get list of working files
List<string> filesPaths = new List<string>();
foreach (string extension in regeneratedExtensions) {
filesPaths.AddRange(
Directory.GetFiles(_assetsPath, extension, SearchOption.AllDirectories)
);
}
// Create dictionary to hold old-to-new GUID map
Dictionary<string, string> guidOldToNewMap = new Dictionary<string, string>();
Dictionary<string, List<string>> guidsInFileMap = new Dictionary<string, List<string>>();
// We must only replace GUIDs for Resources present in Assets.
// Otherwise built-in resources (shader, meshes etc) get overwritten.
HashSet<string> ownGuids = new HashSet<string>();
// Traverse all files, remember which GUIDs are in which files and generate new GUIDs
int counter = 0;
foreach (string filePath in filesPaths) {
if (!EditorUtility.DisplayCancelableProgressBar("Scanning Assets folder", MakeRelativePath(_assetsPath, filePath),
counter / (float) filesPaths.Count)) {
string contents = File.ReadAllText(filePath);
IEnumerable<string> guids = GetGuids(contents);
bool isFirstGuid = true;
foreach (string oldGuid in guids) {
// First GUID in .meta file is always the GUID of the asset itself
if (isFirstGuid && Path.GetExtension(filePath) == ".meta") {
ownGuids.Add(oldGuid);
isFirstGuid = false;
}
// Generate and save new GUID if we haven't added it before
if (!guidOldToNewMap.ContainsKey(oldGuid)) {
string newGuid = Guid.NewGuid().ToString("N");
guidOldToNewMap.Add(oldGuid, newGuid);
}
if (!guidsInFileMap.ContainsKey(filePath))
guidsInFileMap[filePath] = new List<string>();
if (!guidsInFileMap[filePath].Contains(oldGuid)) {
guidsInFileMap[filePath].Add(oldGuid);
}
}
counter++;
} else {
UnityEngine.Debug.LogWarning("GUID regeneration canceled");
return;
}
}
// Traverse the files again and replace the old GUIDs
counter = -1;
int guidsInFileMapKeysCount = guidsInFileMap.Keys.Count;
foreach (string filePath in guidsInFileMap.Keys) {
EditorUtility.DisplayProgressBar("Regenerating GUIDs", MakeRelativePath(_assetsPath, filePath), counter / (float) guidsInFileMapKeysCount);
counter++;
string contents = File.ReadAllText(filePath);
foreach (string oldGuid in guidsInFileMap[filePath]) {
if (!ownGuids.Contains(oldGuid))
continue;
string newGuid = guidOldToNewMap[oldGuid];
if (string.IsNullOrEmpty(newGuid))
throw new NullReferenceException("newGuid == null");
contents = contents.Replace("guid: " + oldGuid, "guid: " + newGuid);
}
File.WriteAllText(filePath, contents);
}
EditorUtility.ClearProgressBar();
}
private static IEnumerable<string> GetGuids(string text) {
const string guidStart = "guid: ";
const int guidLength = 32;
int textLength = text.Length;
int guidStartLength = guidStart.Length;
List<string> guids = new List<string>();
int index = 0;
while (index + guidStartLength + guidLength < textLength) {
index = text.IndexOf(guidStart, index, StringComparison.Ordinal);
if (index == -1)
break;
index += guidStartLength;
string guid = text.Substring(index, guidLength);
index += guidLength;
if (IsGuid(guid)) {
guids.Add(guid);
}
}
return guids;
}
private static bool IsGuid(string text) {
for (int i = 0; i < text.Length; i++) {
char c = text[i];
if (
!((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'z'))
)
return false;
}
return true;
}
private static string MakeRelativePath(string fromPath, string toPath) {
Uri fromUri = new Uri(fromPath);
Uri toUri = new Uri(toPath);
Uri relativeUri = fromUri.MakeRelativeUri(toUri);
string relativePath = Uri.UnescapeDataString(relativeUri.ToString());
return relativePath;
}
}
}
@mpfaff
Copy link

mpfaff commented Mar 24, 2020

I'm facing an issue where after running this script, Unity scenes complain that their scripts cannot be found.

@famotril
Copy link

Hi, my projects do not have the route Assets/Editor, where it is supposed to be copied this script into my project?

@MostHated
Copy link

MostHated commented Jul 27, 2020

@famo

Hi, my projects do not have the route Assets/Editor, where it is supposed to be copied this script into my project?

You just create the folder if it does not exist and then put it in there. Right-click in the project => Create new folder => name it Editor.

@aakwewaanaqa
Copy link

aakwewaanaqa commented Oct 27, 2020

I found my unity project at 2019.4.0f1 Guid didn't have alphabet characters bigger and equal to 'g'.
Maybe it doesn't have to check Guid character from g to z, in the function of IsGuid(string text).

From

        private static bool IsGuid(string text)
        {
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                if (
                    !((c >= '0' && c <= '9') ||
                      (c >= 'a' && c <= 'z'))
                    )
                    return false;
            }

            return true;
        }

to

        private static bool IsGuid(string text)
        {
            for (int i = 0; i < text.Length; i++)
            {
                char c = text[i];
                if (
                    !((c >= '0' && c <= '9') ||
                      (c >= 'a' && c <= 'f'))
                    )
                    return false;
            }

            return true;
        }

@inertiave
Copy link

need assembly definition implementation. asmdef didn't be replaced to new guid

@onur-bakis
Copy link

THAAAAAAAAAAAAAAAAAAANKKSSSSSSSSSSSSSSSSSSSSSSSSSS !!!!!!!!!!!!!!!!!!!

@Devaniti
Copy link

Devaniti commented Oct 5, 2022

Still works as expected in Unity 2021 👍

@UmarHussain149
Copy link

I'm facing an issue where after running this script, Unity scenes complain that their scripts cannot be found . Unity version 2021.3.11f

@hoangledev
Copy link

Perfect!! It worked for me. Thanks a lot!

@magmukendi
Copy link

Thanks for sharing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment