개발/Unity

유니티 - 라인 렌더러로 간단한 화살표 만들기 (Make Simple Arrow with LineRenderer)

피로물든딸기 2023. 4. 3. 12:19
반응형

Unity 전체 링크

 

참고

- 쿼터니언으로 구현한 단진자의 운동
- 쿼터니언으로 구현한 단진자의 운동 확장
- 독립된 단진자의 운동

- 런타임에 단진자 운동 설정하기

- 라인 렌더러를 이용한 단진자 속도의 시각화

- 라인 렌더러로 간단한 화살표 만들기

- 운동하는 단진자의 로프를 끊기

 

라인 렌더러를 이용해 간단한 화살표를 만들어보자.

 

이전 글에서 라인 렌더러로 선을 그었으니 그 반대의 방향으로 선을 만들 수 있다.

 

이 벡터를 양 옆으로 정해진 각도만큼 움직인다.

 

그러면 3개의 점으로 삼각형을 추가하면 화살표가 만들어진다.

 

코드로 보면 다음과 같다.

여기서 기준이 되는 normal은 startPlaneVector가 된다.

    void makeArrow(LineRenderer lr, Vector3 start, Vector3 end, Vector3 normal)
    {
        Vector3 vES = (start - end) * ratio;
        Vector3 dot1 = end + Quaternion.AngleAxis(theta, normal) * vES;
        Vector3 dot2 = end + Quaternion.AngleAxis(360 - theta, normal) * vES;

        lr.positionCount = 5;

        lr.SetPosition(0, start);
        lr.SetPosition(1, end);
        lr.SetPosition(2, dot1);
        lr.SetPosition(3, dot2);
        lr.SetPosition(4, end);
    }

 

속도의 방향에 알맞게 화살표가 생성된 것을 알 수 있다.

그리고 ratio와 theta를 조절해서 화살표의 크기를 적절히 조절할 수 있다.

 

전체 코드는 다음과 같다.

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 gravity = 1.0f;

    float startAngle, endAngle;
    Vector3 startPlaneNormal;

    Vector3 startPO;
    float distance;

    public GameObject lineRenderer;
    LineRenderer[] lrs;

    float startRot = 0.0f;

    bool isDragging;

    [Range(0.0f, 45.0f)]
    public float theta = 30.0f;
    [Range(0.0f, 1.0f)]
    public float ratio = 0.1f;

    float totalEnergy;
    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 OnMouseDown()
    {
        isDragging = true;
    }

    void OnMouseDrag()
    {
        float distance = Camera.main.WorldToScreenPoint(transform.position).z;

        Vector3 mousePos = new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance);
        Vector3 objPos = Camera.main.ScreenToWorldPoint(mousePos);

        objPos.x = 0;
        this.transform.position = objPos;
    }

    void OnMouseUp()
    {
        initSettings();
        isDragging = false;
    }

    void initSettings()
    {
        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);

        startRot = 0.0f;

        totalEnergy = this.transform.position.y; // Potential Energy = mgh
    }

    void Start()
    {
        initSettings();

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

        setLineRenderer(lrs[1], Color.red);     
    }

    int getCurrentState()
    {
        float hPi = Mathf.PI / 2.0f;
        return (int)(startRot / hPi) % 4;
    }

    float getNormalDirection()
    {
        return getCurrentState() < 2 ? 1.0f : -1.0f;
    }

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

    void makeArrow(LineRenderer lr, Vector3 start, Vector3 end, Vector3 normal)
    {
        Vector3 vES = (start - end) * ratio;
        Vector3 dot1 = end + Quaternion.AngleAxis(theta, normal) * vES;
        Vector3 dot2 = end + Quaternion.AngleAxis(360 - theta, normal) * vES;

        lr.positionCount = 5;

        lr.SetPosition(0, start);
        lr.SetPosition(1, end);
        lr.SetPosition(2, dot1);
        lr.SetPosition(3, dot2);
        lr.SetPosition(4, end);
    }

    void FixedUpdate()
    {
        if (isDragging) return;

        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;

        float kinetic = gravity * (totalEnergy - this.transform.position.y);
        float v = Mathf.Sqrt(2 * kinetic); // k = 1/2 m v^2 -> v = sqrt(2k/m)
        Vector3 verocity 
            = (Quaternion.AngleAxis(90, startPlaneNormal) * vPO).normalized * getNormalDirection();

        makeArrow(lrs[1], this.transform.position, this.transform.position + verocity * v, startPlaneNormal);
    }
}

 

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

ArrowWithLineRenderer.unitypackage
0.03MB

 

반응형