Streamingle_URP/Assets/Scripts/KindRetargeting/FingerShapedController.cs
user b9361a9d17 Refactor : KindRetargeting dead code 제거 + IK 안정성/성능 개선
- TwoBoneIK: cosine law → FABRIK 6회 반복 (역관절 안정 + 본 길이 제약)
- 자동 힙 상하 보정 매 프레임 적용, 수동 hipsOffsetX/Y/Z 제거
- kneeFrontBackWeight/InOutWeight, GetAvatarScale 등 dead code 정리
- FingerShapedController GC 제거 (HumanPose/Transform 캐싱)
- IK 본 길이 / FieldInfo / Chair prop / 가중치 배열(List→float[]) 캐싱
- localAxisForWorldRight/Forward, IKJoints 다리 필드 등 미사용 정리
- 매직 넘버 55 → BoneCount 상수

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 00:07:54 +09:00

176 lines
8.0 KiB
C#

using UnityEngine;
namespace KindRetargeting
{
[System.Serializable]
public class FingerShapedController
{
private Animator animator;
private HumanPoseHandler humanPoseHandler;
// 매 프레임 재사용되는 캐시 (GC 압박 제거)
private Transform[] cachedNonFingerBones;
private Quaternion[] savedBoneRotations;
private HumanPose cachedHumanPose;
// 손가락을 제외한 모든 휴먼본 목록
private static readonly HumanBodyBones[] nonFingerBones = new 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,
HumanBodyBones.LeftEye,
HumanBodyBones.RightEye,
HumanBodyBones.Jaw
};
public bool enabled = false;
[Header("왼손 제어 값")]
[Range(-1, 1)] public float leftPinkyCurl; // 새끼손가락 구부리기
[Range(-1, 1)] public float leftRingCurl; // 약지 구부리기
[Range(-1, 1)] public float leftMiddleCurl; // 중지 구부리기
[Range(-1, 1)] public float leftIndexCurl; // 검지 구부리기
[Range(-1, 1)] public float leftThumbCurl; // 엄지 구부리기
[Range(-1, 1)] public float leftSpreadFingers; // 손가락 벌리기
[Header("오른손 제어 값")]
[Range(-1, 1)] public float rightPinkyCurl; // 새끼손가락 구부리기
[Range(-1, 1)] public float rightRingCurl; // 약지 구부리기
[Range(-1, 1)] public float rightMiddleCurl; // 중지 구부리기
[Range(-1, 1)] public float rightIndexCurl; // 검지 구부리기
[Range(-1, 1)] public float rightThumbCurl; // 엄지 구부리기
[Range(-1, 1)] public float rightSpreadFingers; // 손가락 벌리기
public bool leftHandEnabled = false; // 왼손 제어 활성화 상태
public bool rightHandEnabled = false; // 오른손 제어 활성화 상태
private bool isInitialized;
public void Initialize(Animator targetAnimator)
{
animator = targetAnimator;
if (animator == null || !animator.isHuman) return;
humanPoseHandler = new HumanPoseHandler(animator.avatar, animator.transform);
// Transform 배열 + 회전 배열 사전 캐싱 (매 프레임 GetBoneTransform 50회 → 0회)
cachedNonFingerBones = new Transform[nonFingerBones.Length];
savedBoneRotations = new Quaternion[nonFingerBones.Length];
for (int i = 0; i < nonFingerBones.Length; i++)
{
cachedNonFingerBones[i] = animator.GetBoneTransform(nonFingerBones[i]);
}
// HumanPose 사전 할당 (muscles 배열 95개 float, GC 회피)
humanPoseHandler.GetHumanPose(ref cachedHumanPose);
isInitialized = true;
}
public void Cleanup()
{
if (humanPoseHandler != null)
{
humanPoseHandler.Dispose();
humanPoseHandler = null;
}
isInitialized = false;
}
public void OnUpdate()
{
if (!isInitialized || !enabled) return;
UpdateMuscleValues();
}
private void UpdateMuscleValues()
{
// 1. 비손가락 본의 로컬 회전을 배열에 저장 (캐시된 Transform 재사용)
for (int i = 0; i < cachedNonFingerBones.Length; i++)
{
Transform bone = cachedNonFingerBones[i];
if (bone != null) savedBoneRotations[i] = bone.localRotation;
}
// 2. HumanPose 가져오기 (재사용 필드, GC 없음) + 손가락 머슬 설정
humanPoseHandler.GetHumanPose(ref cachedHumanPose);
SetHandMuscles(true, leftThumbCurl, leftIndexCurl, leftMiddleCurl, leftRingCurl,
leftPinkyCurl, leftSpreadFingers, ref cachedHumanPose);
SetHandMuscles(false, rightThumbCurl, rightIndexCurl, rightMiddleCurl, rightRingCurl,
rightPinkyCurl, rightSpreadFingers, ref cachedHumanPose);
// 3. 머슬 포즈 적용 (손가락 포함 전체 본에 영향)
humanPoseHandler.SetHumanPose(ref cachedHumanPose);
// 4. 비손가락 본 로컬 회전 복원 (본 길이 변형 방지)
for (int i = 0; i < cachedNonFingerBones.Length; i++)
{
Transform bone = cachedNonFingerBones[i];
if (bone != null) bone.localRotation = savedBoneRotations[i];
}
}
private void SetHandMuscles(bool isLeft, float thumb, float index, float middle, float ring,
float pinky, float spread, ref HumanPose humanPose)
{
// 해당 손이 비활성화 상태면 건너뛰기
if (isLeft && !leftHandEnabled) return;
if (!isLeft && !rightHandEnabled) return;
int baseOffset = isLeft ? 55 : 75; // 왼손은 55부터, 오른손은 75부터 시작
int muscleCount = humanPose.muscles.Length;
// 엄지손가락
if (baseOffset < muscleCount) humanPose.muscles[baseOffset] = thumb; // Thumb 1
if (baseOffset + 1 < muscleCount) humanPose.muscles[baseOffset + 1] = thumb; // Thumb Spread
if (baseOffset + 2 < muscleCount) humanPose.muscles[baseOffset + 2] = thumb; // Thumb 2
if (baseOffset + 3 < muscleCount) humanPose.muscles[baseOffset + 3] = thumb; // Thumb 3
// 검지
if (baseOffset + 4 < muscleCount) humanPose.muscles[baseOffset + 4] = index; // Index 1
if (baseOffset + 5 < muscleCount) humanPose.muscles[baseOffset + 5] = spread; // Index Spread
if (baseOffset + 6 < muscleCount) humanPose.muscles[baseOffset + 6] = index; // Index 2
if (baseOffset + 7 < muscleCount) humanPose.muscles[baseOffset + 7] = index; // Index 3
// 중지
if (baseOffset + 8 < muscleCount) humanPose.muscles[baseOffset + 8] = middle; // Middle 1
if (baseOffset + 9 < muscleCount) humanPose.muscles[baseOffset + 9] = spread; // Middle Spread
if (baseOffset + 10 < muscleCount) humanPose.muscles[baseOffset + 10] = middle; // Middle 2
if (baseOffset + 11 < muscleCount) humanPose.muscles[baseOffset + 11] = middle; // Middle 3
// 약지
if (baseOffset + 12 < muscleCount) humanPose.muscles[baseOffset + 12] = ring; // Ring 1
if (baseOffset + 13 < muscleCount) humanPose.muscles[baseOffset + 13] = spread; // Ring Spread
if (baseOffset + 14 < muscleCount) humanPose.muscles[baseOffset + 14] = ring; // Ring 2
if (baseOffset + 15 < muscleCount) humanPose.muscles[baseOffset + 15] = ring; // Ring 3
// 새끼손가락
if (baseOffset + 16 < muscleCount) humanPose.muscles[baseOffset + 16] = pinky; // Little 1
if (baseOffset + 17 < muscleCount) humanPose.muscles[baseOffset + 17] = spread; // Little Spread
if (baseOffset + 18 < muscleCount) humanPose.muscles[baseOffset + 18] = pinky; // Little 2
if (baseOffset + 19 < muscleCount) humanPose.muscles[baseOffset + 19] = pinky; // Little 3
}
}
}