파티클 활용하기

# 학습 과정 소개

[파티클 사용하기](/docs/?postId=1036) 가이드에서 파티클 컴포넌트의 개념 및 종류에 대해 알아보았습니다. 이번 시간에는 스크립트를 사용해 파티클을 활용하는 예제를 살펴보겠습니다.

# 파티클 스크립트

## 파티클 프로퍼티 제어

캐릭터와의 거리에 따라 파티클의 재생 속도가 바뀌는 예제를 통해 파티클 프로퍼티를 제어하는 방법을 살펴봅시다.

  1. 파티클이 잘 보이도록 어두운 배경을 설정하고 지형을 배치한 뒤 원하는 파티클을 배치합니다. 본 예제에서는 Background로 back-16을 설정하고 파티클은 Nova를 배치했습니다. [png](1660613012572797db96a127d4a01a0ecd37f02c3ae02)

  2.  Workspace -  MyDesk에 새 스크립트 컴포넌트  ParticlePlaySpeed를 생성합니다.

  3. 맵에 배치했던 파티클 엔티티의 [png](Ect) ParticlePlaySpeed를 추가합니다.

  4.  ParticlePlaySpeed를 열고 `OnUpdate()` 함수를 추가하여 아래와 같이 스크립트를 작성합니다.
         
         @Component
         script ProximityParticleSpeedController extends Component
         @ExecSpace("ClientOnly")
         method void OnUpdate(number delta)
         local positionA = Vector2(self.Entity.TransformComponent.Position.x,self.Entity.TransformComponent.Position.y)
         local positionB = Vector2(_UserService.LocalPlayer.TransformComponent.Position.x,_UserService.LocalPlayer.TransformComponent.Position.y)
         local distance = Vector2.Distance(positionA, positionB)
         
         self.Entity.BasicParticleComponent.PlaySpeed = distance
         end
         end
         



    
    
    5.  시작을 누른 뒤 테스트해 봅시다. 캐릭터가 파티클에 가까이 다가갈수록 파티클이 천천히 재생되고 파티클에서 멀어질수록 빨리 재생되는 것을 확인할 수 있습니다.
        [gif](16606131392543e4d5902b9d74e7ba57dadb7ad920fb5)
    
    ## 파티클 재생 상태 변경
    특정 이벤트가 발생했을 때 파티클을 재생하거나 재생 중인 파티클을 멈추는 방법을 알아봅시다. `Play()`, `Stop()` 함수를 통해 이러한 기능을 구현할 수 있습니다. 캐릭터가 특정 엔티티와 충돌하면 파티클의 재생을 멈추는 예제를 살펴봅시다.
    
    1.  Workspace -  DefaultPlayer의  Property에서 "+" 버튼을 눌러 BasicParticleComponent를 추가합니다.
    [png](1660290482559d3eb4baea5eb46a3b16be66aba558091)
    
    2. ParticleType을 FireField로 설정합니다.
    [png](1660290572916bf1ddbdf34bb4d799dca05964b5c5712)
    
    3.  Workspace -  MyDesk에 새 스크립트 컴포넌트  ParticleController를 생성합니다.
    
    4. [png](Ect) ParticleController를 추가합니다.
    
    5.  ParticleController를 열고 Event Handler에  TriggerEnterEvent를 추가한 뒤 아래와 같이 작성합니다.
        ```lua
    @Component
    script TriggerParticleStopper extends Component
        @EventSender("Self")
        handler HandleTriggerEnterEvent(TriggerEnterEvent event)
        -- Parameters
        local TriggerBodyEntity = event.TriggerBodyEntity
        --------------------------------------------------------
        self.Entity.BasicParticleComponent:Stop()
        end
    end
    

  6. 트리거를 작동시키기 위한 엔티티를 배치하고 [png](collision) TriggerComponent를 추가합니다. 예제에서는  Preset List - object-1814를 사용했습니다. [png](16602924504597b005ed1a05d4844ae69349f39372d47)

  7.  시작을 누른 뒤 테스트해 봅시다. 캐릭터 몸에 붙어 있는 FireField 파티클이 폭포 엔티티에 닿으면 사라지는 것을 확인할 수 있습니다. [gif](16602925276347f42645d58414436a13cd40260c6744b)




# 파티클 서비스

파티클 서비스를 활용하여 파티클을 제어하는 방법을 알아봅시다.

## 일회성 파티클의 생성

  1. 파티클이 잘 보이도록 어두운 배경을 설정하고 지형을 배치한 뒤 원하는 엔티티를 배치합니다. 본 예제에서는  Preset List에서 Background - back-51을 설정하고 Object - object-174를 배치했습니다. [png](1660614857422a2b18c5225874b859938ae7d87d119e1)

  2.  Workspace -  MyDesk에 새 스크립트 컴포넌트  ParticleServiceTest를 생성합니다.

  3. 배치한 엔티티(object-174)의 [png](Ect) ParticleServiceTest를 추가합니다.

  4. 여기에 [png](Ect) TweenCircularComponent도 추가하고 아래와 같이 설정합니다. [png](166061571192425e9d621c5c54d8f8d64fad989e65a7e)   


  5.  ParticleServiceTest를 열고 `AddParticle()` 함수를 추가한 뒤 아래와 같이 작성합니다.
         
         @Component
         script FireworkPlayer extends Component
         method void AddParticle()
         local option = {["Color"] = Color(0.25,0.5,0.5, 1), ["ParticleCount"] = 3}
         
         _ParticleService:PlayBasicParticle(BasicParticleType.Firework, self.Entity, self.Entity.TransformComponent.WorldPosition, 0, Vector3(1,1,1), false, option)
         -- ParticleService의 PlayBasicParticle() 함수를 통해 파티클을 재생합니다. Loop는 false로 설정하여 파티클이 한 번씩만 재생되도록 합니다.
         end
         end
         



    
    
       
    6. `OnBeginPlay()` 함수를 추가한 뒤 아래와 같이 작성합니다.
        ```lua
    @Component
    script ParticleSpawnScheduler extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        _TimerService:SetTimerRepeat(self.AddParticle, 2, 0) 
        -- 2초에 한 번씩 AddParticle 함수가 작동하도록 합니다.
        end
    end
    

  7.  시작을 누른 뒤 테스트해 봅시다. 엔티티에서 2초에 한 번씩 파티클이 생성되는 것을 확인할 수 있습니다. [gif](1660617146781af026d51a0bc46e1acf08c4ab0ea2158)



## 반복 파티클의 생성과 삭제

반복 재생되는 파티클을 생성하거나 삭제하는 방법을 알아봅시다. 특정 엔티티에 캐릭터가 닿으면 파티클이 생성되다가 캐릭터가 목표 지점에 도착하면 파티클 생성이 멈추는 예제를 만들어보겠습니다.

  1. 파티클을 뿜어낼 엔티티와 목표 엔티티를 배치합니다. 본 예제에서는 파티클을 뿜어낼 엔티티로 object-24(호리병)을, 목표 엔티티로는 object-928(소화기)를 사용했습니다. [png](1660626870398bc48f56357f64b8896d961b26265f31a)

  2. 배치한 모든 엔티티에 [png](collision) TriggerComponent를 추가합니다.

  3.  Workspace -  MyDesk에 새 스크립트 컴포넌트  ParticleServiceTest02를 생성합니다.

  4.  ParticleServiceTest02를 열고 아래와 같이 프로퍼티를 추가합니다.
         
         @Component
         script RespawnManager extends Component
         property Entity ResetEntity = nil
         property SyncTable SpawnList
         end
         



    
    
        
    5. 캐릭터가 호리병에 닿으면 호리병에서 파티클이 생성되고, 소화기에 닿으면 파티클이 사라지도록 해봅시다.  ParticleServiceTest02의 Event Handler에  TriggerEnterEvent를 추가한 뒤 아래와 같이 작성합니다.
        ```lua
    @Component
    script ParticleCollector extends Component
        @EventSender("Self")
        handler HandleTriggerEnterEvent(TriggerEnterEvent event)
        -- Parameters
        local TriggerBodyEntity = event.TriggerBodyEntity
        --------------------------------------------------------
        if TriggerBodyEntity ~= self.ResetEntity then
           local serial = _ParticleService:PlayBasicParticleAttached(BasicParticleType.ToonTallFire, TriggerBodyEntity, Vector3.zero, 0, Vector3.one, true)
           table.insert(self.SpawnList, serial)
           -- 캐릭터가 목표 엔티티가 아닌 다른 엔티티에 닿으면 PlayBasicParticleAttached() 함수를 통해 해당 엔티티에서 파티클이 생성됩니다.
        else
           for idx, serial in pairs(self.SpawnList) do
               _ParticleService:RemoveParticle(serial)
           -- 캐릭터가 목표 엔티티에 닿으면 RemoveParticle() 함수를 통해 기존에 생성된 모든 파티클이 제거됩니다.
           end
           table.clear(self.SpawnList)
    
        end
        end
    end
    

  6. [png](Ect) ParticleServiceTest02를 추가합니다. 그리고 목표 엔티티(ResetEntity)를 지정합니다. [png](16606287624144a1602d085b94be9a8a2a82da2df2c2c)

  7.  시작을 누른 뒤 테스트해 봅시다. 호리병에 캐릭터의 몸이 닿으면 불꽃 파티클이 재생되고 소화기에 닿으면 불꽃 파티클이 사라지는 것을 확인할 수 있습니다. [gif](16606269436796b58e75202444fc39e8d8bb970970fe7)




# 파티클 이벤트

파티클 이벤트를 활용하여 파티클을 반복 생성할 때마다 대미지를 주거나 연출을 보여줄 수 있습니다. 번개가 칠 때마다 공격 기능이 작동하여 캐릭터에게 대미지를 주는 예제를 통해 파티클 이벤트의 활용 방법을 살펴봅시다.

  1.  Preset List - LightningStrikeSharp 파티클을 배치합니다. [png](16606310495644b54ab47cff543aaada7c7264539eadc)

  2. AttackComponent를 Extend하여 새 스크립트 컴포넌트  ExtendAttackComponent를 생성합니다. [png](16606293771586eb9e239b50d424d8d0c5f5d79e31eb5)

  3.  ExtendAttackComponent를 열고 `OnLightning()` 함수를 추가합니다.
         
         @Component
         script LightningAttackHandler extends Component
         method void OnLightning()
         self:Attack(Vector2(2, 2), Vector2.zero, nil)
         -- Attack() 함수를 통해 공격 판정을 합니다.
         end
         end
         



    
    
    4. Event Handler에  ParticleEmitStartEvent를 추가하고 다음과 같이 작성합니다.
        ```lua
    @Component
    script ParticleLightningTrigger extends Component
        @EventSender("Self")
        handler HandleParticleEmitStartEvent(ParticleEmitStartEvent event)
        -- Parameters
        local ParticleEntity = event.ParticleEntity
        --------------------------------------------------------
        self:OnLightning()
        -- 파티클이 시작할 때 OnLightning 함수가 작동하도록 합니다.
        end
    end
    

  5. Event Handler에  ParticleLoopEvent를 추가하고 다음과 같이 작성합니다. 
         
         @Component
         script ParticleLightningTrigger extends Component
         @EventSender("Self")
         handler HandleParticleLoopEvent(ParticleLoopEvent event)
         -- Parameters
         local ParticleEntity = event.ParticleEntity
         --------------------------------------------------------
         self:OnLightning()
         -- 파티클이 시작할 때 OnLightning 함수가 작동하도록 합니다.
         end
         end
         



    
    
    6. 처음에 배치했던 LightningStrikeSharp 파티클의 [png](Ect) ExtendAttackComponent를 추가합니다.
        
    7.  시작을 누른 뒤 테스트해 봅시다. 캐릭터가 번개를 맞으면 대미지를 입는 것을 확인할 수 있습니다.
        [gif](166063078658715693caafe5c498cb4d1afb74ab490ca)
    
    > **더 알아보기**
    > [공격과 피격](/docs?postId=206) 가이드를 참고하여 보다 다양한 연출을 시도해 봅시다.
    
    
    # UI에서 파티클 활용
    
    UI에서 파티클을 재생하고 싶다면 UIBasicParticleComponent, UIAreaParticleComponent, UISpriteParticleComponent를 사용해야 합니다. UIBasicParticleComponent를 활용한 예제를 통해 UI에서의 파티클 활용 방법을 알아봅시다.
    [gif](1738820159173bac2118a2b8e4fb3939212dacdec4c30)
    
    #### UI 만들기
    파티클을 재생할 수 있는 UI를 만들어봅시다. 
    
    1.  **Hierachy - World - ui**에 새로운 **UIGroup**을 추가합니다.
    2. 추가한 UIGroup에서 **UIGroupComponent**의 **DefaultShow** 프로퍼티를 **true**로 설정합니다.
    [png](1738820526946c8b71422566e4a919660cc52294d2f78)
    3. UIGroup에서 **콘텍스트 메뉴 - Create Empty - Create Button**을 눌러 새로운 **UIButton**을 추가합니다.
    4. UIButton에서 **콘텍스트 메뉴 - Create Entity as Child - Create UIEmpty**를 선택합니다. 생성된 엔티티의 이름을 **UIParticle**로 변경합니다.
    [png](1738821689063de9df9de3eb24d1ca3aaab61c30a0d34)
       
    
    #### 파티클 만들기
    1. **UIParticle**에 **UIBasicParticleComponent**를 추가합니다.
    
    2. ParticleType을 **CircleBust**로 선택하고, 원하는 파티클 크기, 속도 등을 조절합니다.
    3. **UIBasicParticleComponent**의 **Loop**를 **false**로 설정해 버튼을 눌렀을 때 파티클이 한 번만 재생되도록 합니다. **PlayOnEnable** 또한 **false**로 변경하여 파티클이 월드 진입하자마자 재생되지 않도록 합니다. 
    [png](1738824957555795b8fa96c3b4e31a73bd9d943735ea5)
    4. **Workspace - Create Script - Create Component**를 선택하고 새로운 **UIButtonParticle**을 생성합니다.
    5. 생성한 **UIButtonParticle**을 **UIButton** 엔티티에 추가합니다.
    [png](17388237882156f14f1b12db546a1a445464f92d57fa1)
    6. UIButtonParticle에 **particleEntity** 프로퍼티를 추가합니다. 프로퍼티의 타입을 **Entity**로 변경하고, **UIParticle** 엔티티를 연결합니다.
       [png](173882498272778923333c4a64ed8ad7b1c6c67ddbb18)
    

@Component script ParticleEntityReference extends Component property Entity particleEntity = "/ui/UIGroup/UIButton/UIParticle" end
    
    
    7. Event Handler에 **ButtonClickEvent**를 추가합니다.
    버튼을 누르면 파티클이 재생될 수 있도록 아래와 같이 작성합니다.
    
    

@Component script ButtonParticlePlayer extends Component @EventSender("Self") handler HandleButtonClickEvent(ButtonClickEvent event) \--------------- Native Event Sender Info ---------------- \-- Sender: ButtonComponent \-- Space: Client \---------------------------------------------------------
    
    
    -- Parameters
    -- local Entity = event.Entity
    ---------------------------------------------------------
    local Entity = event.Entity
    ---------------------------------------------------------
    if self.particleEntity.UIBasicParticleComponent.IsEmitting == true then
        self.particleEntity.UIBasicParticleComponent:Stop()
    end
        self.particleEntity.UIBasicParticleComponent:Play()
    end
    

end
    
    
    8. 엔티티의 크기를 적절하게 조절한 후  **[시작]** 버튼을 누르고, 버튼을 누를 때마다 파티클이 재생되는지 확인합니다.