ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Unity] Line Renderer로 직선 궤적 그리기 - Unity Trajectory Line
    쾌락없는 책임 (공부)/Unity 2022. 5. 6. 20:05
    반응형

    개요

     현재 개발중인 프로젝트에서 주인공이 '여의주를 발사' 하는 기능이 있습니다. 이 기능을 만들긴 했는데 뭔가 이후 추가될 이지모드에서 이 예측선을 만들어 줘야할 필요가 있었고 몇번의 시행착오 끝에 이 기능을 만들게 되어서 정리하기 위해 글을 쓰게 되었습니다.

     

     한국어로 된 자료는 많이 없더라구요. 그래서 기능 만드실 분들에게 조금이나마 아이디어를 줄 수 있으면 좋겠습니다.

     

    실제 구현

     실제 구현은 Line Renderer + Raycast의 조합으로 만들어졌습니다. 그리고 2D 환경에서 만들었다는 점 알아두시면 됩니다.

     위 움짤이 실제 결과물로 대충 저런 느낌이라고 봐주시면 됩니다. 충돌 횟수가 정해져 있기 때문에 1쿠션만 하고 여의주가 멈추는 기능을 목표로 설명하겠습니다.

     

     구현은 어렵지 않습니다. LineRenderer 를 사용해서 일단 발사 마우스 방향으로 선을 그려주시면 됩니다. 단 단순 플레이어와 마우스를 이어주기만 하면 마우스 뒤로 선이 그려지지 않게 됩니다. 따라서 Raycast를 통해서 플레이어에서 마우스 방향으로 ray를 쏘고 그 ray가 충돌하는 곳과 플레이어를 이어주는 line을 그리면 됩니다.

            // Draw Prediction Line
            predictionLine.SetPosition(0, this.transform.position);
            predictionHit = Physics2D.Raycast(transform.position, transform.right, Mathf.Infinity, predictionLayerMask);
    
            if(predictionHit.collider == null)
            {
                // no collision => don't draw prediction line
                predictionLine.enabled = false;
                return;
            }
    
            // draw first collision point
            predictionLine.SetPosition(1, predictionHit.point);

     일단 LineRenderer의 포지션 인덱스 0은 플레이어로 지정한 뒤 이후 1번 인덱스를 ray 가 충돌한 지점으로 설정해 줍니다. 만일 이 과정에서 충돌지점이 없다면 그리지 않고 return 해 함수를 종료할 뿐인 코드입니다.

     

            // calculate second ray by Vector2.Reflect
            var inDirection = (predictionHit.point - (Vector2)transform.position).normalized;
            var reflectionDir = Vector2.Reflect(inDirection, predictionHit.normal);

     이후 첫번째 충돌 지점에서 반사되는 방향을 구해야 합니다. 이를 위해서 위와 같은 식을 준비해야 합니다. inDirection의 경우 플레이어와 해당 지점의 벡터를 구해 이후 Vector2.Reflect의 입사각으로 사용하게 됩니다. 

     위에서 구한 입사각과 충돌 표면의 normal 벡터를 Vector2.Reflect 함수에 넣으면 알아서 계산을 해 줍니다.

     

    Unity - Scripting API: Vector2.Reflect

    Success! Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable. Close

    docs.unity3d.com

     공식 문서에서 자세한 설명은 없지만 아무튼 넣으면 쉽게 반사각을 계산해 주게 됩니다.

     

            // By multiply 0.001, can have detail calculation
            predictionHit = Physics2D.Raycast(predictionHit.point + (reflectionDir * 0.001f), reflectionDir, Mathf.Infinity, predictionLayerMask);
    
            if(predictionHit.collider == null)
                return;
            
            predictionLine.SetPosition(2, predictionHit.point);
    
            // finally render linerenderer
            predictionLine.enabled = true;

     그런 다음 위와 같이 한번 더 Raycast를 수행하고 그 ray의 충돌 지점을 linerenderer의 2번째 인덱스 point로 주면 됩니다. 

     여기서 (reflectionDir * 0.001f)를 더해 보정해주지 않으면 처음 충돌과 2번째 충돌이 동일한 지점이라고 나오는 경우가 있습니다. 약간의 보정을 더해서 이런 경우를 없애고자 더해준 것입니다.

     

     여기까지 이해가 잘 안된다면 전체 코드를 보면서 한번 보시면 이해가 잘 될거라고 믿고 있습니다. 글을 잘 못쓴다는 점은 양해 부탁드립니다.

        /* Draw launch prediction line */
        private void DrawPredictionLine()
        {
            // Draw Prediction Line
            predictionLine.SetPosition(0, this.transform.position);
            predictionHit = Physics2D.Raycast(transform.position, transform.right, Mathf.Infinity, predictionLayerMask);
    
            if(predictionHit.collider == null)
            {
                // no collision => don't draw prediction line
                predictionLine.enabled = false;
                return;
            }
    
            // draw first collision point
            predictionLine.SetPosition(1, predictionHit.point);
    
            // calculate second ray by Vector2.Reflect
            var inDirection = (predictionHit.point - (Vector2)transform.position).normalized;
            var reflectionDir = Vector2.Reflect(inDirection, predictionHit.normal);
    
            // By multiply 0.001, can have detail calculation
            predictionHit = Physics2D.Raycast(predictionHit.point + (reflectionDir * 0.001f), reflectionDir, Mathf.Infinity, predictionLayerMask);
    
            if(predictionHit.collider == null)
                return;
            
            predictionLine.SetPosition(2, predictionHit.point);
    
            // finally render linerenderer
            predictionLine.enabled = true;
        }

     

    반응형

    댓글

Designed by Tistory.