using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.IO; using System; namespace Streamingle.Editor { public class PoseRecorderWindow : EditorWindow { private GameObject selectedAvatar; private Animator avatarAnimator; private string poseName = "NewPose"; private string savePath = "Assets/Recordings/"; // 휴먼본 본들 (Animator HumanBodyBones 사용) private readonly HumanBodyBones[] humanBodyBones = { HumanBodyBones.Hips, HumanBodyBones.Spine, HumanBodyBones.Chest, HumanBodyBones.UpperChest, HumanBodyBones.Neck, HumanBodyBones.Head, HumanBodyBones.LeftShoulder, HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand, HumanBodyBones.RightShoulder, HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand, HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot, HumanBodyBones.LeftToes, HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot, HumanBodyBones.RightToes }; // 손가락 본들 private readonly HumanBodyBones[] fingerBones = { // 왼손 손가락 HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal, HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal, HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal, HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal, HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal, // 오른손 손가락 HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal, HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal, HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal, HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal, HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal }; [MenuItem("Tools/Pose Recorder")] public static void ShowWindow() { GetWindow("포즈 기록기"); } private void OnEnable() { // 선택된 GameObject가 아바타인지 확인 if (Selection.activeGameObject != null) { selectedAvatar = Selection.activeGameObject; avatarAnimator = selectedAvatar.GetComponent(); if (avatarAnimator == null) { avatarAnimator = selectedAvatar.GetComponentInChildren(); } } } private Vector2 scrollPosition; private void OnGUI() { scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); GUILayout.Label("아바타 포즈 기록기", EditorStyles.boldLabel); EditorGUILayout.Space(); // 아바타 선택 EditorGUILayout.BeginHorizontal(); GUILayout.Label("아바타:", GUILayout.Width(60)); selectedAvatar = (GameObject)EditorGUILayout.ObjectField(selectedAvatar, typeof(GameObject), true); EditorGUILayout.EndHorizontal(); if (selectedAvatar != null) { avatarAnimator = selectedAvatar.GetComponent(); if (avatarAnimator == null) { avatarAnimator = selectedAvatar.GetComponentInChildren(); } if (avatarAnimator == null) { EditorGUILayout.HelpBox("선택된 오브젝트에 Animator가 없습니다!", MessageType.Error); EditorGUILayout.EndScrollView(); return; } EditorGUILayout.HelpBox($"아바타: {selectedAvatar.name}", MessageType.Info); } else { EditorGUILayout.HelpBox("아바타를 선택해주세요.", MessageType.Warning); } EditorGUILayout.Space(); // 포즈 이름 입력 EditorGUILayout.BeginHorizontal(); GUILayout.Label("포즈 이름:", GUILayout.Width(60)); poseName = EditorGUILayout.TextField(poseName); EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); // 저장 경로 설정 EditorGUILayout.BeginHorizontal(); GUILayout.Label("저장 경로:", GUILayout.Width(60)); savePath = EditorGUILayout.TextField(savePath); if (GUILayout.Button("폴더 선택", GUILayout.Width(80))) { string newPath = EditorUtility.OpenFolderPanel("저장 폴더 선택", savePath, ""); if (!string.IsNullOrEmpty(newPath)) { // Assets 폴더 내부로 경로 변환 if (newPath.StartsWith(Application.dataPath)) { savePath = "Assets" + newPath.Substring(Application.dataPath.Length) + "/"; } else { savePath = "Assets/Recordings/"; } } } EditorGUILayout.EndHorizontal(); EditorGUILayout.Space(); // 버튼들 EditorGUI.BeginDisabledGroup(selectedAvatar == null || avatarAnimator == null); if (GUILayout.Button("현재 포즈 기록 (전체)", GUILayout.Height(30))) { RecordCurrentPose(true); } if (GUILayout.Button("현재 포즈 기록 (본체만)", GUILayout.Height(30))) { RecordCurrentPose(false); } EditorGUILayout.Space(); if (GUILayout.Button("T-Pose 기록", GUILayout.Height(30))) { RecordTPose(); } if (GUILayout.Button("A-Pose 기록", GUILayout.Height(30))) { RecordAPose(); } EditorGUI.EndDisabledGroup(); EditorGUILayout.Space(); // 정보 표시 if (selectedAvatar != null && avatarAnimator != null) { EditorGUILayout.LabelField("기록 가능한 본들:", EditorStyles.boldLabel); // 본체 본들 EditorGUILayout.LabelField("본체 본들:", EditorStyles.boldLabel); EditorGUILayout.BeginVertical("box"); int foundBodyBones = 0; foreach (HumanBodyBones humanBone in humanBodyBones) { Transform bone = avatarAnimator.GetBoneTransform(humanBone); if (bone != null) { EditorGUILayout.LabelField($"✓ {humanBone}"); foundBodyBones++; } } EditorGUILayout.EndVertical(); EditorGUILayout.LabelField($"본체 본: {foundBodyBones}개"); EditorGUILayout.Space(); // 손가락 본들 EditorGUILayout.LabelField("손가락 본들:", EditorStyles.boldLabel); EditorGUILayout.BeginVertical("box"); int foundFingerBones = 0; foreach (HumanBodyBones fingerBone in fingerBones) { Transform bone = avatarAnimator.GetBoneTransform(fingerBone); if (bone != null) { EditorGUILayout.LabelField($"✓ {fingerBone}"); foundFingerBones++; } } EditorGUILayout.EndVertical(); EditorGUILayout.LabelField($"손가락 본: {foundFingerBones}개"); EditorGUILayout.Space(); EditorGUILayout.LabelField($"총 {foundBodyBones + foundFingerBones}개 본 발견", EditorStyles.boldLabel); } EditorGUILayout.EndScrollView(); } private void RecordCurrentPose(bool includeFingers = true) { if (selectedAvatar == null || avatarAnimator == null) { EditorUtility.DisplayDialog("오류", "아바타를 선택해주세요.", "확인"); return; } PoseData poseData = new PoseData(); poseData.avatarName = selectedAvatar.name; poseData.poseName = poseName; poseData.recordedTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); int recordedBones = 0; // 본체 본들 기록 foreach (HumanBodyBones humanBone in humanBodyBones) { Transform bone = avatarAnimator.GetBoneTransform(humanBone); if (bone != null) { BonePoseData boneData = new BonePoseData(); boneData.boneName = humanBone.ToString(); boneData.localPosition = bone.localPosition; boneData.localRotation = bone.localEulerAngles; boneData.localScale = bone.localScale; poseData.bonePoses.Add(boneData); recordedBones++; } } // 손가락 본들 기록 (옵션) if (includeFingers) { int fingerBonesCount = 0; foreach (HumanBodyBones fingerBone in fingerBones) { Transform bone = avatarAnimator.GetBoneTransform(fingerBone); if (bone != null) { BonePoseData boneData = new BonePoseData(); boneData.boneName = fingerBone.ToString(); boneData.localPosition = bone.localPosition; boneData.localRotation = bone.localEulerAngles; boneData.localScale = bone.localScale; poseData.bonePoses.Add(boneData); recordedBones++; fingerBonesCount++; } } if (fingerBonesCount > 0) { UnityEngine.Debug.Log($"손가락 본 {fingerBonesCount}개 기록됨"); } } if (recordedBones > 0) { SavePoseData(poseData); string message = includeFingers ? $"포즈가 기록되었습니다!\n본체 본: {recordedBones - fingerBones.Length}개\n손가락 본: {fingerBones.Length}개\n총 {recordedBones}개 본" : $"포즈가 기록되었습니다!\n본체 본: {recordedBones}개"; EditorUtility.DisplayDialog("성공", message, "확인"); } else { EditorUtility.DisplayDialog("오류", "기록할 본을 찾을 수 없습니다.", "확인"); } } private void RecordTPose() { poseName = "T-Pose"; RecordCurrentPose(true); // 손가락 포함 } private void RecordAPose() { poseName = "A-Pose"; RecordCurrentPose(true); // 손가락 포함 } private void SavePoseData(PoseData poseData) { // 저장 폴더 생성 if (!Directory.Exists(savePath)) { Directory.CreateDirectory(savePath); } // 파일명 생성 string fileName = $"{poseData.poseName}_{DateTime.Now:yyyyMMdd_HHmmss}.json"; string filePath = Path.Combine(savePath, fileName); // JSON으로 저장 string json = JsonUtility.ToJson(poseData, true); File.WriteAllText(filePath, json); // Unity 에디터에서 파일 새로고침 AssetDatabase.Refresh(); UnityEngine.Debug.Log($"포즈가 저장되었습니다: {filePath}"); } } [System.Serializable] public class BonePoseData { public string boneName; public Vector3 localPosition; public Vector3 localRotation; public Vector3 localScale; } [System.Serializable] public class PoseData { public string avatarName; public string poseName; public string recordedTime; public List bonePoses = new List(); } }