using System.Collections.Generic; using UnityEngine; using UnityEditor; using System.Linq; using VRM; // VRMライブラリの名前空間を確認してください。これが正しいかどうかによっては変更が必要です。 public class VRMSpringBoneMoveTool : EditorWindow { GameObject sourcePrefab; GameObject destinationPrefab; //[MenuItem("Tools/Transfer VRM Spring Bones")] static void ShowWindow() { GetWindow("VRM Spring Bone Move Tool"); } void OnGUI() { GUILayout.Label("Transfer VRM Spring Bone Components", EditorStyles.boldLabel); sourcePrefab = EditorGUILayout.ObjectField("Source Prefab", sourcePrefab, typeof(GameObject), true) as GameObject; destinationPrefab = EditorGUILayout.ObjectField("Destination Prefab", destinationPrefab, typeof(GameObject), true) as GameObject; if (GUILayout.Button("Transfer")) { if (sourcePrefab == null || destinationPrefab == null) { EditorUtility.DisplayDialog("Error", "Source and Destination Prefabs must be set.", "OK"); return; } TransferVRMSpringBone(); } } void TransferVRMSpringBone() { if (!EditorUtility.DisplayDialog("확인", "대상 프리팹의 모든 VRMSpringBone이 삭제됩니다. 계속하시겠습니까?", "예", "아니오")) { return; } // Secondary 오브젝트 찾기 또는 생성 Transform destSecondary = destinationPrefab.transform.Find("Secondary"); if (destSecondary == null) { GameObject secondaryObj = new GameObject("Secondary"); secondaryObj.transform.SetParent(destinationPrefab.transform, false); destSecondary = secondaryObj.transform; Debug.Log("Secondary 오브젝트가 생성되었습니다."); } // 기존 VRMSpringBone 컴포넌트 제거 var existingBones = destinationPrefab.GetComponentsInChildren(true); foreach (var bone in existingBones) { DestroyImmediate(bone); } int successCount = 0; int failCount = 0; // 소스의 VRMSpringBone 컴포넌트 복사 var springBones = sourcePrefab.GetComponentsInChildren(true); foreach (var springBone in springBones) { try { // Secondary 오브젝트에 새로운 VRMSpringBone 컴포넌트 추가 VRMSpringBone newSpringBone = destSecondary.gameObject.AddComponent(); if (CopyVRMSpringBoneComponents(springBone, newSpringBone)) { successCount++; } else { DestroyImmediate(newSpringBone); failCount++; } } catch (System.Exception e) { Debug.LogError($"스프링본 복사 중 오류 발생: {e.Message}"); failCount++; } } if (successCount > 0) { EditorUtility.SetDirty(destinationPrefab); AssetDatabase.SaveAssets(); } EditorUtility.DisplayDialog("작업 완료", $"성공: {successCount}개\n실패: {failCount}개", "확인"); } bool CopyVRMSpringBoneComponents(VRMSpringBone original, VRMSpringBone copy) { try { // 기본 속성 복사 copy.m_comment = original.m_comment; copy.m_stiffnessForce = original.m_stiffnessForce; copy.m_gravityPower = original.m_gravityPower; copy.m_gravityDir = original.m_gravityDir; copy.m_dragForce = original.m_dragForce; copy.m_hitRadius = original.m_hitRadius; copy.m_updateType = original.m_updateType; // Center 본 찾기 및 설정 if (original.m_center != null) { copy.m_center = FindCorrespondingTransform(original.m_center, destinationPrefab.transform); if (copy.m_center == null) { Debug.LogError($"Center 본을 찾을 수 없습니다: {original.m_center.name}"); return false; } } // Root 본들 찾기 및 설정 List newRootBones = new List(); foreach (var rootBone in original.RootBones) { if (rootBone == null) continue; var correspondingBone = FindCorrespondingTransform(rootBone, destinationPrefab.transform); if (correspondingBone != null) { newRootBones.Add(correspondingBone); } else { Debug.LogError($"Root 본을 찾을 수 없습니다: {rootBone.name}"); return false; } } if (newRootBones.Count == 0) { Debug.LogError("Root 본이 하나도 설정되지 않았습니다."); return false; } copy.RootBones = newRootBones; // Collider Groups 찾기 및 설정 if (original.ColliderGroups != null && original.ColliderGroups.Length > 0) { List newColliderGroups = new List(); foreach (var colliderGroup in original.ColliderGroups) { if (colliderGroup == null) continue; var correspondingCollider = FindCorrespondingColliderGroup(colliderGroup, destinationPrefab.transform); if (correspondingCollider != null) { newColliderGroups.Add(correspondingCollider); } else { Debug.LogWarning($"Collider Group을 찾을 수 없습니다: {colliderGroup.name}"); } } copy.ColliderGroups = newColliderGroups.ToArray(); } return true; } catch (System.Exception e) { Debug.LogError($"컴포넌트 복사 중 오류 발생: {e.Message}"); return false; } } Transform FindCorrespondingTransform(Transform original, Transform searchRoot) { if (original == null) return null; // 먼저 전체 경로로 시도 var path = GetTransformPath(original, sourcePrefab.transform); var result = searchRoot.Find(path); // 경로로 찾지 못한 경우 이름으로 재시도 if (result == null) { // 대상 프리팹에서 같은 이름을 가진 모든 Transform을 찾음 var allTransforms = searchRoot.GetComponentsInChildren(true); result = allTransforms.FirstOrDefault(t => t.name == original.name); if (result != null) { Debug.Log($"이름으로 매칭된 본 찾음: {original.name}"); } } return result; } string GetTransformPath(Transform current, Transform root) { if (current == root) { return ""; // ルートに到達したら終了 } var path = current.name; while (current.parent != null && current.parent != root) { current = current.parent; path = current.name + "/" + path; // 親の名前をパスに追加 } return path; } VRMSpringBoneColliderGroup FindCorrespondingColliderGroup(VRMSpringBoneColliderGroup original, Transform searchRoot) { if (original == null) { return null; } var path = GetTransformPath(original.transform, sourcePrefab.transform); var correspondingTransform = searchRoot.Find(path); return correspondingTransform != null ? correspondingTransform.GetComponent() : null; } void SetTransform(Transform newTransform, Transform originalTransform) { newTransform.localPosition = originalTransform.localPosition; newTransform.localRotation = originalTransform.localRotation; newTransform.localScale = originalTransform.localScale; } }