TIL 240818 간단한 절차적 애니메이션 - 2

강성원·2024년 8월 19일
0

TIL 오늘 배운 것

목록 보기
69/70
post-thumbnail

Angle Constraint 각도 제한

머리가 한없이 뒤로 돌아서 몸을 뚫고 나간다면 생물이라고 볼 수 없을 것이다.

그래서 종속되는 신체가 머리의 특정한 z축 각도의 범위 안에 존재하도록 제한해주어야 한다.

간단히 그림으로 풀어보면 아래와 같다.

몸통이 머리의 후방 지정 각도 내에 존재해야 하고 그 범위를 벗어나지 않아야 한다.

부모의 후방 각도에 자식 위치시키기

우선 자식(타겟)이 월드 상에서 부모 z축의 어느 각도에 존재하는지 구한다.

// 부모에 대한 자식의 방향 구하기
Vector3 direction = target.position - transform.position;

// 부모위치에 대한 자식의 z축 회전 값
float targetAngle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;

if (targetAngle < 0)
	targetAngle += 360;

그리고 부모가 z축이 얼마나 회전했는지 구한다.

float selfAngle = transform.eulerAngles.z; // 내 자신이 회전한 정도(각도)

if (selfAngle < 0)
	selfAngle += 360;

[부모 회전 각도] - [자식의 각도]를 해주면 부모의 대한 자식 위치의 상대적인 각도를 구할 수 있다.
예를 들어서 자식이 부모 중심의 45도 위치에 있고, 부모가 45도 회전해있으면 자식의 상대적인 각도는 0도가 된다.

float diffAngle = targetAngle - selfAngle; // 나-타겟 각도 => 상대적인 각

if (diffAngle < 0)
	diffAngle += 360;

자식의 상대적인 각도를 원하는 범위 내에 존재하도록 값을 조정해주고,
부모의 회전 값에 더해서 실제로 부모의 중심으로부터 자식이 존재할 각도finalAngle를 구해준다.
그리고 그 값으로 자식을 위치시킨다.

if (diffAngle < 180 - angleConstraint) diffAngle = 180 - angleConstraint;
if (diffAngle > 180 + angleConstraint) diffAngle = 180 + angleConstraint;

float finalAngle = diffAngle + selfAngle; // 타겟을 위치시킬 각도

Quaternion rotation = Quaternion.Euler(0, 0, finalAngle);
target.position = transform.position + (rotation * Vector3.right) * 1;

자식이 부모를 쳐다보도록

위 코드로 머리-몸통 한 개 만 연결했을 때는 정말 잘 됐다.

그런데 몸통을 10개씩 만들었을 때는 두 번째 몸통부터는 뻣뻣한 모습을 보였다.

모든 자식들은 부모를 쳐다보아야 내가 생각했던 움직임이 나온다는 것을 깨달았고 적용하였다.

Vector3 targetToMe = transform.position - target.position;
float targetToMeAlngle = Mathf.Atan2(targetToMe.y, targetToMe.x) * Mathf.Rad2Deg;
target.rotation = Quaternion.Euler(0, 0, targetToMeAlngle);

전체 코드

public class DegreeTest : MonoBehaviour
{
    public Transform target;
    public float angleConstraint = 30; // 제한할 각도의 절반 크기

    void Update()
    {
        if(target != null)
        {
            // 타겟이 내 z축 어느 각도에 있는지 출력
            Vector3 direction = target.position - transform.position; // 방향 구하기

            float targetAngle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg; // 나의 z를 축으로 타겟의 z 각도

            if (targetAngle < 0)
                targetAngle += 360;

            float selfAngle = transform.eulerAngles.z; // 내 자신이 회전한 정도(각도)

            if (selfAngle < 0)
                selfAngle += 360;

            float diffAngle = targetAngle - selfAngle; // 나-타겟 각도 => 상대적인 각

            if (diffAngle < 0)
                diffAngle += 360;

            if (diffAngle < 180 - angleConstraint) diffAngle = 180 - angleConstraint;
            if (diffAngle > 180 + angleConstraint) diffAngle = 180 + angleConstraint;

            float finalAngle = diffAngle + selfAngle; // 타겟을 위치시킬 각도

            Quaternion rotation = Quaternion.Euler(0, 0, finalAngle);
            target.position = transform.position + (rotation * Vector3.right) * 1;


            // 타겟이 나 바라보게
            Vector3 targetToMe = transform.position - target.position;
            float targetToMeAlngle = Mathf.Atan2(targetToMe.y, targetToMe.x) * Mathf.Rad2Deg;
            target.rotation = Quaternion.Euler(0, 0, targetToMeAlngle);


            Debug.Log("내 회전 각도: " + selfAngle + " 내 중심에 대한 타겟의 각도: " + targetAngle + "내 방향에 따른 타겟의 각도 :" + diffAngle);
        }
    }
}

이런 것들을 할 때마다 수학이 발목을 잡는다.
삼각함수의 이론은 알아도 적용하는 것은 또 차원이 다르다는 것을 항상 느낀다.

profile
개발은삼순이발

0개의 댓글