Unity / 베지어곡선이용해서 라인렌더로 곡선 그리기

2022. 8. 16. 23:27유니티 unity

 

참고 주소

https://www.youtube.com/watch?v=pnYccz1Ha34 

https://blog.coderifleman.com/2016/12/30/bezier-curves/

https://suhak.tistory.com/1211

 

베지어 곡선을 이용해서 곡선을 그려보겠습니다.

 

베지어 곡선이란?

프랑스의 공학자 피에르 베지에(Pierre Bézier)의 이름을 딴 곡선. 영어식으로 베지어 곡선으로 읽기도 한다. 2개 이상의 점으로 정의되는 매개변수 곡선이며, 점 몇개로 곡선을 특정할 수 있는 성질 때문에 CAD, 컴퓨터 그래픽 등 컴퓨터 환경, 특히 벡터 그래픽에서 곡선을 표현하는 데 널리 쓰인다.

 

베지어 곡선을 글로만 설명을 들으면 헷갈린데 그림으로 보면 엄청 쉽게 이해가 됩니다.

2차 베지어 곡선

 

 

3차 베지어 곡선

출처 https://blog.coderifleman.com/2016/12/30/bezier-curves/

 

3차 베지어 곡선의 공식(점 4개가 주어졌을 때)

https://namu.wiki/w/%EB%B2%A0%EC%A7%80%EC%97%90%20%EA%B3%A1%EC%84%A0

 

n차 베지어 곡선은 점화식으로 정의가 가능합니다.

 

https://suhak.tistory.com/1211

 

 

이제 이걸 유니티로 구현을 해야 하는데 다음처럼 구현을 하면 됩니다.

 

//점이 4개있는 3차 베지어 공식
public float Bezier(float P0, float P1, float P2, float P3,float t)
{
    return Mathf.Pow((1 - t), 3) * P0 + Mathf.Pow((1 - t), 2) * 3 * t * P1 + Mathf.Pow(t, 2) * 3 * (1 - t) * P2 +
           Mathf.Pow(t, 3) * P3;
}

 

유니티 함수 Lerp를 이용하면 좀 더 보기 쉽게 구현이 가능합니다.

출처 :https://blog.coderifleman.com/2016/12/30/bezier-curves/

public Vector3 Bezier(Vector3 P0, Vector3 P1, Vector3 P2, Vector3 P3, float t)
{
    Vector3 M0 = Vector3.Lerp(P0, P1, t);
    Vector3 M1 = Vector3.Lerp(P1, P2, t);
    Vector3 M2 = Vector3.Lerp(P2, P3, t);

    Vector3 B0 = Vector3.Lerp(M0, M1, t);
    Vector3 B1 = Vector3.Lerp(M1, M2, t);

    return Vector3.Lerp(B0, B1, t);
}

 

 

화면으로 표시하기 위해서 4개의 오브젝트를 만들었습니다.

베지어 곡선을 사용할 때는 P1과 P2의 위치만 따로 넣어주면 됩니다.

단순 곡선만 사용하려면 P0기준으로 높이 P3기준으로 높이만 설정하면 됩니다.

 

 

 

이제 베지어 함수를 가지고 라인 렌더를 설정하면 됩니다.

라인 렌더의 Positions을 균등하게 넣어주면 됩니다.

20개 정도가 깔끔하게 나오는 거 같습니다.

 

public LineRenderer line;//라인렌더
[Min(2)]
public int lineCnt = 20;//라인 수
    
    
    
for (int i = 0; i < lineCnt; i++)
{
    float t;
    if (i == 0)
    {
        t = 0;
    }
    else
    {
        t = (float)i / (lineCnt - 1);
    }

    Vector3 bezier = Bezier(P0_GameObject.transform.position, P1_GameObject.transform.position, P2_GameObject.transform.position,
        P3_GameObject.transform.position, t);

    /*
     bezier2 를 써도 됩니다.
    Vector3 bezier2;
    bezier2.x=Bezier(P0_GameObject.transform.position.x, P1_GameObject.transform.position.x, P2_GameObject.transform.position.x,
        P3_GameObject.transform.position.x, t);
    bezier2.y=Bezier(P0_GameObject.transform.position.y, P1_GameObject.transform.position.y, P2_GameObject.transform.position.y,
        P3_GameObject.transform.position.y, t);
    bezier2.z=Bezier(P0_GameObject.transform.position.z, P1_GameObject.transform.position.z, P2_GameObject.transform.position.z,
        P3_GameObject.transform.position.z, t);


    */

    line.SetPosition(i, bezier); // 라인을 설정합니다
}

 

 

 

라인 수가 3개 일 때

 

 

라인 수가 20개 일 때

 

이걸 활용해서 수류탄 궤적이나 점프 궤적을 그릴 수 있고 포탄의 포물선이동도 구현이 가능합니다.

 

P1와 P2의 위치를 일정범위로 랜덤 하게 조절한다면 롤의 카이사 Q 스킬처럼 미사일이 나갈 수 있습니다.

롤 카이사Q
개인프로젝트에서 사용했던 베지어곡선 함수

 

 

코드 보기

더보기
using UnityEngine;

public class Curve : MonoBehaviour
{
    public LineRenderer line;//라인렌더
    [Min(2)]
    public int lineCnt = 20;//라인 수

    public Vector3 p1Pos=new Vector3(0,5,0); // P0기준으로 p1 더하기
    public Vector3 p2Pos=new Vector3(0,5,0);// P3기준으로 p2 더하기

    public GameObject P0_GameObject;
    public GameObject P1_GameObject;
    public GameObject P2_GameObject;
    public GameObject P3_GameObject;

    void Update()
    {
        LineDraw();
    }

    public Vector3 Bezier(Vector3 P0, Vector3 P1, Vector3 P2, Vector3 P3, float t)
    {
    //gif를 보시면 좀더 이해하기 쉽습니다.
        Vector3 M0 = Vector3.Lerp(P0, P1, t);
        Vector3 M1 = Vector3.Lerp(P1, P2, t);
        Vector3 M2 = Vector3.Lerp(P2, P3, t);

        Vector3 B0 = Vector3.Lerp(M0, M1, t);
        Vector3 B1 = Vector3.Lerp(M1, M2, t);

        return Vector3.Lerp(B0, B1, t);
    }

    //점이 4개있는 3차 베지어 공식
    public float Bezier(float P0, float P1, float P2, float P3,float t)
    {
        return Mathf.Pow((1 - t), 3) * P0 + Mathf.Pow((1 - t), 2) * 3 * t * P1 + Mathf.Pow(t, 2) * 3 * (1 - t) * P2 +
               Mathf.Pow(t, 3) * P3;
    }
        
        

    void LineDraw()
    {
        line.positionCount = lineCnt;
        //라인렌더러 라인수 설정

        P1_GameObject.transform.position = P0_GameObject.transform.position + p1Pos;
        //보여주기식 P1를 설정한다
        P2_GameObject.transform.position = P3_GameObject.transform.position + p2Pos;
        //보여주기식 P2를 설정한다
        
        
        for (int i = 0; i < lineCnt; i++)
        {
            float t;
            if (i == 0)
            {
                t = 0;
            }
            else
            {
                t = (float)i / (lineCnt - 1);
            }

            Vector3 bezier = Bezier(P0_GameObject.transform.position, P1_GameObject.transform.position, P2_GameObject.transform.position,
                P3_GameObject.transform.position, t);

            /*
             bezier2 를 써도 됩니다.
            Vector3 bezier2;
            bezier2.x=Bezier(P0_GameObject.transform.position.x, P1_GameObject.transform.position.x, P2_GameObject.transform.position.x,
                P3_GameObject.transform.position.x, t);
            bezier2.y=Bezier(P0_GameObject.transform.position.y, P1_GameObject.transform.position.y, P2_GameObject.transform.position.y,
                P3_GameObject.transform.position.y, t);
            bezier2.z=Bezier(P0_GameObject.transform.position.z, P1_GameObject.transform.position.z, P2_GameObject.transform.position.z,
                P3_GameObject.transform.position.z, t);
            
            
            */
            
            line.SetPosition(i, bezier); // 라인을 설정합니다
        }
    }
}