using System.Collections.Generic; using UnityEngine; namespace KindRetargeting { /// /// 아바타 간의 회전 오프셋을 계산하고 포즈 데이터를 복사하는 기능을 제공합니다. /// public class OffsetTransfer : MonoBehaviour { [Header("아바타 설정")] [SerializeField] public Animator sourceAnimator; // 원본 아바타의 Animator [SerializeField] public Animator targetAnimator; // 대상 아바타의 Animator [Header("오프셋 설정")] [SerializeField] private bool autoCalculateOnStart = true; // 시작 시 자동 계산 여부 [SerializeField] private bool autoCopyPose = true; // 매 프레임마다 자동으로 포즈 복사 여부 // 본별 회전 오프셋을 저장하는 딕셔너리 private Dictionary rotationOffsets = new Dictionary(); #region 프로퍼티 /// /// 계산된 회전 오프셋 딕셔너리를 반환합니다. /// public Dictionary RotationOffsets => rotationOffsets; #endregion #region 초기화 private void Start() { if (autoCalculateOnStart) { CalculateRotationOffsets(); } } #endregion #region 업데이트 private void Update() { if (autoCopyPose) { CopyPoseToTarget(); } } private void LateUpdate() { if (autoCopyPose) { CopyPoseToTarget(); } } #endregion #region 오프셋 계산 /// /// 원본과 대상 아바타의 각 본 간 회전 오프셋을 계산하여 저장합니다. /// public void CalculateRotationOffsets() { if (sourceAnimator == null || targetAnimator == null) { Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다."); return; } // Dictionary가 null이면 초기화 if (rotationOffsets == null) { rotationOffsets = new Dictionary(); } // 모든 본에 대해 오프셋 계산 (기본 몸체 본 + 손가락 본 + UpperChest) for (int i = 0; i <= 54; i++) { HumanBodyBones bone = (HumanBodyBones)i; Transform sourceBone = sourceAnimator.GetBoneTransform(bone); Transform targetBone = targetAnimator.GetBoneTransform(bone); if (sourceBone != null && targetBone != null) { Quaternion offset = Quaternion.Inverse(sourceBone.rotation) * targetBone.rotation; // 최적화: ContainsKey + 인덱서 이중 조회 대신 인덱서 직접 사용 rotationOffsets[bone] = offset; } } Debug.Log($"회전 오프셋 계산 완료: {rotationOffsets.Count}개 본"); } /// /// 특정 본의 회전 오프셋을 반환합니다. /// /// 본 타입 /// 회전 오프셋 (없으면 Identity) public Quaternion GetRotationOffset(HumanBodyBones bone) { // 최적화: TryGetValue로 단일 조회 return rotationOffsets.TryGetValue(bone, out Quaternion offset) ? offset : Quaternion.identity; } /// /// 특정 본에 회전 오프셋을 적용합니다. /// /// 본 타입 /// 원본 회전값 /// 오프셋이 적용된 회전값 public Quaternion ApplyRotationOffset(HumanBodyBones bone, Quaternion originalRotation) { // 최적화: TryGetValue로 단일 조회 return rotationOffsets.TryGetValue(bone, out Quaternion offset) ? originalRotation * offset : originalRotation; } #endregion #region 포즈 복사 /// /// 원본 아바타의 포즈를 대상 아바타에 복사합니다. /// public void CopyPoseToTarget() { if (sourceAnimator == null || targetAnimator == null) { Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다."); return; } // 모든 본에 대해 포즈 복사 for (int i = 0; i <= 54; i++) { HumanBodyBones bone = (HumanBodyBones)i; Transform sourceBone = sourceAnimator.GetBoneTransform(bone); Transform targetBone = targetAnimator.GetBoneTransform(bone); if (sourceBone != null && targetBone != null) { // 회전은 오프셋 적용, 위치는 그대로 복사 Quaternion targetRotation = ApplyRotationOffset(bone, sourceBone.rotation); targetBone.rotation = targetRotation; targetBone.position = sourceBone.position; } } } /// /// 특정 본의 포즈를 복사합니다. /// /// 복사할 본 public void CopyBonePose(HumanBodyBones bone) { if (sourceAnimator == null || targetAnimator == null) { Debug.LogError("소스 또는 타겟 Animator가 설정되지 않았습니다."); return; } Transform sourceBone = sourceAnimator.GetBoneTransform(bone); Transform targetBone = targetAnimator.GetBoneTransform(bone); if (sourceBone != null && targetBone != null) { Quaternion targetRotation = ApplyRotationOffset(bone, sourceBone.rotation); targetBone.rotation = targetRotation; } } #endregion } }