애니메이션 테스트
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
/// 구현 노트 2: VRM 컨트롤 리그는 Vrm10Instance.LateUpdate 에서 실제 본으로 반영되므로
|
||||
/// 그보다 먼저 실행돼야 한다. HeadLookAt 과 같은 이유로 실행 순서를 앞당긴다.
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(-100)]
|
||||
[DefaultExecutionOrder(-110)] // 몸통을 먼저, 그다음 HeadLookAt(-100) 이 머리를 처리
|
||||
public class ProceduralIdle : MonoBehaviour
|
||||
{
|
||||
[Header("참조")]
|
||||
@@ -56,18 +56,41 @@ public class ProceduralIdle : MonoBehaviour
|
||||
Transform root;
|
||||
Transform hips, chest, leftUpperArm, rightUpperArm, leftLowerArm, rightLowerArm;
|
||||
|
||||
// 각 본의 기준(정지) 자세. 매 프레임 여기로 되돌린 뒤 다시 계산한다.
|
||||
Quaternion restHips, restChest, restLUpper, restRUpper, restLLower, restRLower;
|
||||
|
||||
float phaseOffset;
|
||||
|
||||
bool resolved;
|
||||
bool warned;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
root = transform;
|
||||
if (animator == null) animator = GetComponentInChildren<Animator>();
|
||||
|
||||
// 여러 캐릭터가 있어도 동시에 같은 박자로 숨쉬지 않게 한다.
|
||||
phaseOffset = Random.Range(0f, 100f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 본 해석을 Awake 가 아니라 첫 LateUpdate 로 미룬다.
|
||||
///
|
||||
/// VRM 컨트롤 리그는 Vrm10Instance.Runtime 에 처음 접근할 때 생성되고, 그때
|
||||
/// Animator.avatar 가 컨트롤 리그용으로 교체된다. 그 전에 GetBoneTransform 을
|
||||
/// 부르면 실제 메시 본이 잡히는데, 그 본은 매 프레임 ControlRig.Process() 가
|
||||
/// 덮어쓰므로 여기서 가한 회전이 전부 지워진다.
|
||||
/// </summary>
|
||||
bool TryResolveBones()
|
||||
{
|
||||
if (animator == null || !animator.isHuman)
|
||||
{
|
||||
Debug.LogWarning("[ProceduralIdle] 휴머노이드 Animator 가 없어 비활성화합니다.");
|
||||
enabled = false;
|
||||
return;
|
||||
if (!warned)
|
||||
{
|
||||
warned = true;
|
||||
Debug.LogWarning("[ProceduralIdle] 휴머노이드 Animator 를 찾지 못했습니다.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
hips = animator.GetBoneTransform(HumanBodyBones.Hips);
|
||||
@@ -79,12 +102,63 @@ void Awake()
|
||||
leftLowerArm = animator.GetBoneTransform(HumanBodyBones.LeftLowerArm);
|
||||
rightLowerArm = animator.GetBoneTransform(HumanBodyBones.RightLowerArm);
|
||||
|
||||
// 여러 캐릭터가 있어도 동시에 같은 박자로 숨쉬지 않게 한다.
|
||||
phaseOffset = Random.Range(0f, 100f);
|
||||
if (leftUpperArm == null || rightUpperArm == null)
|
||||
{
|
||||
if (!warned)
|
||||
{
|
||||
warned = true;
|
||||
Debug.LogWarning("[ProceduralIdle] 위팔 본을 찾지 못했습니다.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CaptureRest();
|
||||
|
||||
resolved = true;
|
||||
Debug.Log($"[ProceduralIdle] 본 해석 완료. 위팔 경로: {GetPath(leftUpperArm)}");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CaptureRest()
|
||||
{
|
||||
if (hips != null) restHips = hips.localRotation;
|
||||
if (chest != null) restChest = chest.localRotation;
|
||||
if (leftUpperArm != null) restLUpper = leftUpperArm.localRotation;
|
||||
if (rightUpperArm != null) restRUpper = rightUpperArm.localRotation;
|
||||
if (leftLowerArm != null) restLLower = leftLowerArm.localRotation;
|
||||
if (rightLowerArm != null) restRLower = rightLowerArm.localRotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 매 프레임 기준 자세로 되돌린다.
|
||||
///
|
||||
/// 이 컴포넌트는 "애니메이션 클립이 없을 때"를 위한 것이다. 클립이 있으면
|
||||
/// Animator 가 매 프레임 포즈를 새로 써주므로 델타를 얹기만 하면 되지만,
|
||||
/// 지금은 되돌려 주는 주체가 없어 회전이 무한 누적된다. 그래서 직접 되돌린다.
|
||||
/// 나중에 실제 클립을 넣으면 이 컴포넌트를 끄면 된다.
|
||||
/// </summary>
|
||||
void ResetToRest()
|
||||
{
|
||||
if (hips != null) hips.localRotation = restHips;
|
||||
if (chest != null) chest.localRotation = restChest;
|
||||
if (leftUpperArm != null) leftUpperArm.localRotation = restLUpper;
|
||||
if (rightUpperArm != null) rightUpperArm.localRotation = restRUpper;
|
||||
if (leftLowerArm != null) leftLowerArm.localRotation = restLLower;
|
||||
if (rightLowerArm != null) rightLowerArm.localRotation = restRLower;
|
||||
}
|
||||
|
||||
static string GetPath(Transform t)
|
||||
{
|
||||
var sb = new System.Text.StringBuilder(t.name);
|
||||
for (var p = t.parent; p != null; p = p.parent) sb.Insert(0, p.name + "/");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!resolved && !TryResolveBones()) return;
|
||||
|
||||
ResetToRest();
|
||||
ApplyArms();
|
||||
ApplyBody();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user