본문 바로가기
개발/Unity

유니티 - 독립된 단진자의 운동 (Independent Simple Pendulum with Unity LerpAngle)

by 피로물든딸기 2023. 3. 29.
반응형

Unity 전체 링크

 

참고

- 쿼터니언으로 구현한 단진자의 운동
- 쿼터니언으로 구현한 단진자의 운동 확장
- 독립된 단진자의 운동
- 런타임에 단진자 운동 설정하기

 

쿼터니언으로 구현한 단진자의 운동의 경우 pivot을 회전시키고, pivot의 자식으로 단진자를 설정해야 한다.

이러한 제약 조건은 나중에 개발을 확장할 때 불편할 수 있다.

 

LerpAngle 메서드를 이용하면 부모 - 자식 관계가 아닌 독립된 상태에서 단진자 운동을 구현할 수 있다.

 

Mathf.LerpAngle도 Quaternion.Lerp와 원리가 같으므로, 시작 각도, 도착 각도가 필요하다.

그리고 최초 세 점을 지나는 평면(pivot, pivot 바로 아래, 단진자)을 저장한다.

pivot → oscillator 벡터의 최초 상태는 startPO에 저장하고 두 오브젝트의 사이도 distance에 저장해둔다.

    float startAngle, endAngle;
    Vector3 startPlaneNormal;
    
    Vector3 startPO;
    float distance;

 

angle은 처음 0º에서 2Θ를 움직이게 된다. 

normal은 이전에 만들어 둔 getNormal로 구한다.

    void Start()
    {   
        float angle = Vector3.Angle(this.transform.position - pivot.position, Vector3.down);

        startAngle = 0.0f; endAngle = angle * 2;

        Vector3 normal 
            = getNormal(pivot.position + Vector3.down, pivot.transform.position, this.transform.position);

        startPlaneNormal = normal;

        setPlane(pivot.position + Vector3.down, pivot.transform.position, this.transform.position);

        startPO = (this.transform.position - pivot.position).normalized;
        distance = Vector3.Distance(this.transform.position, pivot.position);
    }

 

시간에 따라 angle이 변화하게 될 것이고, 이 angle을 평면에 대한 노멀 벡터에 대해 회전시킨다.

pivot → oscillator 벡터가 AngleAxis에 의해 회전될 것이고, 

pivot의 위치에서 변화한 벡터에 distance만큼 곱해서 더해주면 현재의 진자의 위치가 될 것이다.

    void FixedUpdate()
    {
        startRot += (Time.fixedDeltaTime * speed);

        float angle 
            = Mathf.LerpAngle(startAngle, endAngle, (Mathf.Sin(startRot - Mathf.PI / 2) + 1.0f) / 2.0f);

        Vector3 vPO 
            = Quaternion.AngleAxis(angle, startPlaneNormal) * startPO;

        this.transform.position 
            = pivot.transform.position + (vPO).normalized * distance;
    }

 

즉, pivot을 회전시키는 것이 아니라, 단진자의 위치 자체를 직접 수정하므로 부모-자식 관계를 맺을 필요가 없다.

 

게임을 실행하면 pivot은 회전하지 않고 단진자만 움직인다.

 

전체 코드는 다음과 같다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExpandedSimpleHarmonicOscillator : MonoBehaviour
{
    public Transform pivot;
    public float speed = 2.0f;
    public GameObject plane;

    float startAngle, endAngle;
    Vector3 startPlaneNormal;

    Vector3 startPO;
    float distance;

    public GameObject lineRenderer;
    LineRenderer[] lrs;

    float startRot = 0.0f;
    
    void setLineRenderer(LineRenderer lr, Color color)
    {
        lr.startWidth = lr.endWidth = .1f;
        lr.material.color = color;

        lr.positionCount = 2;
    }

    Vector3 getNormal(Vector3 a, Vector3 b, Vector3 c)
    {
        Plane p = new Plane(a, b, c);
        return p.normal;
    }

    void setPlane(Vector3 a, Vector3 b, Vector3 c)
    {
        plane.transform.position = a;
        plane.transform.up = getNormal(a, b, c);
    }

    void Start()
    {   
        float angle = Vector3.Angle(this.transform.position - pivot.position, Vector3.down);

        startAngle = 0.0f; endAngle = angle * 2;

        Vector3 normal 
            = getNormal(pivot.position + Vector3.down, pivot.transform.position, this.transform.position);

        startPlaneNormal = normal;

        setPlane(pivot.position + Vector3.down, pivot.transform.position, this.transform.position);

        startPO = (this.transform.position - pivot.position).normalized;
        distance = Vector3.Distance(this.transform.position, pivot.position);

        lrs = lineRenderer.GetComponentsInChildren<LineRenderer>();
        setLineRenderer(lrs[0], Color.blue);
        lrs[0].SetPosition(0, pivot.position);
    }

    void Update()
    {
        lrs[0].SetPosition(1, this.transform.position);
    }

    void FixedUpdate()
    {
        startRot += (Time.fixedDeltaTime * speed);

        float angle 
            = Mathf.LerpAngle(startAngle, endAngle, (Mathf.Sin(startRot - Mathf.PI / 2) + 1.0f) / 2.0f);

        Vector3 vPO 
            = Quaternion.AngleAxis(angle, startPlaneNormal) * startPO;

        this.transform.position 
            = pivot.transform.position + (vPO).normalized * distance;
    }
}

 

위의 실행결과는 아래의 unitypackage에서 확인 가능하다.

ExpandedSimpleHarmonicOscillator.unitypackage
0.03MB

 
반응형

댓글