다양한 선 그리기

# 학습 과정 소개

LineRendererComponent를 활용해 다양한 선을 월드에 자유롭게 그릴 수 있습니다.

# LineRendererComponent

LineRendererComponent는 점의 개수를 지정해 MapleTile, RectTile 위에서 원하는 직선, 곡선을 그릴 수 있습니다. LineRendererComponent 하나당 하나의 선을 그릴 수 있으므로, 여러 선을 표현하고 싶다면 여러 개의 LineRendererComponent가 포함된 엔티티를 사용해야 합니다. 주요 프로퍼티는 아래와 같습니다.

#### IsSmooth

활성화 시 부드러운 곡선을 그려지고, 비활성화 시 날카로운 꺾은 선으로 그립니다. [png](1657949398141826c485f476c4c54b73aea6bfb6198d6)

#### Loop

활성화 시 시작점과 끝점을 이어 닫힌 선을 그려줍니다. 기본값은 비활성화 상태입니다. [png](1657949416703539389372f8641d7b98f05d5d5cb287e)

#### Points

선을 구성하는 정점들의 집합입니다.

  * Points Edit: [Edit] 버튼을 눌러 편집을 활성화할 수 있습니다. Scene에서 Point 위치를 수정 및 추가, 삭제할 수 있습니다.
  * Size: 선을 연결하는 점의 개수를 의미합니다. 0부터 시작하며, Size에 입력한 값만큼 점이 추가됩니다.
  * [숫자]: 선의 연결점 개수를 의미합니다.
  * Color: 선 색상을 지정합니다. 하나의 컴포넌트에서 Size마다 다른 색을 지정하면, 색이 교차하는 지점은 자연스럽게 섞여 보입니다 [png](16944139559805794266b75bd4670835226315bec0354)
  * Position: 선을 연결하는 점의 위치를 나타냅니다. 최소 2개를 만들어야 선이 그려집니다. 가장 처음 [1]을 (0,0) 기준으로 삼아 x, y 값을 변경해 선을 그릴 수 있습니다.
  * Width:선의 두께를 지정합니다. 수가 클수록 두꺼워집니다.  
[png](1657949740811c064ec15791d40c0a2fc0c98dbbf57b6)



# 월드에 선 그리기

#### 직선 그리기

Points 2개를 이용해 Position을 정하고 직선을 그릴 수 있습니다.

  1.  EmptyEntity를 생성하고, LineRendererComponent를 추가합니다.
  2. Size에 3을 입력하고, Color와 Width를 지정합니다.
  3. 각각의 Position에 (0,0), (4,0)을 입력합니다. [png](166358584146243474ff4f1644094891eed7a6da66834)



#### 삼각형 그리기

Points 3개를 활용해 시작점과 끝점을 이어 삼각형을 그릴 수 있습니다.

  1.  EmptyEntity를 생성하고, LineRendererComponent를 추가합니다.
  2. Size에 3을 입력하고, Color와 Width를 지정합니다.
  3. 각각의 Position에 (0,0), (1,1), (2,0)을 입력합니다.
  4. Loop 프로퍼티를 활성화합니다. [png](1663844235905ace8de03857e44f1b9c55a8c83d2199d)



#### 곡선으로 모양 그리기

Points 4개와 IsSmooth 프로퍼티를 활용해 선을 곡선으로 그릴 수 있습니다.

  1.  EmptyEntity를 생성하고, LineRendererComponent를 추가합니다.
  2. Size에 4을 입력하고, Color와 Width를 지정합니다.
  3. 각각의 Position에 (0,0), (1,1), (2,-1), (3,0)을 입력합니다.
  4. IsSmooth와 Loop 프로퍼티 활성화합니다. [png](16579497886734ab7d8aba66a4ceab4f245a45535c6cd)



#### 머티리얼을 사용해 그리기

머티리얼의 SpritePattern 셰이더를 활용해 선을 그릴 수 있습니다. 머티리얼 활용 방법은 [머티리얼 활용하기](docs/?postId=828)를 참고하세요. LineRendererComponent에 활용하는 스프라이트로는 유저 리소스와 복제한 MSW 리소스를 사용할 수 있습니다. MSW 리소스 복제 방법은 [리소스 관리](docs/?postId=690)를 참고하세요.

  1. Workspace - MyDesk - Create Material을 선택해 새로운 Material을 만듭니다.
  2. 생성한 머티리얼의 프로퍼티 창에서 Shader - LineRenderer - SpritePattern를 선택합니다.
  3. SpriteRUID에 사용할 이미지를 선택합니다. 필요한 경우 Offset, Scale 값을 조정합니다.
  4.  EmptyEntity를 생성하고, LineRendererComponent를 추가합니다.
  5. LineRendererComponent의 MaterialId에서 을 눌러 머티리얼을 지정합니다.
  6. Size와 Position 값을 변경해 선을 그립니다. [png](16842378305042dc663a11c3b49eebd58b8804d407e88)



> Tip. 유저 리소스와 복제한 MSW 리소스 모두 랩 모드를 Repeat로 설정해야 합니다. [png](168422631874542cfe7bb765e4d659b113e430d223b99)

> Tip. LinePoints 수가 10000개가 넘으면, 그 이후의 LinePoints는 선이 그려지지 않습니다. 또한 IsSmooth를 사용하면 그릴 수 있는 LinePoints 총 개수는 약 1000개로 감소합니다. 

# 활용 예시

플레이어를 따라 다니는 모델을 만들어, 플레이어의 움직임을 따라 선이 그려지고, 사라지게 만들어봅시다.

[gif](1657952569571be162fe5c3c64716b82f6a51e091f1cc)

  1.  Trail 모델을 생성하고, TransformComponent와 LineRendererComponent를 추가합니다.
  2. 새로운  TrailDrawer 컴포넌트를 생성하고, Trail 모델에 추가합니다. 
         
         @Component
         script TrailRenderer extends Component
         @ExecSpace("ClientOnly")
         method void OnUpdate(number delta)
         self:EraseTail(delta)
         
         local currPos = self.Target.TransformComponent.WorldPosition
         local dist = Vector3.Distance(self.PrevPos, currPos)
         
         if dist < self.Threshold then
             return
         end
         
         self.PrevPos = currPos
         self:AddPoint(currPos)
         end
         
         method void EraseTail(number delta)
         local currPos = self.Target.TransformComponent.WorldPosition
         
         for i = 1, #(self.PointLives) do
             local life = self.PointLives[i]
             life = life - delta
             self.PointLives[i] = life
         end
         
         if #(self.PointLives) == 1 then
             self:RemovePointAt(1)
             self:AddPoint(currPos)
             return
         end
         
         if self.PointLives[1] <= 0 then
             self:RemovePointAt(1)
         end
         end
         
         method void AddPoint(Vector3 pos)
         local line = self.Entity.LineRendererComponent
         local pointPos = Vector2(pos.x, pos.y + 0.25)
         
         -- 랜덤으로 색상 그리기
         local r = _UtilLogic:RandomDouble()
         local g = _UtilLogic:RandomDouble()
         local b = _UtilLogic:RandomDouble()
         line.Points:Add(LinePoint(pointPos, Color(r, g, b), 1))
         table.insert(self.PointLives, self.PointLife)
         end
         
         method void RemovePointAt(number idx)
         -- 선 삭제
         table.remove(self.PointLives, idx)
         self.Entity.LineRendererComponent.Points:RemoveAt(idx)
         end
         
         method void Init(Entity target)
         self.Target = target
         
         local pos = target.TransformComponent.WorldPosition
         
         self.PrevPos = pos
         
         self:AddPoint(pos)
         end
         end
         



    
    
    3. 새로운  TrailSpawner 컴포넌트를 생성하고,  DefaultPlayer에 추가합니다.
    4.  TrailSpawner 컴포넌트에 움직임에 따라 선이 스폰되도록 `SpawnService`를 활용해 아래와 같이 작성합니다. SpawnByModelId에는   Trail의 ID를 입력해야합니다. 예시의 ID를 사용하면 정상 동작하지 않으므로 주의해 작성하세요. 
        ```lua
    @Component
    script TrailManager extends Component
        property Entity TrailEntity = nil
    
        @ExecSpace("ClientOnly")
        method void OnBeginPlay()
        self.TrailEntity = _SpawnService:SpawnByModelId("8ae67c69-32ac-40e3-82b1-03d1409be467", "Trail", Vector3.zero, nil)
        self.TrailEntity.TrailDrawer:Init(self.Entity)
        end
    
        method void OnEndPlay()
        if self.TrailEntity ~= nil then
        	self.TrailEntity:Destroy()
        end
        end
    end
    

##### 참고 가이드

  * [RectTileMap의 활용](/docs/?postId=589)
  * [엔티티의 위치, 크기, 회전 조정](/docs/?postId=82)