using CC; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; namespace CC { public class CreatePrefab : MonoBehaviour { #if UNITY_EDITOR public string prefabSaveFolder = "CharacterCustomizer/CustomPrefabs"; public bool resetPose = true; public string suffix = "_Prefab"; public void createPrefab(string name, GameObject orig, string prefabSource) { string prefabFolder = Path.Combine("Assets/" + prefabSaveFolder + "/" + name); string matFolder = prefabFolder + "/Materials"; if (!Directory.Exists(prefabFolder)) Directory.CreateDirectory(prefabFolder); if (!Directory.Exists(matFolder)) Directory.CreateDirectory(matFolder); //Delete old materials string[] matFiles = Directory.GetFiles(matFolder, "*.mat", SearchOption.TopDirectoryOnly); foreach (var file in matFiles) { // Convert system path to Unity asset path string assetPath = file.Replace("\\", "/"); AssetDatabase.DeleteAsset(assetPath); } string prefabPath = prefabFolder + "/" + name + suffix + ".prefab"; //Duplicate the GO for modifying GameObject temp = Instantiate(orig); temp.name = name; //All renderers in the clone Renderer[] renderers = temp.GetComponentsInChildren(true); // Map "material key" -> duplicated asset Dictionary materialMap = new Dictionary(); foreach (var rend in renderers) { var src = rend.sharedMaterials; if (src == null || src.Length == 0) continue; var dst = new Material[src.Length]; for (int i = 0; i < src.Length; i++) { var m = src[i]; if (m == null) { dst[i] = null; continue; } string key = buildMaterialKey(m); if (!materialMap.TryGetValue(key, out Material dup)) { //Name file from the base (non-instance) name string baseName = getBaseName(m.name); string fileName = sanitizeFileName(baseName) + "_Copy.mat"; string matPath = AssetDatabase.GenerateUniqueAssetPath(matFolder + "/" + fileName); dup = new Material(m); //copies all properties/keywords/shader AssetDatabase.CreateAsset(dup, matPath); materialMap[key] = dup; } dst[i] = dup; } rend.sharedMaterials = dst; } //Remove unneeded scripts DestroyImmediate(temp.GetComponentInChildren(true)); DestroyImmediate(temp.GetComponentInChildren(true)); foreach (var modifyBone in temp.GetComponentsInChildren(true)) { DestroyImmediate(modifyBone); } foreach (var copyPose in temp.GetComponentsInChildren(true)) { DestroyImmediate(copyPose); } foreach (var blendshapeManager in temp.GetComponentsInChildren(true)) { DestroyImmediate(blendshapeManager); } foreach (var headColliders in temp.GetComponentsInChildren(true)) { foreach (var GO in headColliders.colliderObjects) { if (GO != null) DestroyImmediate(GO); } } DestroyImmediate(temp.GetComponentInChildren(true)); //Disable customization mode in physics manager var physicsManager = temp.GetComponentInChildren(); if (physicsManager != null) { physicsManager.customizing = false; }; // Reset bones back to prefab's original transforms if (resetPose && !string.IsNullOrEmpty(prefabSource)) resetBones(temp, prefabSource); // Save the prefab from the modified clone PrefabUtility.SaveAsPrefabAsset(temp, prefabPath); DestroyImmediate(temp); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"Prefab created (overwritten if existed): {prefabPath}"); } private static string buildMaterialKey(Material m) { string path = AssetDatabase.GetAssetPath(m); string shaderName = m.shader ? m.shader.name : ""; if (!string.IsNullOrEmpty(path)) { string guid = AssetDatabase.AssetPathToGUID(path); return $"GUID:{guid}|SH:{shaderName}"; } // Non-asset instance (e.g., created by renderer.material). Group by base name + shader. string baseName = getBaseName(m.name); return $"NAME:{baseName}|SH:{shaderName}"; } private static string getBaseName(string name) { const string inst = " (Instance)"; return name.EndsWith(inst) ? name.Substring(0, name.Length - inst.Length) : name; } private static string sanitizeFileName(string s) { foreach (char c in Path.GetInvalidFileNameChars()) s = s.Replace(c.ToString(), "_"); return s; } private void resetBones(GameObject go, string prefabSource) { if (string.IsNullOrEmpty(prefabSource)) { Debug.LogWarning("Prefab name is null or empty, cannot reset bones."); return; } // Load the original prefab from Resources GameObject prefab = Resources.Load(prefabSource); if (prefab == null) { Debug.LogWarning($"Could not load prefab '{prefabSource}' from Resources."); return; } // Instantiate prefab temporarily (hidden in hierarchy) GameObject prefabInstance = (GameObject)PrefabUtility.InstantiatePrefab(prefab); prefabInstance.hideFlags = HideFlags.HideAndDontSave; // Copy transforms recursively by name copyTransforms(prefabInstance.transform, go.transform); // Destroy the temporary prefab instance DestroyImmediate(prefabInstance); } private static void copyTransforms(Transform source, Transform target) { target.localPosition = source.localPosition; target.localRotation = source.localRotation; target.localScale = source.localScale; // Match children by name foreach (Transform srcChild in source) { var tgtChild = target.Find(srcChild.name); if (tgtChild != null) copyTransforms(srcChild, tgtChild); } } #endif } }