626 lines
23 KiB
C#
626 lines
23 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using VRM;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
public class VRMUtilityWindow : EditorWindow
|
|
{
|
|
private GameObject originalPrefab;
|
|
private GameObject destinationPrefab;
|
|
private GameObject springBoneObject;
|
|
private GameObject avatarRoot;
|
|
private Vector2 scrollPosition;
|
|
private int selectedTab = 0;
|
|
private readonly string[] tabNames = { "스프링본 이동", "콜라이더 이동", "콜라이더 설정", "잘못된 본 제거" };
|
|
|
|
[MenuItem("VRM0/VRM 유틸리티")]
|
|
static void ShowWindow()
|
|
{
|
|
GetWindow<VRMUtilityWindow>("VRM 유틸리티").Show();
|
|
}
|
|
|
|
void OnGUI()
|
|
{
|
|
// 탭 스타일 정의
|
|
var tabStyle = new GUIStyle(EditorStyles.toolbarButton);
|
|
tabStyle.fixedHeight = 30;
|
|
tabStyle.fontSize = 12;
|
|
tabStyle.fontStyle = FontStyle.Bold;
|
|
tabStyle.alignment = TextAnchor.MiddleCenter;
|
|
|
|
EditorGUILayout.Space(10);
|
|
|
|
// 윈도우 너비에 맞춰 탭 버튼 크기 조정
|
|
float windowWidth = position.width;
|
|
float minTabWidth = 100; // 최소 탭 너비
|
|
float padding = 20; // 좌우 여백
|
|
float availableWidth = windowWidth - (padding * 2); // 사용 가능한 전체 너비
|
|
|
|
// 탭 버튼들을 수평으로 배치
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
|
|
// 사용 가능한 너비가 최소 필요 너비보다 작으면 버튼을 세로로 배치
|
|
if (availableWidth < (minTabWidth * tabNames.Length))
|
|
{
|
|
EditorGUILayout.EndHorizontal();
|
|
for (int i = 0; i < tabNames.Length; i++)
|
|
{
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Toggle(selectedTab == i, tabNames[i], tabStyle, GUILayout.Width(availableWidth)))
|
|
{
|
|
selectedTab = i;
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 충분한 너비가 있으면 가로로 배치
|
|
float tabWidth = availableWidth / tabNames.Length;
|
|
for (int i = 0; i < tabNames.Length; i++)
|
|
{
|
|
if (GUILayout.Toggle(selectedTab == i, tabNames[i], tabStyle, GUILayout.Width(tabWidth)))
|
|
{
|
|
selectedTab = i;
|
|
}
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
}
|
|
|
|
EditorGUILayout.Space(10);
|
|
|
|
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
|
|
|
// 공통 필드들을 탭에 따라 표시
|
|
switch (selectedTab)
|
|
{
|
|
case 0: DrawSpringBoneTransferTab(); break;
|
|
case 1: DrawColliderMoveTab(); break;
|
|
case 2: DrawColliderSetupTab(); break;
|
|
case 3: DrawInvalidBonesTab(); break;
|
|
}
|
|
|
|
EditorGUILayout.EndScrollView();
|
|
}
|
|
|
|
void DrawSpringBoneTransferTab()
|
|
{
|
|
DrawHeader("스프링본 이동");
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
DrawSourceDestinationFields();
|
|
EditorGUILayout.Space(10);
|
|
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("스프링본 이동", GUILayout.Height(30), GUILayout.Width(200)))
|
|
{
|
|
if (ValidateSourceDestination())
|
|
{
|
|
TransferVRMSpringBone();
|
|
}
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
|
|
EditorGUILayout.Space(5);
|
|
DrawHelpBox("원본 아바타의 스프링본을 대상 아바타로 이동합니다.\n기존 스프링본은 선택적으로 제거할 수 있습니다.");
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
void DrawColliderMoveTab()
|
|
{
|
|
DrawHeader("콜라이더 이동");
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
DrawSourceDestinationFields();
|
|
EditorGUILayout.Space(10);
|
|
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("콜라이더 이동", GUILayout.Height(30), GUILayout.Width(200)))
|
|
{
|
|
if (ValidateSourceDestination())
|
|
{
|
|
MoveColliders();
|
|
}
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
|
|
EditorGUILayout.Space(5);
|
|
DrawHelpBox("원본 아바타의 콜라이더를 대상 아바타의 동일한 본으로 이동합니다.");
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
void DrawColliderSetupTab()
|
|
{
|
|
DrawHeader("콜라이더 설정");
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.Space(5);
|
|
|
|
springBoneObject = (GameObject)EditorGUILayout.ObjectField(
|
|
"스프링본 오브젝트", springBoneObject, typeof(GameObject), true);
|
|
EditorGUILayout.Space(2);
|
|
avatarRoot = (GameObject)EditorGUILayout.ObjectField(
|
|
"아바타 루트", avatarRoot, typeof(GameObject), true);
|
|
|
|
EditorGUILayout.Space(10);
|
|
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("콜라이더 설정", GUILayout.Height(30), GUILayout.Width(200)))
|
|
{
|
|
if (ValidateColliderSetup())
|
|
{
|
|
SetupColliders();
|
|
}
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
|
|
EditorGUILayout.Space(5);
|
|
DrawHelpBox("선택한 오브젝트의 모든 스프링본에 콜라이더를 자동으로 설정합니다.");
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
void DrawInvalidBonesTab()
|
|
{
|
|
DrawHeader("잘못된 본 제거");
|
|
|
|
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
|
EditorGUILayout.Space(5);
|
|
|
|
destinationPrefab = (GameObject)EditorGUILayout.ObjectField(
|
|
"대상 아바타", destinationPrefab, typeof(GameObject), true);
|
|
|
|
EditorGUILayout.Space(10);
|
|
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
if (GUILayout.Button("잘못된 본 제거", GUILayout.Height(30), GUILayout.Width(200)))
|
|
{
|
|
if (ValidateDestination())
|
|
{
|
|
RemoveInvalidBones();
|
|
}
|
|
}
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
|
|
EditorGUILayout.Space(5);
|
|
DrawHelpBox("참조가 깨진 스프링본을 찾아서 제거합니다.");
|
|
EditorGUILayout.EndVertical();
|
|
}
|
|
|
|
void DrawSourceDestinationFields()
|
|
{
|
|
EditorGUILayout.Space(5);
|
|
originalPrefab = (GameObject)EditorGUILayout.ObjectField(
|
|
"원본 아바타", originalPrefab, typeof(GameObject), true);
|
|
EditorGUILayout.Space(2);
|
|
destinationPrefab = (GameObject)EditorGUILayout.ObjectField(
|
|
"대상 아바타", destinationPrefab, typeof(GameObject), true);
|
|
}
|
|
|
|
void DrawHeader(string title)
|
|
{
|
|
EditorGUILayout.Space(5);
|
|
using (new EditorGUILayout.HorizontalScope())
|
|
{
|
|
GUILayout.FlexibleSpace();
|
|
EditorGUILayout.LabelField(title, EditorStyles.boldLabel, GUILayout.Width(200));
|
|
GUILayout.FlexibleSpace();
|
|
}
|
|
EditorGUILayout.Space(5);
|
|
}
|
|
|
|
void DrawHelpBox(string message)
|
|
{
|
|
EditorGUILayout.HelpBox(message, MessageType.Info);
|
|
EditorGUILayout.Space(5);
|
|
}
|
|
|
|
// 검증 메서드들
|
|
bool ValidateSourceDestination()
|
|
{
|
|
if (originalPrefab == null || destinationPrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("오류", "원본과 대상 아바타를 모두 지정해주세요.", "확인");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ValidateColliderSetup()
|
|
{
|
|
if (springBoneObject == null || avatarRoot == null)
|
|
{
|
|
EditorUtility.DisplayDialog("오류", "스프링본 오브젝트와 아바타 루트를 모두 지정해주세요.", "확인");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ValidateDestination()
|
|
{
|
|
if (destinationPrefab == null)
|
|
{
|
|
EditorUtility.DisplayDialog("오류", "대상 아바타를 지정해주세요.", "확인");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// 콜라이더 설정
|
|
void SetupColliders()
|
|
{
|
|
if (springBoneObject == null || avatarRoot == null)
|
|
{
|
|
EditorUtility.DisplayDialog("오류", "스프링본 오브젝트와 아바타 루트를 모두 지정해주세요.", "확인");
|
|
return;
|
|
}
|
|
|
|
// 스프링본 컴포넌트들 가져오기
|
|
VRMSpringBone[] springBones = springBoneObject.GetComponents<VRMSpringBone>();
|
|
if (springBones.Length == 0)
|
|
{
|
|
EditorUtility.DisplayDialog("오류", "선택한 오브젝트에 VRMSpringBone 컴포넌트가 없습니다.", "확인");
|
|
return;
|
|
}
|
|
|
|
// 아바타 루트 아래의 모든 콜라이더 그룹 찾기
|
|
VRMSpringBoneColliderGroup[] colliderGroups = avatarRoot.GetComponentsInChildren<VRMSpringBoneColliderGroup>(true);
|
|
|
|
// 각 스프링본 컴포넌트에 콜라이더 그룹 설정
|
|
Undo.RecordObjects(springBones, "Setup Spring Bone Colliders");
|
|
|
|
foreach (var springBone in springBones)
|
|
{
|
|
springBone.ColliderGroups = colliderGroups;
|
|
}
|
|
|
|
EditorUtility.SetDirty(springBoneObject);
|
|
Debug.Log($"콜라이더 설정 완료: {colliderGroups.Length}개의 콜라이더 그룹이 {springBones.Length}개의 스프링본에 설정되었습니다.");
|
|
}
|
|
|
|
// 콜라이더 이동
|
|
void MoveColliders()
|
|
{
|
|
VRMSpringBoneColliderGroup[] originalColliderGroups = originalPrefab.GetComponentsInChildren<VRMSpringBoneColliderGroup>();
|
|
int successCount = 0;
|
|
int failCount = 0;
|
|
int createdCount = 0;
|
|
|
|
foreach (VRMSpringBoneColliderGroup originalColliderGroup in originalColliderGroups)
|
|
{
|
|
try
|
|
{
|
|
Transform correspondingTransform = FindCorrespondingTransform(originalColliderGroup.transform, destinationPrefab.transform);
|
|
|
|
if (correspondingTransform == null)
|
|
{
|
|
// 원본 본의 부모 경로를 찾음
|
|
Transform originalParent = originalColliderGroup.transform.parent;
|
|
Transform targetParent = destinationPrefab.transform;
|
|
|
|
// 부모가 있는 경우, 대상 프리팹에서 대응하는 부모를 찾음
|
|
if (originalParent != originalPrefab.transform)
|
|
{
|
|
Transform correspondingParent = FindCorrespondingTransform(originalParent, destinationPrefab.transform);
|
|
if (correspondingParent != null)
|
|
{
|
|
targetParent = correspondingParent;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"부모 본을 찾을 수 없습니다: {originalParent.name}. 루트에 생성합니다.");
|
|
}
|
|
}
|
|
|
|
// 새로운 본 생성
|
|
GameObject newBone = new GameObject(originalColliderGroup.transform.name);
|
|
newBone.transform.SetParent(targetParent, false); // false로 설정하여 로컬 트랜스폼 유지
|
|
newBone.transform.localPosition = originalColliderGroup.transform.localPosition;
|
|
newBone.transform.localRotation = originalColliderGroup.transform.localRotation;
|
|
newBone.transform.localScale = originalColliderGroup.transform.localScale;
|
|
|
|
correspondingTransform = newBone.transform;
|
|
createdCount++;
|
|
Debug.Log($"새로운 본 생성됨: {newBone.name} (부모: {targetParent.name})");
|
|
}
|
|
|
|
// 기존 콜라이더 그룹이 있다면 제거
|
|
var existingCollider = correspondingTransform.GetComponent<VRMSpringBoneColliderGroup>();
|
|
if (existingCollider != null)
|
|
{
|
|
DestroyImmediate(existingCollider);
|
|
}
|
|
|
|
// 새로운 콜라이더 그룹 추가
|
|
var newColliderGroup = correspondingTransform.gameObject.AddComponent<VRMSpringBoneColliderGroup>();
|
|
|
|
// 원본과 대상 본의 월드 회전 차이 계산
|
|
Quaternion rotationDiff = Quaternion.Inverse(originalColliderGroup.transform.rotation) * correspondingTransform.rotation;
|
|
|
|
// 콜라이더 설정 복사 및 회전 보정
|
|
newColliderGroup.Colliders = new VRMSpringBoneColliderGroup.SphereCollider[originalColliderGroup.Colliders.Length];
|
|
for (int i = 0; i < originalColliderGroup.Colliders.Length; i++)
|
|
{
|
|
var originalCollider = originalColliderGroup.Colliders[i];
|
|
|
|
// 원본 오프셋을 월드 공간으로 변환
|
|
Vector3 worldOffset = originalColliderGroup.transform.TransformDirection(originalCollider.Offset);
|
|
// 대상 본의 로컬 공간으로 변환
|
|
Vector3 newOffset = correspondingTransform.InverseTransformDirection(worldOffset);
|
|
|
|
newColliderGroup.Colliders[i] = new VRMSpringBoneColliderGroup.SphereCollider
|
|
{
|
|
Offset = newOffset,
|
|
Radius = originalCollider.Radius
|
|
};
|
|
}
|
|
|
|
successCount++;
|
|
Debug.Log($"콜라이더 그룹 이동 완료: {correspondingTransform.name}");
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
failCount++;
|
|
Debug.LogError($"콜라이더 이동 중 오류 발생: {e.Message}");
|
|
}
|
|
}
|
|
|
|
if (successCount > 0 || createdCount > 0)
|
|
{
|
|
EditorUtility.SetDirty(destinationPrefab);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
EditorUtility.DisplayDialog("작업 완료",
|
|
$"성공: {successCount}개\n실패: {failCount}개\n새로 생성된 본: {createdCount}개", "확인");
|
|
}
|
|
|
|
private Transform FindTransformByName(string name, Transform searchRoot)
|
|
{
|
|
// 대상 프리팹에서 같은 이름을 가진 모든 Transform을 찾음
|
|
var allTransforms = searchRoot.GetComponentsInChildren<Transform>(true);
|
|
return allTransforms.FirstOrDefault(t => t.name == name);
|
|
}
|
|
|
|
private void CopyColliderGroupSettings(VRMSpringBoneColliderGroup source, VRMSpringBoneColliderGroup destination)
|
|
{
|
|
destination.Colliders = new VRMSpringBoneColliderGroup.SphereCollider[source.Colliders.Length];
|
|
|
|
for (int i = 0; i < source.Colliders.Length; i++)
|
|
{
|
|
destination.Colliders[i] = new VRMSpringBoneColliderGroup.SphereCollider
|
|
{
|
|
Offset = source.Colliders[i].Offset,
|
|
Radius = source.Colliders[i].Radius
|
|
};
|
|
}
|
|
}
|
|
|
|
// 스프링본 이동
|
|
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<VRMSpringBone>(true);
|
|
foreach (var bone in existingBones)
|
|
{
|
|
DestroyImmediate(bone);
|
|
}
|
|
|
|
// 소스의 VRMSpringBone 컴포넌트 복사
|
|
var springBones = originalPrefab.GetComponentsInChildren<VRMSpringBone>(true);
|
|
int successCount = 0;
|
|
int failCount = 0;
|
|
|
|
foreach (var springBone in springBones)
|
|
{
|
|
try
|
|
{
|
|
VRMSpringBone newSpringBone = destSecondary.gameObject.AddComponent<VRMSpringBone>();
|
|
if (CopyVRMSpringBoneComponents(springBone, newSpringBone))
|
|
{
|
|
successCount++;
|
|
}
|
|
else
|
|
{
|
|
DestroyImmediate(newSpringBone);
|
|
failCount++;
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogError($"스프링본 복사 중 오류 발생: {e.Message}");
|
|
failCount++;
|
|
}
|
|
}
|
|
|
|
EditorUtility.SetDirty(destinationPrefab);
|
|
AssetDatabase.SaveAssets();
|
|
EditorUtility.DisplayDialog("작업 완료", $"성공: {successCount}개\n실패: {failCount}개", "확인");
|
|
}
|
|
|
|
// 잘못된 본 제거
|
|
void RemoveInvalidBones()
|
|
{
|
|
var springBones = destinationPrefab.GetComponentsInChildren<VRMSpringBone>(true);
|
|
int removedCount = 0;
|
|
|
|
foreach (var bone in springBones)
|
|
{
|
|
if (bone.RootBones.Any(rb => rb == null) || bone.ColliderGroups.Any(cg => cg == null))
|
|
{
|
|
DestroyImmediate(bone);
|
|
removedCount++;
|
|
}
|
|
}
|
|
|
|
if (removedCount > 0)
|
|
{
|
|
EditorUtility.SetDirty(destinationPrefab);
|
|
AssetDatabase.SaveAssets();
|
|
}
|
|
|
|
EditorUtility.DisplayDialog("작업 완료", $"제거된 잘못된 스프링본: {removedCount}개", "확인");
|
|
}
|
|
|
|
// 유틸리티 메서드들
|
|
private Transform FindCorrespondingBone(Transform originalBone, Transform[] copyBones)
|
|
{
|
|
return System.Array.Find(copyBones, bone => bone.name == originalBone.name);
|
|
}
|
|
|
|
private 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.LogWarning($"Center 본을 찾을 수 없습니다: {original.m_center.name} - Center 본 없이 계속 진행합니다.");
|
|
}
|
|
}
|
|
|
|
// Root 본들 설정
|
|
List<Transform> newRootBones = new List<Transform>();
|
|
foreach (var rootBone in original.RootBones)
|
|
{
|
|
if (rootBone == null) continue;
|
|
|
|
var correspondingBone = FindCorrespondingTransform(rootBone, destinationPrefab.transform);
|
|
if (correspondingBone != null)
|
|
{
|
|
newRootBones.Add(correspondingBone);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"Root 본을 찾을 수 없습니다: {rootBone.name} - 이 본은 건너뜁니다.");
|
|
}
|
|
}
|
|
|
|
// 찾은 본이 하나도 없으면 false 반환
|
|
if (newRootBones.Count == 0)
|
|
{
|
|
Debug.LogError("유효한 Root 본을 하나도 찾을 수 없습니다.");
|
|
return false;
|
|
}
|
|
|
|
copy.RootBones = newRootBones;
|
|
|
|
// Collider Groups 설정
|
|
if (original.ColliderGroups != null && original.ColliderGroups.Length > 0)
|
|
{
|
|
List<VRMSpringBoneColliderGroup> newColliderGroups = new List<VRMSpringBoneColliderGroup>();
|
|
foreach (var colliderGroup in original.ColliderGroups)
|
|
{
|
|
if (colliderGroup == null) continue;
|
|
|
|
var correspondingCollider = FindCorrespondingColliderGroup(colliderGroup);
|
|
if (correspondingCollider != null)
|
|
{
|
|
newColliderGroups.Add(correspondingCollider);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"콜라이더 그룹을 찾을 수 없습니다: {colliderGroup.name} - 이 콜라이더 그룹은 건너뜁니다.");
|
|
}
|
|
}
|
|
copy.ColliderGroups = newColliderGroups.ToArray();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogError($"컴포넌트 복사 중 오류 발생: {e.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private Transform FindCorrespondingTransform(Transform original, Transform searchRoot)
|
|
{
|
|
if (original == null) return null;
|
|
|
|
string path = GetTransformPath(original);
|
|
Transform result = searchRoot.Find(path);
|
|
|
|
if (result == null)
|
|
{
|
|
var allTransforms = searchRoot.GetComponentsInChildren<Transform>(true);
|
|
result = allTransforms.FirstOrDefault(t => t.name == original.name);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private string GetTransformPath(Transform transform)
|
|
{
|
|
string path = transform.name;
|
|
Transform parent = transform.parent;
|
|
|
|
while (parent != null && parent != originalPrefab.transform)
|
|
{
|
|
path = parent.name + "/" + path;
|
|
parent = parent.parent;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
private VRMSpringBoneColliderGroup FindCorrespondingColliderGroup(VRMSpringBoneColliderGroup original)
|
|
{
|
|
if (original == null) return null;
|
|
|
|
Transform correspondingTransform = FindCorrespondingTransform(original.transform, destinationPrefab.transform);
|
|
return correspondingTransform?.GetComponent<VRMSpringBoneColliderGroup>();
|
|
}
|
|
|
|
// 최소 윈도우 크기 설정
|
|
void OnEnable()
|
|
{
|
|
// 최소 윈도우 크기 설정
|
|
minSize = new Vector2(250, 400);
|
|
}
|
|
} |