Files
Fishing2/Assets/CharacterCustomizer/UI/Scripts/CreatePrefab.cs
2026-03-22 09:33:56 +08:00

197 lines
7.2 KiB
C#

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<Renderer>(true);
// Map "material key" -> duplicated asset
Dictionary<string, Material> materialMap = new Dictionary<string, Material>();
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<CharacterCustomization>(true));
DestroyImmediate(temp.GetComponentInChildren<ModifyBone_Manager>(true));
foreach (var modifyBone in temp.GetComponentsInChildren<ModifyBone>(true))
{
DestroyImmediate(modifyBone);
}
foreach (var copyPose in temp.GetComponentsInChildren<CopyPose>(true))
{
DestroyImmediate(copyPose);
}
foreach (var blendshapeManager in temp.GetComponentsInChildren<BlendshapeManager>(true))
{
DestroyImmediate(blendshapeManager);
}
foreach (var headColliders in temp.GetComponentsInChildren<HeadColliders>(true))
{
foreach (var GO in headColliders.colliderObjects)
{
if (GO != null) DestroyImmediate(GO);
}
}
DestroyImmediate(temp.GetComponentInChildren<HeadColliders>(true));
//Disable customization mode in physics manager
var physicsManager = temp.GetComponentInChildren<PhysicsManager>();
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 : "<no-shader>";
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<GameObject>(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
}
}