Can I duplicate folder with internal dependencies between objects?

Hi guys!

I have a folder with some prefabs, materials, textures and etc. Prefabs refers to textures in this folder.
Can I clone this folder with saving local dependencies?
For example:
Folder is named “Assets/BaseFolder”. And prefab “Assets/BaseFolder/preFab” refers to “Assets/BaseFolder/texture”.
I need to Copy “Assets/BaseFolder” to “Assets/CloneFolder”, so that “Assets/CloneFolder/preFab” refer to “Assets/CloneFolder/texture”.

By default, when I dublicate folder - dependencies refers to the old resources with old paths.

I can do it manually in editor for every object. But I have many objects and many dependencies, and I need to automate this process.

Does anyone have any ideas?

Thanks for any answer,
Vlad

I’ve just had the same problem. In 22 years of industry, that’s the first time I see a program where you duplicate something and the internal dependencies aren’t properly redirected to the adequate duplicates.! I considered writing something in Unity for a while, but I ended up deciding for an easier (although not elegant) hack that will do the trick, as long as you’re using visible meta files as the version control mode.

I wrote a python script that changes the guids of the assets as well as the internal references in materials and prefabs. I’m using python 3.5 on this one, but it should work on later versions as well. Have a back-up of the project folder, for I’ve tested it enough for my own personal use only.

>> USE AT YOUR OWN RISK <<

Usage: python scriptname.py -i foldername

I pulled a copy of the folder I wanted to duplicate out of the project, renamed it and ran the script. As it doesn’t affect guid references that are not found inside the folder itself, when you move the renamed folder to the project , Unity correctly matches the external references. :slight_smile:

Just to make sure you’ve read it: >> USE AT YOUR OWN RISK <<

import fnmatch
import os
import argparse
import uuid

# PARAMETERS
parser = argparse.ArgumentParser(description='Rearrange a Unity\'s project folder guids.')
parser.add_argument('-i, --input',  metavar='FOLDER', type=str, nargs=1, required=True, dest='input_folder',
                    help='input folder')
args = parser.parse_args()
input_folder = None if args.input_folder is None else args.input_folder[0]
if not os.path.isdir(input_folder):
    print('ERROR: Path not found.')
    quit(1)

# SCAN FILES

register_list = []
print('')
metas = dict()
mats = []
prefabs = []
for root, folders, files in os.walk(input_folder):
    for fname in files:
        if fnmatch.fnmatch(fname, '*.meta'):
            metaname = root + os.sep + fname
            file = open(metaname, 'r')
            for line in file:
                elements = line.split(': ')
                if elements[0] == 'guid':
                    id = elements[1].replace('

', ‘’)
metas.update({id: [metaname, uuid.uuid4().hex]})
file.close()
if fnmatch.fnmatch(fname, ‘.mat’):
mats += [root + os.sep + fname]
if fnmatch.fnmatch(fname, '
.prefab’):
prefabs += [root + os.sep + fname]

# CHANGE METAS

for k, v in metas.items():
    file = open(v[0], 'r')
    met = file.readlines()
    for i, l in enumerate(met):
        if 'guid:' in l:
            met *= l.replace(k, v[1])*

file.close()
file = open(v[0], ‘w’)
file.writelines(met)
file.close()

#CHANGE PREFABS AND MATERIALS

for pname in prefabs + mats:
file = open(pname, ‘r’)
changed = False
prefab = file.readlines()
for i, l in enumerate(prefab):
if ‘guid:’ in l:
key = None
for k in metas.keys():
if k in l:
key = k
changed = True
if key is not None:
prefab = l.replace(key, metas[key][1])
print(prefab*, end=‘’)*
print(key, ‘->’, metas[key][1])
else:
print(prefab*)*
file.close()
if changed:
file = open(pname, ‘w’)
file.writelines(prefab)
file.close()

My solution:

-I copy the directory
-I recursively retrieve the guid of all the files contained in the .meta of the destination folder
-I generate a correspondence table between the original guid (that I just got) and the new ones that I get thanks to GUID.Generate
-I replace the original guid with the new ones in all files

This procedure allows you to simply perform a deep copy of an entire folder.

This code can surely be improved and optimized, but it is functional.

        public static void CopyDirectoryDeep(string sourcePath, string destinationPath)
        {
            CopyDirectoryRecursively(sourcePath, destinationPath);

            List<string> metaFiles = GetFilesRecursively(destinationPath, (f) => f.EndsWith(".meta"));
            List<(string originalGuid, string newGuid)> guidTable = new List<(string originalGuid, string newGuid)>();

            foreach (string metaFile in metaFiles)
            {
                StreamReader file = new StreamReader(metaFile);
                file.ReadLine();
                string guidLine = file.ReadLine();
                file.Close();
                string originalGuid = guidLine.Substring(6, guidLine.Length - 6);
                string newGuid = GUID.Generate().ToString().Replace("-", "");
                guidTable.Add((originalGuid, newGuid));
            }

            List<string> allFiles = GetFilesRecursively(destinationPath);

            foreach (string fileToModify in allFiles)
            {
                string content = File.ReadAllText(fileToModify);

                foreach (var guidPair in guidTable)
                {
                    content = content.Replace(guidPair.originalGuid, guidPair.newGuid);
                }

                File.WriteAllText(fileToModify, content);
            }

            AssetDatabase.Refresh();
        }

        private static void CopyDirectoryRecursively(string sourceDirName, string destDirName)
        {
            DirectoryInfo dir = new DirectoryInfo(sourceDirName);

            DirectoryInfo[] dirs = dir.GetDirectories();

            if (!Directory.Exists(destDirName))
            {
                Directory.CreateDirectory(destDirName);
            }

            FileInfo[] files = dir.GetFiles();
            foreach (FileInfo file in files)
            {
                string temppath = Path.Combine(destDirName, file.Name);
                file.CopyTo(temppath, false);
            }

            foreach (DirectoryInfo subdir in dirs)
            {
                string temppath = Path.Combine(destDirName, subdir.Name);
                CopyDirectoryRecursively(subdir.FullName, temppath);
            }
        }

        private static List<string> GetFilesRecursively(string path, Func<string, bool> criteria = null, List<string> files = null)
        {
            if (files == null)
            {
                files = new List<string>();
            }

            files.AddRange(Directory.GetFiles(path).Where(f =>criteria == null || criteria(f)));

            foreach (string directory in Directory.GetDirectories(path))
            {
                GetFilesRecursively(directory, criteria, files);
            }

            return files;
        }

sounds like you need to write arrays or file strings to reference many prefabs, because otherwise they just save with scene views if you save them in the player. you have to copy them with unity explorer window and then they all have file names, prefab names texture names. a texture should refer to a variable or an image file. you have to be familiar with accessing prefabs in unity. straings can code file names, you can add them togther in loops to deal with just one string being different in batches of prefabs.

the example with quotes was confusing… texture isnt prefab?

There is many unity objects, who refer to another.

It is not only prefabs. For example, Animator controller refer to animations. Scene refer to prefabs. Prefab refer to textures, materials, etc. I can change these links in unity editor (refer to objects with same names, but in another folder), manually, each individually. But I don’t know how do that for multiple objects in editor, or script.

When I copy prefab and texture to another folder - it refer to texture from old folder. I need, that prefab refer to texture in new folder.

I am not sure, what you mean. Are you propose to make connections between objects through “Resources” class or analog?