ADD: 야모 스크립트 업로드
This commit is contained in:
parent
ec53fdf1e1
commit
0ac908fd2b
8
Assets/Scripts/YAMO_Scripts.meta
Normal file
8
Assets/Scripts/YAMO_Scripts.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef4c11e718c403342851dfa313db61cc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
109
Assets/Scripts/YAMO_Scripts/FindUnusedBones.cs
Normal file
109
Assets/Scripts/YAMO_Scripts/FindUnusedBones.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class FindUnusedBones : EditorWindow
|
||||
{
|
||||
private List<string> excludeStrings = new List<string>(); // 제외할 문자열 리스트
|
||||
|
||||
[MenuItem("Tools/Find Unused Bones")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<FindUnusedBones>("Find Unused Bones");
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Exclude Strings", EditorStyles.boldLabel);
|
||||
|
||||
// 동적으로 문자열 입력 필드와 + - 버튼을 추가
|
||||
for (int i = 0; i < excludeStrings.Count; i++)
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
excludeStrings[i] = EditorGUILayout.TextField($"Exclude String {i + 1}", excludeStrings[i]);
|
||||
|
||||
if (GUILayout.Button("-", GUILayout.Width(20)))
|
||||
{
|
||||
excludeStrings.RemoveAt(i);
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("+", GUILayout.Width(20)))
|
||||
{
|
||||
excludeStrings.Add(string.Empty); // 빈 문자열 입력 필드 추가
|
||||
}
|
||||
|
||||
GUILayout.Space(20);
|
||||
|
||||
if (GUILayout.Button("Find and Select Unused Bones"))
|
||||
{
|
||||
FindAndSelectUnusedBones();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindAndSelectUnusedBones()
|
||||
{
|
||||
if (Selection.activeGameObject == null)
|
||||
{
|
||||
Debug.LogWarning("No GameObject selected!");
|
||||
return;
|
||||
}
|
||||
|
||||
Transform root = Selection.activeGameObject.transform;
|
||||
SkinnedMeshRenderer[] skinnedMeshRenderers = root.GetComponentsInChildren<SkinnedMeshRenderer>();
|
||||
|
||||
HashSet<Transform> usedBones = new HashSet<Transform>();
|
||||
HashSet<Transform> excludedObjects = new HashSet<Transform>();
|
||||
|
||||
// Add all bones used by SkinnedMeshRenderers to usedBones set
|
||||
foreach (var skinnedMeshRenderer in skinnedMeshRenderers)
|
||||
{
|
||||
foreach (var bone in skinnedMeshRenderer.bones)
|
||||
{
|
||||
usedBones.Add(bone);
|
||||
}
|
||||
// Add the SkinnedMeshRenderer's gameObject and its parents to excludedObjects set
|
||||
Transform current = skinnedMeshRenderer.transform;
|
||||
while (current != null)
|
||||
{
|
||||
excludedObjects.Add(current);
|
||||
current = current.parent;
|
||||
}
|
||||
}
|
||||
|
||||
List<Transform> unusedBones = new List<Transform>();
|
||||
Transform[] allBones = root.GetComponentsInChildren<Transform>();
|
||||
foreach (var bone in allBones)
|
||||
{
|
||||
// 문자열 필터링: 사용자가 입력한 문자열을 포함한 오브젝트는 제외
|
||||
bool excludeBone = false;
|
||||
foreach (var excludeString in excludeStrings)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(excludeString) && bone.name.ToLower().Contains(excludeString.ToLower()))
|
||||
{
|
||||
excludeBone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!usedBones.Contains(bone) &&
|
||||
!excludedObjects.Contains(bone) &&
|
||||
bone != root &&
|
||||
!excludeBone) // 필터링된 오브젝트 제외
|
||||
{
|
||||
unusedBones.Add(bone);
|
||||
}
|
||||
}
|
||||
|
||||
if (unusedBones.Count > 0)
|
||||
{
|
||||
Selection.objects = unusedBones.ConvertAll(b => b.gameObject).ToArray();
|
||||
Debug.Log($"Found {unusedBones.Count} unused bones. Selected in the hierarchy.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("No unused bones found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/YAMO_Scripts/FindUnusedBones.cs.meta
Normal file
2
Assets/Scripts/YAMO_Scripts/FindUnusedBones.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e394acc6b38d9e4f8fadd8b5f7622cc
|
||||
248
Assets/Scripts/YAMO_Scripts/MaterialAndTextureCollectorWindow.cs
Normal file
248
Assets/Scripts/YAMO_Scripts/MaterialAndTextureCollectorWindow.cs
Normal file
@ -0,0 +1,248 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
public class MaterialAndTextureTool : EditorWindow
|
||||
{
|
||||
private GameObject targetPrefab;
|
||||
private string materialOutputPath = "Assets/DuplicatedMaterials";
|
||||
private string textureOutputPath = "Assets/DuplicatedTextures";
|
||||
|
||||
private Dictionary<string, List<Material>> duplicateMaterialMap = new Dictionary<string, List<Material>>();
|
||||
private Dictionary<string, List<Texture>> duplicateTextureMap = new Dictionary<string, List<Texture>>();
|
||||
private Dictionary<Material, Material> materialCopies = new Dictionary<Material, Material>();
|
||||
private Dictionary<Texture, Texture> textureCopies = new Dictionary<Texture, Texture>();
|
||||
private HashSet<Material> collectedMaterials = new HashSet<Material>();
|
||||
private HashSet<Texture> collectedTextures = new HashSet<Texture>();
|
||||
private Vector2 scroll;
|
||||
|
||||
[MenuItem("Tools/Material & Texture Tool")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<MaterialAndTextureTool>("MatTex Tool");
|
||||
window.minSize = new Vector2(600, 500);
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Label("\uD83C\uDF1F 머테리얼 & 텍스처 유틸리티", EditorStyles.boldLabel);
|
||||
|
||||
targetPrefab = (GameObject)EditorGUILayout.ObjectField("\uD83D\uDCE6 타겟 프리팹", targetPrefab, typeof(GameObject), true);
|
||||
materialOutputPath = EditorGUILayout.TextField("\uD83D\uDCC2 머테리얼 저장 경로", materialOutputPath);
|
||||
textureOutputPath = EditorGUILayout.TextField("\uD83D\uDCC2 텍스처 저장 경로", textureOutputPath);
|
||||
|
||||
if (GUILayout.Button("\uD83D\uDCCB 중복 이름 검사")) CollectDuplicates();
|
||||
if (GUILayout.Button("\uD83D\uDD04 중복 이름 자동 변경")) RenameDuplicateAssets();
|
||||
if (GUILayout.Button("\uD83D\uDD04 머테리얼 및 텍스처 복사")) DuplicateMaterialsAndTextures();
|
||||
|
||||
scroll = EditorGUILayout.BeginScrollView(scroll);
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("\u26A0\uFE0F 중복된 이름의 머테리얼", EditorStyles.boldLabel);
|
||||
DrawDuplicateList(duplicateMaterialMap);
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("\u26A0\uFE0F 중복된 이름의 텍스처", EditorStyles.boldLabel);
|
||||
DrawDuplicateList(duplicateTextureMap);
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("\uD83D\uDD0D 참조된 모든 머테리얼", EditorStyles.boldLabel);
|
||||
foreach (var mat in collectedMaterials)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(mat.name, GUILayout.Width(200)))
|
||||
{
|
||||
EditorGUIUtility.PingObject(mat);
|
||||
}
|
||||
EditorGUILayout.ObjectField(mat, typeof(Material), false);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("\uD83D\uDD0D 참조된 모든 텍스처", EditorStyles.boldLabel);
|
||||
foreach (var tex in collectedTextures)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button(tex.name, GUILayout.Width(200)))
|
||||
{
|
||||
EditorGUIUtility.PingObject(tex);
|
||||
}
|
||||
EditorGUILayout.ObjectField(tex, typeof(Texture), false);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
void CollectDuplicates()
|
||||
{
|
||||
duplicateMaterialMap.Clear();
|
||||
duplicateTextureMap.Clear();
|
||||
collectedMaterials.Clear();
|
||||
collectedTextures.Clear();
|
||||
|
||||
if (targetPrefab == null) return;
|
||||
|
||||
HashSet<Material> seenMaterials = new HashSet<Material>();
|
||||
HashSet<Texture> seenTextures = new HashSet<Texture>();
|
||||
|
||||
var renderers = targetPrefab.GetComponentsInChildren<Renderer>(true);
|
||||
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
foreach (var mat in renderer.sharedMaterials)
|
||||
{
|
||||
if (mat == null || seenMaterials.Contains(mat)) continue;
|
||||
seenMaterials.Add(mat);
|
||||
collectedMaterials.Add(mat);
|
||||
|
||||
if (!duplicateMaterialMap.ContainsKey(mat.name))
|
||||
duplicateMaterialMap[mat.name] = new List<Material>();
|
||||
duplicateMaterialMap[mat.name].Add(mat);
|
||||
|
||||
Shader shader = mat.shader;
|
||||
int count = ShaderUtil.GetPropertyCount(shader);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string propName = ShaderUtil.GetPropertyName(shader, i);
|
||||
Texture tex = mat.GetTexture(propName);
|
||||
if (tex == null || seenTextures.Contains(tex)) continue;
|
||||
seenTextures.Add(tex);
|
||||
collectedTextures.Add(tex);
|
||||
|
||||
if (!duplicateTextureMap.ContainsKey(tex.name))
|
||||
duplicateTextureMap[tex.name] = new List<Texture>();
|
||||
duplicateTextureMap[tex.name].Add(tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawDuplicateList<T>(Dictionary<string, List<T>> map) where T : Object
|
||||
{
|
||||
foreach (var pair in map)
|
||||
{
|
||||
if (pair.Value.Count < 2) continue;
|
||||
GUILayout.Label("\u26A0\uFE0F " + pair.Key + " (" + pair.Value.Count + "개)", GetRedStyle());
|
||||
foreach (var obj in pair.Value)
|
||||
{
|
||||
EditorGUILayout.ObjectField(obj, typeof(T), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenameDuplicateAssets()
|
||||
{
|
||||
Dictionary<string, int> renameCount = new Dictionary<string, int>();
|
||||
|
||||
RenameAssetGroup(duplicateMaterialMap, ".mat", renameCount);
|
||||
RenameAssetGroup(duplicateTextureMap, null, renameCount);
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
void RenameAssetGroup<T>(Dictionary<string, List<T>> map, string extOverride, Dictionary<string, int> counter) where T : Object
|
||||
{
|
||||
foreach (var pair in map)
|
||||
{
|
||||
if (pair.Value.Count < 2) continue;
|
||||
foreach (var obj in pair.Value)
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(obj);
|
||||
if (string.IsNullOrEmpty(path)) continue;
|
||||
|
||||
if (!counter.ContainsKey(pair.Key)) counter[pair.Key] = 1;
|
||||
else counter[pair.Key] += 1;
|
||||
|
||||
string newName = pair.Key + "_" + counter[pair.Key];
|
||||
string result = AssetDatabase.RenameAsset(path, newName);
|
||||
if (result != "")
|
||||
{
|
||||
Debug.LogWarning("리네이밍 실패: " + result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DuplicateMaterialsAndTextures()
|
||||
{
|
||||
if (targetPrefab == null) return;
|
||||
|
||||
materialCopies.Clear();
|
||||
textureCopies.Clear();
|
||||
|
||||
if (!AssetDatabase.IsValidFolder(materialOutputPath)) AssetDatabase.CreateFolder("Assets", "DuplicatedMaterials");
|
||||
if (!AssetDatabase.IsValidFolder(textureOutputPath)) AssetDatabase.CreateFolder("Assets", "DuplicatedTextures");
|
||||
|
||||
Renderer[] renderers = targetPrefab.GetComponentsInChildren<Renderer>(true);
|
||||
|
||||
foreach (var renderer in renderers)
|
||||
{
|
||||
Material[] newMats = new Material[renderer.sharedMaterials.Length];
|
||||
|
||||
for (int i = 0; i < newMats.Length; i++)
|
||||
{
|
||||
Material orig = renderer.sharedMaterials[i];
|
||||
if (orig == null) continue;
|
||||
|
||||
if (!materialCopies.ContainsKey(orig))
|
||||
{
|
||||
Material newMat = new Material(orig);
|
||||
string matPath = AssetDatabase.GenerateUniqueAssetPath(materialOutputPath + "/" + orig.name + "_Copy.mat");
|
||||
AssetDatabase.CreateAsset(newMat, matPath);
|
||||
materialCopies[orig] = newMat;
|
||||
CopyTextures(orig, newMat);
|
||||
EditorUtility.SetDirty(newMat);
|
||||
}
|
||||
newMats[i] = materialCopies[orig];
|
||||
}
|
||||
|
||||
Undo.RecordObject(renderer, "Apply Copied Materials");
|
||||
renderer.sharedMaterials = newMats;
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
void CopyTextures(Material original, Material copy)
|
||||
{
|
||||
Shader shader = original.shader;
|
||||
int count = ShaderUtil.GetPropertyCount(shader);
|
||||
|
||||
string[] maskProps = { "_MaskMap", "_OcclusionMap", "_DetailMask", "_RoughnessMap", "_MetallicGlossMap" };
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string prop = ShaderUtil.GetPropertyName(shader, i);
|
||||
Texture tex = original.GetTexture(prop);
|
||||
if (tex == null) continue;
|
||||
|
||||
bool isTexEnv = ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv;
|
||||
bool isMask = System.Array.IndexOf(maskProps, prop) >= 0;
|
||||
|
||||
if (isTexEnv || isMask)
|
||||
{
|
||||
if (!textureCopies.ContainsKey(tex))
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(tex);
|
||||
string newPath = AssetDatabase.GenerateUniqueAssetPath(textureOutputPath + "/" + tex.name + "_Copy" + Path.GetExtension(path));
|
||||
AssetDatabase.CopyAsset(path, newPath);
|
||||
Texture newTex = AssetDatabase.LoadAssetAtPath<Texture>(newPath);
|
||||
textureCopies[tex] = newTex;
|
||||
}
|
||||
|
||||
copy.SetTexture(prop, textureCopies[tex]);
|
||||
EditorUtility.SetDirty(copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GUIStyle GetRedStyle()
|
||||
{
|
||||
var style = new GUIStyle(EditorStyles.label);
|
||||
style.normal.textColor = Color.red;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d0354ae398917c4d9e1ca82cf113f94
|
||||
127
Assets/Scripts/YAMO_Scripts/RenameHumanoidBones.cs
Normal file
127
Assets/Scripts/YAMO_Scripts/RenameHumanoidBones.cs
Normal file
@ -0,0 +1,127 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class RenameHumanoidBones : EditorWindow
|
||||
{
|
||||
private Animator selectedAnimator;
|
||||
private Dictionary<HumanBodyBones, Transform> humanoidBones = new Dictionary<HumanBodyBones, Transform>();
|
||||
private Dictionary<HumanBodyBones, string> newBoneNames = new Dictionary<HumanBodyBones, string>();
|
||||
|
||||
[MenuItem("Tools/Humanoid Bone Renamer")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<RenameHumanoidBones>("Humanoid Bone Renamer");
|
||||
}
|
||||
|
||||
private Vector2 scrollPosition;
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Humanoid Bone Renamer", EditorStyles.boldLabel);
|
||||
|
||||
if (GUILayout.Button("Find Humanoid Bones"))
|
||||
{
|
||||
FindHumanoidBones();
|
||||
}
|
||||
|
||||
if (selectedAnimator == null)
|
||||
{
|
||||
GUILayout.Label("No valid Humanoid Animator selected.", EditorStyles.helpBox);
|
||||
return;
|
||||
}
|
||||
|
||||
GUILayout.Label("Selected GameObject: " + selectedAnimator.gameObject.name, EditorStyles.boldLabel);
|
||||
|
||||
if (humanoidBones.Count > 0)
|
||||
{
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition, GUILayout.Height(400));
|
||||
|
||||
EditorGUILayout.BeginVertical("box");
|
||||
GUILayout.Label("Humanoid Bones", EditorStyles.boldLabel);
|
||||
|
||||
foreach (var bone in humanoidBones)
|
||||
{
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
// First column: Unity's Humanoid bone name
|
||||
GUILayout.Label(bone.Key.ToString(), GUILayout.Width(150));
|
||||
|
||||
// Second column: Current bone name
|
||||
GUILayout.Label(bone.Value != null ? bone.Value.name : "None", GUILayout.Width(150));
|
||||
|
||||
// Third column: Input field for new name (defaulting to the first column's name)
|
||||
if (!newBoneNames.ContainsKey(bone.Key))
|
||||
{
|
||||
newBoneNames[bone.Key] = bone.Key.ToString();
|
||||
}
|
||||
newBoneNames[bone.Key] = EditorGUILayout.TextField(newBoneNames[bone.Key], GUILayout.Width(150));
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Change Humanoid Bones Name"))
|
||||
{
|
||||
ChangeHumanoidBoneNames();
|
||||
}
|
||||
}
|
||||
|
||||
private void FindHumanoidBones()
|
||||
{
|
||||
selectedAnimator = null;
|
||||
humanoidBones.Clear();
|
||||
newBoneNames.Clear();
|
||||
|
||||
if (Selection.activeGameObject == null)
|
||||
{
|
||||
Debug.LogError("No GameObject selected. Please select a GameObject with an Animator component.");
|
||||
return;
|
||||
}
|
||||
|
||||
selectedAnimator = Selection.activeGameObject.GetComponent<Animator>();
|
||||
|
||||
if (selectedAnimator == null || selectedAnimator.avatar == null || !selectedAnimator.avatar.isValid || !selectedAnimator.avatar.isHuman)
|
||||
{
|
||||
Debug.LogError("Selected GameObject does not have a valid Humanoid Avatar.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < HumanTrait.BoneCount; i++)
|
||||
{
|
||||
HumanBodyBones bone = (HumanBodyBones)i;
|
||||
Transform boneTransform = selectedAnimator.GetBoneTransform(bone);
|
||||
|
||||
if (boneTransform != null)
|
||||
{
|
||||
humanoidBones[bone] = boneTransform;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("Humanoid bones found and ready for renaming.");
|
||||
}
|
||||
|
||||
private void ChangeHumanoidBoneNames()
|
||||
{
|
||||
if (selectedAnimator == null)
|
||||
{
|
||||
Debug.LogError("No valid Humanoid Animator selected.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var bone in humanoidBones)
|
||||
{
|
||||
if (bone.Value != null && !string.IsNullOrWhiteSpace(newBoneNames[bone.Key]))
|
||||
{
|
||||
string newName = newBoneNames[bone.Key];
|
||||
Debug.Log($"Renaming {bone.Value.name} to {newName}");
|
||||
bone.Value.name = newName;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("Bone renaming completed.");
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/YAMO_Scripts/RenameHumanoidBones.cs.meta
Normal file
2
Assets/Scripts/YAMO_Scripts/RenameHumanoidBones.cs.meta
Normal file
@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d90295d3945afb04dbf6709ab5c26a3e
|
||||
Loading…
x
Reference in New Issue
Block a user