스킬 이펙트를 뿌려보자!

# 학습 과정 소개

메이플스토리 월드에서는 메이플스토리에서 사용되었던 수많은 이펙트 리소스들을 제공하고 있습니다. 그리고 우리는 이러한 이펙트들을 적절한 시점과 위치에 호출해 줌으로써 한층 더 멋진 연출을 구사할 수 있습니다. 이번 과정에서는 특정 키를 누를 때마다 스킬 이펙트가 노출되는 예제를 진행해봄으로써 스크립트를 통해 이펙트를 호출하는 방법에 대해 알아보도록 합니다. [gif](1657073589665cedf2671ce1f46b1966a5a7b8ce7817f)   
다음 가이드를 먼저 학습하면 이번 과정을 진행하는 데 많은 도움이 될 것입니다.  

# EffectService

이펙트를 호출하기 위해 메이플스토리 월드가 제공하고 있는 서비스 중 _EffectService를 간단히 알아보도록 하겠습니다. _EffectService는 이펙트 제어 함수를 가진 서비스입니다. 이펙트 호출을 위한 함수로는 `PlayEffect`와 `PlayEffectAttached` 함수가 있습니다. 각 함수의 기능은 다음과 같습니다.   


##### PlayEffect (string animationClipRUID, Entity instigator, Vector3 position, number zRotation, Vector3 scale, boolean isLoop = False)

  * 이펙트를 고정된 특정 위치에 원하는 크기로 호출할 수 있습니다.
  * 매개 변수 : 
    * animationClipRUID : 호출하려는 이펙트 리소스의 RUID 입니다.
    * instigator : 맵 정보를 받아오기 위한 엔티티로, 이펙트가 호출될 맵과 동일한 맵에 존재하는 엔티티를 넣어줍니다.
    * position : 이펙트가 호출될 위치 벡터를 입력합니다.
    * zRotation : 회전 값을 넣어줍니다. 회전시키지 않으면 0을 넣어줍니다.
    * scale : 호출될 이펙트의 크기 벡터값을 넣어줍니다. 기본적으로는 Vector3(1, 1, 1)을 넣어줍니다.
    * isLoop : true로 넣어주면 이펙트가 무한으로 재생되고 false로 넣으면 이펙트가 한 번 재생됩니다.



##### PlayEffectAttached(string animationClipRUID, Entity parentEntity, Vector3 localPosition, number localZRotation, Vector3 localScale, boolean isLoop = False)

  * `PlayEffect`와 같이 이펙트를 호출하지만, 호출될 이펙트의 부모 엔티티를 선정하고 부모의 위치를 기준으로 호출될 위치를 설정합니다.
  * 매개 변수 : 
    * animationClipRUID : 호출하려는 이펙트 리소스의 RUID 입니다.
    * parentEntity : 호출될 이펙트의 부모가 될 엔티티를 넣어줍니다. 호출된 이펙트는 해당 엔티티의 자식으로 생성됩니다.
    * localPosition : 부모 엔티티의 position을 기준으로 얼마만큼 떨어진 곳에서 호출할 것인지에 대한 위치 벡터값을 넣어줍니다. 기본적으로 Vector3(0,0,0)을 넣어주면 부모 엔티티와 같은 위치에 이펙트가 생성됩니다.
    * localZRotation : 부모 엔티티의 회전값을 기준으로 얼마나 더 회전할 것인가를 입력합니다.
    * localScale : 호출될 이펙트의 크기 벡터값을 넣어줍니다. 기본적으로는 Vector3(1, 1, 1)을 넣어줍니다.
    * isLoop : true로 넣어주면 이펙트가 무한으로 재생되고 false로 넣으면 이펙트가 한 번 재생됩니다.



이펙트를 호출하는 함수에 대해 간략히 알아보았으니 본격적으로 예제를 살펴봅시다.

# 키 입력 기능 만들기

스킬 호출 기능에 앞서 간단하게 키 입력을 받는 기능을 먼저 만들어보겠습니다.

  1. Workspace에서 새 컴포넌트를 추가하고, 이름으로 NewSkill을 입력합니다. [png](16878457449028bebb7ed9b9141288f93dd176baca7c5)   

  2. NewSkill 컴포넌트에 키 입력 이벤트를 처리할 핸들러를 추가합니다. [+] 버튼을 눌러 KeyDownEvent를 추가합니다.   

  3. HandleKeyDownEvent의 코드를 다음과 같이 작성해 줍니다. 
         
         @Component
         script HotkeyInputHandler extends Component
         @EventSender("Service", "InputService")
         handler HandleKeyDownEvent(KeyDownEvent event)
         -- Parameters
         local key = event.key
         --------------------------------------------------------
         if key == KeyboardKey.Q then
             log("Q")   
         elseif key == KeyboardKey.W then
             log("W")
         elseif key == KeyboardKey.E then
             log("E")
         elseif key == KeyboardKey.R then
             log("R")
         end
         end
         end
         



    
    
          
    
    4. **Workspace - DefaultPlayer**의 프로퍼티 에디터에서 **NewSkill** 컴포넌트를 추가합니다.
        [png](163548611402098c3ded642444abfadea659e0d4c11e4)
          
    
    5.  **[시작]** 버튼을 누른 뒤 테스트해 봅시다.
        Q, W, E, R 버튼을 차례로 눌러보고 콘솔 창에 로그가 잘 출력되는지 확인합니다.
        [png](168784592446662b0ed4106354178bf8d999b4e63ce09)
    # 키 입력할 때마다 이펙트 호출하기
    
    위에 이어서 이번에는 각 키를 입력할 때마다 다음의 스킬 이펙트가 호출되도록 만들어보겠습니다.
    [png](16570736554871ba7e07a399d41e9ae395e244a86397f)
      
    
    우선 **_EffectService**의 함수 중 `PlayEffect` 함수를 이용해 스크립트를 작성해보겠습니다.
    1. **NewSkill** 컴포넌트의 **HandleKeyDownEvent**를 다음과 같이 수정해줍니다.
        ```lua
    @Component
    script KeyEffectTrigger extends Component
        @EventSender("Service", "InputService")
        handler HandleKeyDownEvent(KeyDownEvent event)
        -- Parameters
        local key = event.key
        --------------------------------------------------------
        local transform = self.Entity.TransformComponent
        local x = transform.Position.x
        local y = transform.Position.y
        local z = transform.Position.z
    
        if key == KeyboardKey.Q then
            _EffectService:PlayEffect("f396262ddb6e4d5581360496bb4e9f86",self.Entity,Vector3(x,y,z), 0, Vector3(1,1,1), false)
        elseif key == KeyboardKey.W then
            _EffectService:PlayEffect("aae828e6daa342a996320e32dc5c2ab6",self.Entity,Vector3(x,y,z), 0, Vector3(1,1,1), false)
        elseif key == KeyboardKey.E then
            _EffectService:PlayEffect("0da34c3dd7ba4c2890107d743b8c7a97",self.Entity,Vector3(x,y,z), 0, Vector3(1,1,1), false)
        elseif key == KeyboardKey.R then
            _EffectService:PlayEffect("5f67e1efe0b84d9c9111a21ecfedc937",self.Entity,Vector3(x,y,z), 0, Vector3(1,1,1), false)
        end
        end
    end
    
    
    
      
    
    

  2.  [시작] 버튼을 누른 뒤 테스트해 봅시다. Q, W, E, R 버튼을 차례로 눌러 이펙트가 정상적으로 호출되는지 확인해봅니다. [gif](16570737747217dd51981074941c29e9f0fcb75018662)



# PlayEffectAttached를 이용해 이펙트 호출하기

위에서 살펴본 이펙트 호출 방법은 사실 스킬 이펙트에 적합한 방법은 아닙니다. 위의 예제처럼 구현하면 Q, W, E, R 키를 누른 후 캐릭터를 이동할 때 캐릭터가 이펙트를 벗어나 이동하게 됩니다. `PlayEffect`는 위에서 잠깐 언급한 것처럼 고정된 위치에 이펙트를 호출하는데 적합한 함수이기 때문에 버프류 스킬과 같이 항상 캐릭터 위에 떠 있어야 할 이펙트에는 적합하지 않습니다. 따라서 이러한 경우에 사용하는 함수가 바로 `PlayEffectAttached` 함수입니다. `PlayEffectAttached`의 경우 이펙트가 떠 있을 대상 엔티티, 즉 부모 엔티티를 지정할 수 있으므로 위와 같은 캐릭터와 이펙트가 분리되는 현상을 없앨 수 있습니다. 이번에는 `PlayEffect` 대신 `PlayEffectAttached`를 사용하는 것으로 스크립트를 수정해 봅시다.

  1. NewSkill 컴포넌트의 HandleKeyDownEvent를 다음과 같이 수정해줍니다. 
         
         @Component
         script SkillEffectTrigger extends Component
         @EventSender("Service", "InputService")
         handler HandleKeyDownEvent(KeyDownEvent event)
         -- Parameters
         local key = event.key
         --------------------------------------------------------
         if key == KeyboardKey.Q then
             _EffectService:PlayEffectAttached("f396262ddb6e4d5581360496bb4e9f86",self.Entity,Vector3(0,0,0), 0, Vector3(1,1,1), false)
         elseif key == KeyboardKey.W then
             _EffectService:PlayEffectAttached("aae828e6daa342a996320e32dc5c2ab6",self.Entity,Vector3(0,0,0), 0, Vector3(1,1,1), false)
         elseif key == KeyboardKey.E then
             _EffectService:PlayEffectAttached("0da34c3dd7ba4c2890107d743b8c7a97",self.Entity,Vector3(0,0,0), 0, Vector3(1,1,1), false)
         elseif key == KeyboardKey.R then
             _EffectService:PlayEffectAttached("5f67e1efe0b84d9c9111a21ecfedc937",self.Entity,Vector3(0,0,0), 0, Vector3(1,1,1), false)
         end
         end
         end
         



    
    
          
    
    2.  **[시작]** 버튼을 누른 뒤 테스트해 봅시다.
        Q, W, E, R 버튼을 차례로 눌러 이펙트가 정상적으로 호출되는지 확인해봅니다.
    [gif](1657074462609d91dd217137d4049a2aa8ff3aa5df599)