에디터 서비스의 활용

# 학습 과정 소개

월드를 제작하고 테스트 플레이를 시작하면 라이프 사이클에 따라 이벤트 함수를 호출합니다. 이와 다르게 [EditorService](/apiReference/Services/EditorService)는 메이커에서 스크립트 사용을 지원합니다. 크리에이터는 EditorService를 이용해 자신이 필요한 제작 도구를 만들 수 있습니다.

# 에디터 서비스 체험하기

EditorService를 활용해 로그를 남겨봅시다.

  1. MyDesk- Create Scripts - Create Component를 선택하여 스크립트 컴포넌트를 추가하고 이름을 HelloEditor로 설정합니다.

  2. Event Handler에 EnterEditorEvent, EnterPlayEvent를 추가하고 아래와 같이 작성합니다.
         
         @Component
         script EditorModeMonitor extends Component
         @EventSender("Service", "EditorService")
         handler HandleEnterEditorEvent(EnterEditorEvent event)
         --------------- Native Event Sender Info ----------------
         -- Sender: EditorService
         -- Space: Editor
         ---------------------------------------------------------
         
         -- Parameters
         ---------------------------------------------------------
         log("Enter Editor Event")
         end
         
         @EventSender("Service", "EditorService")
         handler HandleEnterPlayEvent(EnterPlayEvent event)
         --------------- Native Event Sender Info ----------------
         -- Sender: EditorService
         -- Space: Editor
         ---------------------------------------------------------
         
         -- Parameters
         ---------------------------------------------------------
         log("Enter Play Event")
         end
         end
         



    
    
    3. **Hierarchy - World - Common**에 HelloEditor 컴포넌트를 추가합니다.
    [png](16456799439142a316dfd3f4d423faf840a94b8681bc1)
    
    4. 시작[png](163453086660754178e0ff96a45c58d1a580a4dfab9d1)을 눌러 **EnterPlayEvent**를 호출하며 해당 로그가 출력되는지 확인합니다.
    [png](1687841635285b77ef2ece1e04d24808a25fdba02c8c4)
    
    5. 플레이를 종료하고 다시 UI 에디터로 돌아옵니다. 에디터로 돌아왔으므로 **EnterEditorEvent**가 호출됩니다. **EnterEditorEvent**는 아웃 게임에서 최초 에디터 진입 과정에서도 호출됩니다.
    [png](168784164997278c571c6f54c49a282600c1412e2790d)
    
    
    # 에디터 타입의 UI 사용
    
    UIGroupComponent의 **GroupType**을 변경해 에디터 타입의 UI를 구성할 수 있습니다.
    에디터 타입 UI는 월드를 플레이하거나, 월드를 편집 중인 상태에서만 확인 및 기능 작동이 가능합니다. 간단한 예시를 통해 확인해보겠습니다.
    [png](16560458866694a2f964d7cbb44a1b4e966596d010277)
    
    
    1. UI 에디터를 엽니다.
        [png](16611491146220ecdd23bd75c4e0b893476b109727246)
          
    
    2. 새로운 UIGroup을 추가합니다.
        [png](16560459550233fba6f0be20f40b8813dd49b8b984959)
          
    
    3. 추가한 UIGroup을 선택합니다.
        [png](16560460059060c0a533e60744a3a94c3be9280876c60)
          
    
    4. 프로퍼티 에디터에서 UIGroupComponent의 GroupType을 **EditorType**으로 변경합니다.
        [png](1645680750596d7f27d2366374420b79ae4dfd6ef6d7e)
          
    
    5. 새로운 버튼을 추가해봅시다. 좌측 상단의 메뉴에서 버튼을 클릭해 추가하고 버튼의 이름을 **"Btn_EditorTest"**로 변경합니다.
        [png](1656046223542857a7fa8661b424a8fb5bbd89e60eb72)
    
    6. 씬 화면의 우측 상단에 있는 **[Editor UI]** 버튼을 클릭합니다. 플레이 상태가 아님에도 추가한 버튼이 보입니다.
        [png](1656046080229263374149c6541d9b49665418b2943ec)
        버튼은 클릭 가능한 상태이므로 클릭 이벤트를 받아 처리하면 버튼을 의도대로 작동시킬 수 있습니다.
    
    7. 앞서 추가한 **HelloEditor** 컴포넌트를 엽니다.
    8. Event Handler에서 **ButtonClickEditorEvent**를 추가합니다.
    9. 해당 이벤트의 대상을 앞서 추가한 **Btn_EditorTest** 엔티티로 설정합니다.
    10. 아래와 같은 형태로 로그 추가 후 스크립트 에디터를 닫습니다.
        ```lua
    @Component
    script ButtonClickHandler extends Component
        @EventSender("Self")
        handler HandleButtonClickEditorEvent(ButtonClickEditorEvent event)
        --------------- Native Event Sender Info ----------------
        -- Sender: ButtonComponent
        -- Space: Editor
        ---------------------------------------------------------
    
        -- Parameters
        local Entity = event.Entity
        ---------------------------------------------------------
        log("Button Click")
        end
    end
    

  11. 배치한 버튼을 클릭해봅시다. 추가한 로그가 정상적으로 출력됨을 확인할 수 있습니다. [png](168784179772119cec289725b4252b43efb1499f22b93)



# 에디터 UI를 이용한 프로퍼티와 엔티티의 배치

스크립트를 이용해 에디터 상태에서 다른 컴포넌트에 접근해 프로퍼티를 수정할 수 있습니다. 이때 월드 플레이 때와는 다른 방식으로 프로퍼티 값이 저장됩니다. 월드 플레이 시에는 플레이 종료 후 메이커로 돌아오며 프로퍼티가 월드 제작 때 설정한 본래 값으로 돌아가지만, 에디터 UI의 경우 플레이를 종료하고 메이커로 돌아오면 변경된 값을 저장합니다. 메이플스토리 월드에서 제공하는 대부분의 API는 에디터 환경에서도 사용할 수 있습니다.

#### 에디터에서 월드 배경 변경

다음은 에디터 상태에서 버튼을 클릭할 때마다 월드의 배경을 변경하는 예시입니다. 월드 배경을 변경한 상태에서 저장하면 월드는 변경된 배경을 유지합니다.
    
    
    @Component
    script ToggleBackgroundChanger extends Component
        @EventSender("Self")
        handler HandleButtonClickEvent(ButtonClickEvent event)
        self.IsButtonClicked = not self.IsButtonClicked
    
        local backGroundRuid = "000000"
        local backGroundComp = _EntityService:GetEntityByPath("/maps/map01/Background").BackgroundComponent
    
        if self.IsButtonClicked then
            backGroundRuid = "000001"
        end
    
        backGroundComp:ChangeBackgroundByTemplateRUID(backGroundRuid)
        end
    end
    

#### 에디터에서 엔티티 스폰

버튼 클릭 시 에디터 상에 엔티티를 스폰하여 배치할 수도 있습니다. 에디터 상태에서 버튼을 클릭할 때마다 오브젝트를 스폰하는 예시입니다.
    
    
    @Component
    script ClickModelSpawner extends Component
        @EventSender("Self")
        handler HandleButtonClickEvent(ButtonClickEvent event)
        --------------- Native Event Sender Info ----------------
        -- Sender: ButtonComponent
        -- Space: Client
        ---------------------------------------------------------
    
        -- Parameters
        -- local Entity = event.Entity
        ---------------------------------------------------------
    
        local spawnModelId = "000000"
        local spawnPosition = Vector2(0,0)
    
        _EditorService:SetSelectedModel(spawnModelId)
        _EditorService:CreateSelectedModel(spawnPosition)
        end
    end
    

# 에디터 서비스의 이벤트

에디터 서비스는 실제로 월드를 플레이하는 상태가 아니므로 `OnBeginPlay()`나 `OnUpdate()`처럼 라이프 사이클을 사용하지 않습니다. 에디터에서 발생하는 이벤트를 중심으로 스크립트를 제작합니다. 사용 가능한 에디터 서비스의 함수와 이벤트는 API 레퍼런스 사이트에서 확인할 수 있습니다. 대표적인 에디터 서비스의 이벤트로는 WorldLoadEditorEvent, EnterEditorEvent가 있습니다.

  * WorldLoadEditorEvent: 에디터 실행 후 월드를 로드하는 과정에서 1회 실행됩니다. 초기화를 시작하기 위한 좋은 이벤트입니다.
  * EnterEditorEvent: 에디터 실행 후 월드를 최초 로드할 때와 테스트 플레이 종료 시 호출됩니다. 반복적으로 호출됨을 유의해야 합니다.



자주 사용하는 유형의 이벤트는 다음과 같습니다.

[{"유형":"에디터 사용 간 특정 지점에 호출되는 이벤트","이벤트":"EnterEditorEventWorldLoadEditorEventEnterPlayEvent"},{"유형":"엔티티 선택 관련 이벤트","이벤트":"EntitySelectEditorEventEntityDeselectEditorEvent"},{"유형":"엔티티의 생성과 삭제 관련 이벤트","이벤트":"EntityCreateEditorEventEntityDeleteEditorEvent"},{"유형":"마우스로 클릭한 위치 정보를 확인하기 위한 이벤트","이벤트":"ScreenTouchEditorEventScreenTouchHoldEditorEventScreenTouchReleaseEditorEvent"},{"유형":"버튼 상태를 감지하기 위한 이벤트","이벤트":"ButtonClickEditorEventButtonStateChangeEditorEvent"},{"유형":"텍스트 인풋 필드의 상태를 감지하기 위한 이벤트","이벤트":"TextInputValueChangeEditorEventTextInputEndEditEditorEvent"}]

# 편집한 정보 저장

SpawnService를 이용해 엔티티 배치하거나 프로퍼티 값을 수정해 변경된 값을 저장할 수 있습니다. 프로퍼티의 값을 수정 및 제어할 수 있다는 의미는 많은 정보를 수정할 수 있다는 의미를 내포하나, 복잡한 정보를 저장하기에는 충분하지 않을 수 있습니다.

EditorService는 데이터에 접근하여 값을 읽고 쓸 수 있는 기능을 제공합니다.아래와 같이 데이터를 구성했다고 가정해보겠습니다.

[png](16456812592398638debc633f4b4383d1001170926f89)

dash의 col3 컬럼의 값을 가져오기 위해 아래와 같이 작성해 활용할 수 있습니다.
    
    
    @Component
    script ConfigValueFinder extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
            local data = _DataService:GetTable("CustomEditorData")
            local rowCount = data:GetRowCount()
            
            for i=1, rowCount do
                local name = data:GetCell(i, "name")
                if name == "dash"then
                    local value = data:GetCell(i, "col3")
                    log("find value : "..value)
                    break
                end
            end
        end
    end
    

데이터 서비스의 사용 방법과 기본적으로 동일하게 사용할 수 있으나 데이터를 Set 하는 방법이 다릅니다. 데이터를 Set하기 위해서는 EditorService의 API를 이용해야 합니다. 에디터 서비스에서는 이를 위해 `DataSetInsertRow()`, `DataSetRemoveRow()`, `DataSetSetCell()` 함수를 지원합니다.

아래는 4번째 줄에 데이터를 추가하고 관련 데이터를 세팅하는 예제입니다.
    
    
    @Component
    script EditorDataEntryCreator extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
            local data = _DataService:GetTable("CustomEditorData")
            local rowCount = data:GetRowCount()
            
            _EditorService:DataSetInsertRow("CustomEditorData")
            
            _EditorService:DataSetSetCell("CustomEditorData", rowCount + 1, "name", "skill1")
            _EditorService:DataSetSetCell("CustomEditorData", rowCount + 1, "col1", "10")
            _EditorService:DataSetSetCell("CustomEditorData", rowCount + 1, "col2", "20")
            _EditorService:DataSetSetCell("CustomEditorData", rowCount + 1, "col3", "30")
        end
    end
    

만약 dash의 col2 컬럼 값을 40으로 변경하고 싶다면 다음과 같이 작성합니다.
    
    
    @Component
    script DashConfigUpdater extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
            local data = _DataService:GetTable("CustomEditorData")
            local rowCount = data:GetRowCount()
            
            for i=1, rowCount do
                local name = data:GetCell(i, "name")
                if name == "dash"then
                    local value = data:GetCell(i, "col2")
                    _EditorService:DataSetSetCell("CustomEditorData", i, "col2", "40")
                    break
                end
            end
        end
    end