msw-ai-tf/maplestory-worlds-creator-docs huggingface dataset:
텔레포트

# 학습 과정 소개

특정 시간마다 캐릭터를 다른 맵으로 이동시키는 기능을 만들어 보면서 `TeleportService`의 `TeleportToEntityPath` 함수 사용법을 알아봅시다.

##### 참고 API Reference

[TeleportService](/apiReference/Services/TeleportService)

# 이동할 맵 만들기

먼저 캐릭터가 이동할 맵을 만듭니다.

  1. Window - Map List 창을 엽니다. [png](168775942033420815d67e57a4a5e8089b084e57856fa)   


  2. Map List 창에서 [새 맵 만들기] 버튼을 클릭합니다. 텔레포트로 3개 맵을 순환하도록 만들 것이므로 2개의 새 맵을 추가합니다. [png](163462014875719559f9f4a96453990420dbbb5e5acc2)   


  3. 이동했을 때 쉽게 알아볼 수 있도록 각 맵을 특색 있게 꾸밉니다. 그리고 각 맵으로 텔레포트했을 때, 목적지 역할을 할 엔티티를 하나씩 배치합니다.

[{"맵":"map01","맵 생성 예시":"[png](165665674992362b0791572c143caac4932432ffabccd)"},{"맵":"map02","맵 생성 예시":"[png](16566567844498a1bd211546447a397a044a8bc9c5df1)"},{"맵":"map03","맵 생성 예시":"[png](1656656814159bbb5a8e0966b4ae388dda9382e40ec95)"}]

  

  4. 목적지 엔티티의 경로를 확인합니다. Hierarchy에서 각 엔티티의 콘텍스트 메뉴를 열고 Copy Entity Path를 클릭해 경로를 복사한 뒤 따로 기록합니다. [png](16862103427568f6a56e7203741289157a083b3cd4c20) 이 예제에서 사용한 목적지 엔티티 경로는 다음과 같습니다.

[{"맵":"map01","목적지 엔티티 경로":"\/maps\/map01\/object-19_1"},{"맵":"map02","목적지 엔티티 경로":"\/maps\/map02\/object-6_1"},{"맵":"map03","목적지 엔티티 경로":"\/maps\/map03\/object-42_1"}]




# 텔레포트 기능 만들기

스크립트를 통해 텔레포트 기능을 만듭니다. 새 스크립트 컴포넌트를 만든 뒤 캐릭터에 추가합니다.

  1. Workspace - MyDesk 콘텍스트 메뉴에서 Create Scripts - Create Component를 클릭해 새 컴포넌트를 추가합니다. [png](1687756376627c84b1418872c42a2bb42529622985e1f)   

  2. 컴포넌트 이름을 Teleport로 설정합니다. [png](1635423340528786df9745a5e455c96c59ebc7d949ce9)   

  3. Workspace - DefaultPlayer에 Teleport 컴포넌트를 추가합니다. [png](1635423349810751f20bc31d24e4aa10027ab4d58f36e)   




이제 본격적으로 스크립트를 작성해 봅시다.

  1. Teleport 스크립트를 엽니다. [png](163542336059313a91256a8d64ff6bfb60b3534702e55)   

  2. `OnUpdate` 함수를 추가하고 다음과 같이 작성합니다. 
         
         @Component
         script TeleportCycler extends Component
         @ExecSpace("ServerOnly")
         method void OnUpdate(number delta)
         --프로퍼티 동기화가 필요 없으므로 _T 프로퍼티를 활용
         if self._T.accTime == nil then self._T.accTime = 0 end
         if self._T.teleportCnt == nil then self._T.teleportCnt = 0 end
         
         --테이블 타입의 프로퍼티를 선언, 각 맵에 배치한 목적지 엔티티의 Path 입력
         if self._T.destinationTable == nil then
             self._T.destinationTable = {}
             self._T.destinationTable[1] = "/maps/map01/object-19_1"
             self._T.destinationTable[2] = "/maps/map02/object-6_1"
             self._T.destinationTable[3] = "/maps/map03/object-42_1"
         end
         
         self._T.accTime = self._T.accTime + delta
         
         --3초마다 캐릭터를 다른 맵으로 이동
         if self._T.accTime >= 3 then
             self._T.accTime = 0
             self._T.teleportCnt = self._T.teleportCnt + 1
             local destinationNum = self._T.teleportCnt % 3 + 1
         
         --TeleportService의 TeleportToEntityPath 함수 사용
         --이동시킬 엔티티와 목적지 엔티티의 Path를 매개 변수로 넘겨줌
             _TeleportService:TeleportToEntityPath(self.Entity, self._T.destinationTable[destinationNum])
         end
         end
         end
         



    
    
          
    
    3.  **[시작]** 버튼을 누른 뒤 테스트해 봅시다.
        캐릭터가 3초마다 다른 맵으로 이동하는 것을 확인합니다.
    [gif](16566569336433dc028f996c541839f32ad85efbd8755)
Callback

# 학습 과정 소개

로직은 기본적으로 순차적으로 진행되지만 그렇지 않은 경우도 종종 있습니다. 작업을 순차적으로 진행하는 동기 방식과 그렇게 진행하지 않는 비동기 방식의 차이점을 살펴보고, 비동기 방식을 활용하는데 필요한 Callback에 대해서도 살펴봅시다.

# 동기, 비동기

로직을 처리하는 방식에는 두 가지가 있습니다.

  1. 동기(Synchronous)
  2. 비동기(Asynchronous)



동기 방식(Synchronous)은 로직을 순서대로 처리합니다. 이를 실생활에 대입해봅시다. 톰은 집 대청소를 하는 날입니다. 해야 하는 일은 청소, 설거지 빨래 3가지가 있습니다. 각각 청소 10분, 설거지 30분, 빨래 50분 걸립니다. 동기 방식으로 일하면 톰은 하나의 일을 시작하고, 마친 뒤 다른 일을 시작합니다. 순차적으로 하므로 대청소를 마치는 데 90분이 걸립니다. 동기 방식은 이처럼 이전 작업이 끝날 때까지 대기하고 있다가 그 작업이 끝나고 나면 새로운 작업을 시작합니다. 순차적으로 처리하므로 언제 작업이 끝났을 때를 알 수 있습니다.

[png](165780220427024ff6674499b47f4a10eb5a75b40ffb3)

비동기 방식(Asynchronous)은 모든 작업을 순차적으로 처리하지 않습니다. 어떤 작업은 별도의 작업으로 처리합니다. 다시 톰의 예시로 돌아가 봅시다. 톰이 비동기 방식으로 일하면 빨래를 세탁기로 하고, 그 시간 동안 청소하고 설거지를 할 수 있습니다. 톰은 병행해서 일을 처리했기 때문에 모든 일을 마치는 데 50분이 걸립니다. 로직 중에는 실제 작업을 수행하는 시간보다 다른 외부 요인으로 인해 시간이 오래 걸리는 경우가 있습니다. 외부 데이터를 주고받거나 다른 IO(Input/Output)를 처리할 때 이런 일이 발생합니다. 메이플스토리 월드에서는 DataStorage가 이와 관련된 API입니다. 이런 경우에 비동기 방식으로 처리하면 불필요한 시간을 줄일 수 있습니다.

[png](16578022411436d355c98e08c4347ba61fc07dcd12856)

#### 함수 동기 방식 알아보기

크리에이터가 사용하고자 하는 함수가 동기로 실행되는지, 비동기로 실행되는지는 함수명의 어미로 쉽게 확인할 수 있습니다. 동기 방식은 Wait이 붙어 있고, 비동기 방식은 Async가 붙어 있습니다.

# Callback

Callback을 알아보기 전에 다시 톰에게 돌아가 봅시다. 톰이 대청소를 끝내기 위해선 세 가지 일을 모두 마친 후 빨래 널기까지 해야 합니다. 톰이 동기 방식으로 일한다면 빨래가 끝난 뒤 빨래를 널 것입니다. 비동기 방식으로 일한다고 해도 빨래가 끝나야 빨래를 널 수 있습니다. 그러나 세탁기가 톰 대신 빨래를 하고 있고, 톰은 다른 일을 하고 있으므로 빨래가 언제 끝나는지 톰이 알 수 없습니다. 세탁기가 톰에게 빨래 완료 알림을 울려 빨래가 다 되었다는 것을 알려줘야 합니다. 이처럼 주요 흐름 외에서 처리한 일의 완료 시점을 알 수 있도록 알림을 울리는 것을 Callback이라 합니다. 비동기 작업은 순차적인 흐름대로 진행되는 것이 아니기에 끝났을 때의 타이밍을 알아야 합니다. 이럴 때 사용하는 것이 'Callback'입니다.

Callback은 처리 완료 알림뿐만 아니라 다른 용도로도 사용할 수 있습니다. 비동기 작업이 끝난 뒤에 실행할 연계 작업을 정의하거나, Callback을 특정 타이밍에 호출하는 것도 가능합니다. 예를 들어, TimerService의 SetTimerRepeat은 주기적인 시간대에 '특정 작업'을 하도록 하는 함수입니다. 이때 '특정 작업'을 Callback으로 지정하면 주기적인 타이밍마다 Callback 함수를 실행하도록 할 수 있습니다.

[png](16578022702296367102f25cb49269dc80e1985bfa959)

#### Callback 함수 활용

처리가 완료되었을 때 Callback 함수를 실행하는 예시입니다.
    
    
    @Component
    script PowerInitializer extends Component
        @Sync
        property number Power = 0
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local data = _DataStorageService:GetGlobalDataStorage("data")
    
        self.Power = 20
    
        local callBack = function (errorcode, key)
            log("Power is saved")
        end
    
        data:SetAsync("Power", tostring(self.Power), callBack)
        end
    end
    

> Tip. Component의 Function을 Callback으로 사용할 때는 `self:function()`이 아닌 `self.function()`을 사용해야 합니다.
하의 만들기

# 학습 과정 소개

하의 제작 방법을 알아봅시다. 기본으로 제공되는 청 반바지의 길이를 조정하고 컬러를 바꿔 제작해보고, 자신만의 독특한 치마나 긴 바지를 만들 수 있습니다. [아바타 아이템 제작의 기본 개념](/docs?postId=588)을 참고해 아바타의 기본 개념을 먼저 숙지하길 바랍니다.

# 사전 준비

아이템 제작을 위해선 Adobe Photoshop이 필요합니다. 해당 소프트웨어를 설치한 후 아래 파일을 받아 엽니다. $$attachFile {"name":"Avatar_Pants.psd","url":"https://mod-file.dn.nexoncdn.co.kr/storage/Avatar_psd/version2/Avatar_Pants.psd"} $$

> Tip. 아이템은 아바타 발을 기준으로 최대 120x120 안에서 제작을 권장합니다. [png](1677032255514a026c711634c4196ab0b96bc5424e7f9)

# 제작 시트 설명

제작 시트는 회색 레이어와 노란색 레이어로 구분되어 있습니다. 3개의 회색 레이어는 이미지를 데이터화하기 위한 data 폴더, 이미지 제작에 도움을 줄 아바타 body 레이어, 가이드용 선 레이어가 있습니다. 회색 레이어는 모두 잠겨있고, 작업 중에 수정은 필요하지 않습니다. 노란색 레이어는 크리에이터가 실제 아이템을 그릴 수 있는 곳입니다.

> Tip. 모든 레이어의 이름을 수정하거나 삭제할 경우 아이템을 제작할 수 없습니다. 절대 이름을 변경하지 마세요.

#### 회색 레이어

  1. data 폴더 data 폴더는 아이템을 데이터화하기 위해 필요한 폴더입니다. 이미지 제작에는 영향을 주지 않습니다. 
     * data:origin: 빨간색 점으로 데이터의 중심을 표시합니다.
     * data:type:Pants: 어떤 종류의 데이터인지 알 수 있습니다.
     * data:vslot:Pn: vslot의 개념은 모자에서만 사용됩니다.
     * data:use_zmap_preset1: 아바타를 구성하는 이미지는 층층이 쌓여 만들어집니다. 아바타의 다양한 몸동작을 만들기 위해 몸을 바탕으로 옷, 방패, 무기 순으로 이미지가 층층이 위에 쌓이는 구조입니다.  
아이템에 따라 분리된 각 Sprite의 높이가 순서대로 정리된 파일이 zmap입니다.
  2. guide_character_summary 레이어: 아바타의 샘플 body입니다. 레이어 이름 뒤에 붙은 숫자가 클수록 아래에 배치되는 내부적인 규칙입니다.
  3. guide_grid: 그리드 선 기준으로 아이템을 제작합니다. 초록색 선 안에서만 작업을 해야 합니다. [png](1645498107053da17e0eeb2704d77ba834b73bf65d9c7)



#### 노란색 레이어

노란색 레이어(edithere:pants_pantsBelowShoes_75)는 크리에이터가 제작할 하의를 그릴 수 있습니다. [png](16454981267650d9164635d8d474bad641d8cd0cd5ea0)

# 제작하기

기본 하의를 바탕으로 아래와 같은 짧은 바지와 긴 바지를 제작해봅시다.

[{"index":"기본 하의","0":"[png](16454979999409fd4d08bd2f4489c858250629ef0f2ed)"},{"index":"색상 변경","0":"[png](1645498013081746575d0b440465897991f2dc116df30)"},{"index":"긴 바지","0":"[png](1645498023976f2cc0d293501416f9c65f13ce1c63681)"}]

#### 색상 변경하기

  1. 노란색 레이어(edithere:pants_pantsBelowShoes_75)를 클릭합니다.
  2. image - Adjustments - Hue/Saturation 또는 단축키 Ctrl+U를 입력해 Hue/Saturation 창을 엽니다.
  3. Hue 값을 조정합니다. 하의 색상을 원하는 색으로 바꾸고 'OK' 버튼을 누른 후 파일을 저장합니다. [png](164550404415997459e844dd54f63917a5815a36a1fb2)



#### 다른 종류의 하의 제작

  1. 노란색 레이어(edithere:pants_pantsBelowShoes_75)에 이미지 작업을 합니다.  
[png](164558135888530c409a7de4e47e49ccac81ee21c58d2)
  2. 포토샵에서 Pencil tool을 선택하고 아바타의 body에 맞춰 모든 동작에 도트로 그려줍니다.  
치마류를 그릴 때는 발에 맞춰 그리고, 초록색 안내선을 넘지 않게 주의해야 합니다.  
[png](16455041383060a17d6620fdf422cab54bab983600362)



##### 참고 가이드

  * [신발 만들기](/docs?postId=583)
  * [망토 만들기](/docs?postId=585)
  * [상의 만들기](/docs?postId=586)
  * [장갑 만들기](/docs?postId=587)
TargetUserSync와 프로퍼티 동기화 최적화

# 학습 과정 소개

프로퍼티 동기화 방식 중 하나인 TargetUserSync에 대해 알아봅니다.

##### 참고 가이드

  * [프로퍼티](/docs/?postId=205)
  * [프로퍼티 동기화](/docs/?postId=208)
  * [서버와 클라이언트](/docs/?postId=207)



# TargetUserSync 알아보기

TargetUserSync는 Sync 프로퍼티 동기화를 좀 더 특정해 최적화하는 방식입니다. TargetUserSync는 플레이어가 스스로 조작하는 PlayerEntity에 포함된 컴포넌트의 프로퍼티만을 대상으로 동기화합니다. 서버에서 프로퍼티 값이 변경되었다면 프로퍼티 값이 변경된 플레이어 엔티티를 조작할 수 있는 유저에게만 프로퍼티 값을 동기화해줍니다. TargetUserSync는 다른 유저의 정보를 참조할 필요 없는 값인 경우에 사용하는 것이 좋습니다. 개인 재화, 업적, 소모품 같은 개인의 데이터가 이에 해당합니다. TargetUserSync 프로퍼티를 PlayerEntity가 아닌 다른 Entity의 컴포넌트에서 사용한다면 TargetUserSync는 정상적으로 동작하지 않습니다. 이 경우 None 프로퍼티와 동일하게 동작합니다

> Tip. TargetUserSync는 접속한 Client와 해당 Client가 조작하는 PlayerEntity를 기준으로 작동합니다. 즉 Entity에 적용되는 Component Script에서만 작동하며 아래 Script의 프로퍼티에서는 TargetUserSync를 사용할 수 없습니다.
> 
>   * EventType, ItemType, BTNodeType, StateType, StructType, Logic 
> 


#### TargetUserSync 설정

TargetUserSync는 프로퍼티의 동기화 설정 부분에서 설정할 수 있습니다. 동기화가 가능한 타입은 Sync와 동일합니다.

  * 동기화 가능한 타입: string, integer, number, boolean, SyncTable< v >, SyncTable< k,v >, Vector2, Vector3, Vector4, Color, Entity, Component, EntityRef, ComponentRef



[png](16988033334991153af9548234020a1c66e3872cadf4a)

한 Component에서 Sync와 TargetUserSync를 함께 쓰는 경우도 가능합니다. Sync 프로퍼티와 마찬가지로 OnSyncProperty를 통해 TargetUserSync가 변화되는 시점을 확인할 수 있습니다. [png](16987294568417ecb037ea67e4e35a64f34c4a401f740)

# Sync와 TargetUserSync의 차이점

Sync로 설정한 프로퍼티의 경우 서버에서 프로퍼티 값을 변경하면, 서버에서 클라이언트 방향으로 해당 엔티티를 가진 맵에 있는 모든 유저에게 값을 동기화합니다. 만약 실시간으로 다른 유저의 정보를 확인하고 처리하는 월드를 만든다면 Sync로 설정한 프로퍼티를 사용할 수 있습니다. 그러나 Sync로 설정한 프로퍼티 값의 변화는 맵의 모든 유저에게 동기화되므로 최적화를 고려해 사용해야 합니다.

#### Sync 방식

모든 플레이어 엔티티에 아래와 같은 컴포넌트를 추가했다고 생각해봅시다. meso 프로퍼티는 유저의 재화를 관리하기 위한 프로퍼티입니다. 따라서 다른 유저의 meso 값을 몰라야 합니다.
    
    
    @Component
    script MesoWallet extends Component
        @Sync
        property integer meso = 0
    
        @ExecSpace("ServerOnly")
        method void IncreaseMeso()
        self.meso = self.meso + 1
        end
    end
    

A, B client가 같은 맵에 접속해 있다면 각각 서버와 클라이언트에서 보는 엔티티들의 프로퍼티 값은 아래와 같습니다.

[png](1698802548910ae0fdd13b6e14f4ab5bd34c52b2f5cd4)

이때 PlayerEntity A의 `InscreaseMeso()`가 호출되어 A 엔티티의 변경된 meso 프로퍼티 값이 동기화되면 모든 Client에서 PlayerEntityA 값이 변경됩니다. Sync 프로퍼티는 같은 맵에 있는 모든 Client의 PlayerEntityA에게 제한 없이 변경된 meso 값을 동기화하기 때문입니다. 모든 Client에게 동기화하게 되면, ClientB는 불필요한 프로퍼티 동기화 처리로 인해 연산과 패킷 낭비가 발생합니다. 또한 서버는 불필요한 패킷을 모든 Client로 전송해야하므로 패킷을 낭비하게 됩니다.   
[png](169925298455090466d29434b4a46b24070db138b88d5)

#### TargetUserSync 방식

meso 프로퍼티를 TargetUserSync로 변경한 뒤 서버에서 PlayerEntity A의 `IncreaseMeso()`를 호출하면, PlayerEntity A의 조작 제어권이 있는 Client A에만 meso 값이 동기화됩니다. Client B의 PlayerEntity A에는 변경이 일어나지 않는, None과 동일하게 처리됩니다.
    
    
    @Component
    script MesoManager extends Component
        property integer meso = 0
    
        @ExecSpace("ServerOnly")
        method void IncreaseMeso()
        self.meso = self.meso + 1
        end
    end
    

  
[png](169872111296512b78d9fec8f463f8fd68d25ea813523)
모델 에디터

# 학습 과정 소개

모델의 특성을 활용하면 월드를 보다 효율적으로 제작할 수 있습니다. 모델을 편집할 수 있는 모델 에디터에 대해 알아봅시다.

# 모델 에디터 소개

모델 에디터를 활용해 모델을 더욱 효율적으로 생성하고, 편집할 수 있습니다. 모델 에디터로 진입하면 맵이나 UIGroup에서 편집하던 내용과는 독립된 공간에서 모델만을 자유롭게 편집할 수 있습니다. 모델 에디터를 사용하는 동안 맵 편집과 테스트 플레이는 불가능해집니다.

#### 모델 에디터 진입하기

모델 에디터로 진입하게 되면 Scene 패널이 변경되고, Hierarchy 창은 편집 중인 모델의 계층 구조를 나타내는 용도로 변경됩니다. 모델 에디터로 진입하는 방법은 두 가지입니다.

  1. Workspace에서 진입

     1. Workspace에서 새로운 모델을 만듭니다.
     2. 더블 클릭해 모델 에디터를 엽니다.
  2. Hierarchy에서 진입

     1. Hierarchy에서 엔티티를 생성하고, 콘텍스트 메뉴를 열어 Create Model From Entity을 선택해 엔티티를 모델화합니다..
     2. 생성한 모델을 선택하고 콘텍스트 메뉴 - Edit Model을 선택합니다.



#### 화면 소개

모델 에디터를 활성화하면 Scene 패널이 모델 편집 화면으로 변경되며, 주요 버튼의 기능이 변경됩니다. [png](175211224021236c5261d9da34c7d8f44febbb48c0e14)

[{"번호":"!","이름":"Scene","설명":"제작 중인 모델을 저장하고, Scene으로 돌아갑니다."},{"번호":"!","이름":"NewModel","설명":"편집 중인 모델의 이름입니다."},{"번호":"!","이름":"Discard","설명":"저장하기 전의 모든 편집 내용을 버립니다."},{"번호":"!","이름":"Save","설명":"편집한 모델을 저장합니다."},{"번호":"!","이름":"UI 버튼","설명":"UI 모델을 만들 때 사용할 수 있습니다."},{"번호":"!","이름":"Property 창","설명":"편집 중인 모델에 컴포넌트를 추가\/삭제 하고, 프로퍼티 값을 변경할 수 있습니다. 확장한 모델인 경우 모델의 확장 정보를 확인할 수 있습니다.Model Extension Info: 모델의 확장 구조를 한 눈에 확인할 수 있습니다. 작업 중인 모델 외의 다른 계층의 모델을 편집해야 할 경우 콘텍스트 메뉴 - Find Model을 선택해 진입할 수 있습니다."},{"번호":"!","이름":"Hierarchy 창","설명":"모델의 계층 구조를 확인할 수 있는 창입니다. 새로운 자식 엔티티를 추가하거나, 계층 구조를 변경할 수 있습니다."}]

# 모델 편집하기

모델 에디터에서 편집할 수 있는 모델은 세 가지입니다.

  1. 크리에이터가 직접 생성한 모델
  2. native 모델을 확장한 모델
  3. DefaultPlayer



#### 컴포넌트 추가/삭제

프로퍼티 창에서 모델에 새로운 컴포넌트를 추가하거나, 삭제할 수 있습니다.

#### 자식 엔티티 추가/ 계층 구조 변경 가능

모델 에디터에서도 계층 구조를 가진 모델을 만들 수 있습니다. Hierarchy 창에서 부모-자식 관계를 드래그해 설정할 수 있습니다. [png](17521081010150fea6ccf5f314ccea8df8b2181341c3e)

> 더 알아보기 편집 중인 씬에서도 계층 구조를 변경하고, Create Model From Entity를 활용해 계층 구조를 가진 모델을 생성할 수 있습니다. Hierarchy에서 확인하세요.

#### 편집 시 유의사항

Scene에 배치한 모델은 개별 엔티티로 취급됩니다. 배치된 엔티티는 모델에 종속되어 있지 않으므로 이점을 유의해야 합니다. 그러므로 맵에 배치한 엔티티는, 원모델을 편집해 내용이 변경되었더라도 영향을 받지 않습니다. 만약, 배치된 모델 기반 엔티티에도 수정된 모델을 적용하고 싶은 경우 해당 엔티티를 Revert 해 동일하게 적용할 수 있습니다.

만든 모델을 배치한 후 해당 모델을 수정하는 경우, 배치된 모델 기반 엔티티에는 추가한 새로운 엔티티, 변경된 계층 구조가 반영되지 않습니다. 모델 에디터에서 추가/삭제한 컴포넌트와 프로퍼티 값만 변경됩니다.

> Tip. Scene에 배치한 모델의 컴포넌트를 삭제하거나, 계층 구조를 변경하는 것은 모델 에디터에서만 가능합니다.
모자 만들기

모자는 총 7가지 유형으로 나뉩니다. 모자는 다른 아바타 아이템과 다르게 유형별 전용 제작 시트 파일이 있습니다. 이는 모자 아이템의 vslot과 관련된 특성 때문입니다. 제작에 앞서 해당 개념을 참고해, 유형에 맞는 시트로 아이템을 제작하길 권장합니다.

#### 모자 유형

[{"샘플1":"[png](1645777924042417149b9fb9b45b9b214cdb4854d4fa7)","샘플2":"[png](1645777935190921446c0abd14896b87e14bcbbec739c)","투시1":"[png](16457779460962128864e42dd4c2896f5747f7a3fa5eb)","투시2":"[png](1645777963423dd79456d93ec488aabb30e933b33e20b)","유형 설명":"A 유형: [뒤통수가 개방된 모자](\/docs?postId=684) 모자를 쓰면 헤어가 눌립니다. 뒤통수가 개방된 모자를 착용하면 헤어는 backHairBelowCapWide 레이어로 표시됩니다."},{"샘플1":"[png](164577842985210a6c24b9f9440a7b2b802dc62854236)","샘플2":"[png](1645778444159b093c84415864cc59165952a9c196c13)","투시1":"[png](164577847540108355f2621c0437299f4b1a21152d900)","투시2":"[png](1645778489992f4e3c1d28701405f8cf911fae9ec3c94)","유형 설명":"B 유형: [뒤통수를 덮는 모자](\/docs?postId=677) 모자를 쓰면 헤어가 눌립니다. 뒤통수를 덮는 모자를 착용하면 헤어는 backHairBelowCapNarrow 레이어로 표시됩니다."},{"샘플1":"[png](1645778503508a24d0a12c7324372af2c138f5b49e79b)","샘플2":"[png](1645778518183a9c81f107e3c43169d941ddf1340c91a)","투시1":"[png](16457785324087f05a2b78a684541ba254432e7a7a210)","투시2":"[png](164577854844637f0793dbc7047cdaeaa7239b4810404)","유형 설명":"C 유형: [액세서리](\/docs?postId=678) 외형에 변화가 생기지 않습니다."},{"샘플1":"[png](1645778572537c87fa59d7b144c2c8693e53b753deb64)","샘플2":"[png](1645778591154a2636cfaf4144f999f790c3601125939)","투시1":"[png](164577860337329f91d96a8ea483398f4d43630e1937f)","투시2":"[png](16457802535079c282c68556c4bc390311e3269c5e2e9)","유형 설명":"D 유형: [가발](\/docs?postId=679)모든 헤어 레이어를 제거합니다. 얼굴 장식, 눈 장식, 귀고리는 제거하지 않습니다."},{"샘플1":"[png](1645780283283f1500aa5d4654b03a0c33e03d8d8c1b8)","샘플2":"[png](16457803021515658ac24831b4e1dba5a368b6623da01)","투시1":"[png](1645780317331c3e64710314746aca10ab76ba4a62fd3)","투시2":"[png](1645780335549094ab565ccd449dd8c03f482fae70ac8)","유형 설명":"E 유형: [얼굴을 가리는 탈](\/docs?postId=680)모든 헤어 레이어를 제거합니다. 눈 장식, 귀고리도 제거합니다."},{"샘플1":"[png](1645780399240593fc2b18daa4ed8b80422b275e90fe8)","샘플2":"[png](1645780363419401531cf33b34963aea00d6694cdf75b)","투시1":"[png](164577860337329f91d96a8ea483398f4d43630e1937f)","투시2":"[png](16457802535079c282c68556c4bc390311e3269c5e2e9)","유형 설명":"F 유형: [얼굴이 보이는 탈](\/docs?postId=681)모든 헤어 레이어를 제거합니다. 눈 장식, 얼굴 장식은 살리고 귀고리만 제거합니다."},{"샘플1":"[png](1645780352198ac4e37d7ca1649c0a274f95da70d1ebb)","샘플2":"[png](1645780516061d381bb75f818472ea515849ce188f5dc)","투시1":"[png](1645780531627c60d3b9865de4ed99d63e3408c2108c9)","투시2":"[png](16457802535079c282c68556c4bc390311e3269c5e2e9)","유형 설명":"G 유형: [후드](\/docs?postId=682)앞모습 눌린 헤어만 살리고 나머지 헤어는 제거합니다. 눈 장식, 얼굴 장식은 살리고 귀고리만 제거합니다."}]
물리 사용하기

# 학습 과정 소개

메이플스토리 월드의 맵과 엔티티에 물리 법칙 적용 방법을 알아봅시다. 물리 법칙을 활용해 엔티티가 서로 충돌해 튕기거나, 포물선 운동을 하며 날아가거나, 엔티티가 서로 연결되어 움직이게 하는 등 물리를 활용한 움직임을 다양하게 만들어낼 수 있습니다. [엔티티의 충돌](/docs?postId=175)과 [충돌 그룹 만들기](/docs?postId=754)를 함께 학습하길 권장합니다.

# 맵에 물리 법칙 적용하기

기본 맵은 메이플스토리 맵 구성과 이동 원칙을 따르므로 물리 법칙은 기본 적용되어 있지 않습니다. 그러므로 물리 법칙을 적용하고 싶은 맵에 별도의 설정이 필요합니다. 물리 기능을 맵에서 사용하고 싶다면 반드시 해당 맵에 [png](move) PhysicsSimulatorComponent를 추가해야 합니다. [png](move) PhysicsSimulatorComponent의 Gravity 프로퍼티를 활용해 맵의 중력을 설정할 수 있습니다. Gravity의 x, y 값에 따라 맵의 중력 방향과 힘이 달라집니다. 만약 물리 법칙이 적용되는 모든 엔티티가 중력을 왼쪽 방향으로 작용하는 맵을 만들고 싶다면 x 값을 음수로 지정하고, 중력이 위로 작용하는 맵을 만들고 싶다면 y 값을 양수로 지정합니다. x, y 값을 0으로 설정해 무중력 맵을 만들 수도 있습니다.

  1.  Hierachy -  World -  maps -  map01을 선택합니다.
  2. PhysicsSimulatorComponent를 추가하고, Gravity 값을 입력합니다.  
[png](16602791075369a9cbb9197f24d2bacf24d88f506eba3)



[{"index":"(-5,-10)","0":"[gif](1659508219042208f13fd27134e3992c526d914be0b51)"},{"index":"(0,4)","0":"[gif](16595082076157020bb152a804a76b314d6c9eedc6012)"}]

#### 지면 만들기

기본 월드의 설정은 물리 엔티티들과 상호 작용이 일어나지 않기 때문에 기본 발판도 마찬가지로 물리 엔티티가 충돌하지 않고 뚫고 지나가게 됩니다. 물리 엔티티가 타일에 충돌하게 하려면 [png](map) TileMapComponent, [png](Ect) CustomeFootholdComponent의 PhysicsInteractable를 활성화 합니다. [png](map) TileMapComponent의 PhysicsInteractable을 활성화 해 물리 엔티티들이 충돌할 수 있는 지면과 벽 같은 역할을 할 수 있게 만들어봅시다.

  1. [png](map) TileMap을 선택합니다.
  2. [png](map) TileMapComponent의 PhysicsInteractable 프로퍼티를 [png](checkbox)활성화합니다. [png](1659508535605c9a75f4c314346f7a7917c8414ad7f19)



[{"index":"PhysicsInteractable png","0":"[gif](16595852217680f7f0463ddd342b1bae1498907fbe9c8)"},{"index":"PhysicsInteractable png.1","0":"[gif](16595852355591216ebd84358467e8a22501149d7856a)"}]

# 엔티티에 물리 법칙 적용하기

엔티티에 PhysicsRigidbodyComponent를 엔티티에 추가해 엔티티의 물리 타입, 마찰력 등을 조정할 수 있습니다. 이때 PhysicsColliderComponent가 함께 추가되며 충돌체의 모양과 크기를 설정할 수 있습니다. 두 컴포넌트를 추가한 엔티티는 충돌할 수 있고, 관련 Event를 발생시킬 수 있습니다. 만약 PhysicsRigidbodyComponent를 추가하지 않는다면 질량이 무한히 큰 Static 타입 엔티티가 됩니다. 물리 엔티티는 세가지 타입으로 나뉘며 아래와 같은 특성이 있습니다.

  * Static: 질량이 무한히 큰 엔티티로 취급되며 Dynamic 타입과 충돌할 수 있습니다. [gif](1659576952722cf8c504f8d504c7e9086e3bec94a09c9)
  * Dynamic: 엔티티가 물리 법칙에 따라 동작합니다. 물리 충돌이 가능한 다른 Entity와 충돌할 수 있습니다. [gif](1659576966548ee80da29541e440eac920f8b56b56064)
  * Kinematic: 질량이 무한히 큰 엔티티로 취급되나 Static 타입과 다르게 속도를 지정해 움직일 수 있습니다 [gif](165959362285423ac24e7dcdb4ca6b543886eee581004)



##### 참고 가이드

  * [엔티티에 물리 적용하기](/docs?postId=761)
  * [다양한 물리 Joint 활용하기](/docs?postId=760)
  * [엔티티의 충돌](/docs/?postId=175)
LocaleDataSet

# 학습 과정 소개

월드의 텍스트 번역 시 필수적으로 사용해야 하는 LocaleDataSet에 대해 알아봅시다.

##### 참고 가이드

[로컬라이징의 이해](docs/?postId=951) [포맷에 맞게 로컬라이징하기](docs/?postId=968) [자동 번역](docs/?postId=1072)

# LocaleDataSet 소개

LocaleDataSet은 번역을 위한 전용 DataSet입니다. LocaleDataSet은 Default, WebSync 두 종류가 있습니다. LocaleDataSet은 Workspace - MyDesk - Create LocaleDataSet에서 Default, WebSync중 하나를 선택해 생성할 수 있습니다. [png](16983152847384b0c9b653fc74b3ea842f39a739ac243)

#### Default

기본 LocaleDataSet입니다. Key, Source, Note 컬럼이 기본으로 추가되어 있으며 크리에이터가 컬럼을 추가해 번역문을 입력하고 관리할 수 있습니다. Default 종류의 LocaleDataSet은 공식 홈페이지의 번역 관리와 연동되지 않습니다. 열 추가 버튼을 눌러 번역할 수 있는 언어 목록을 선택해 언어 코드를 추가할 수 있습니다. 언어 코드는 중복으로 추가할 수 없습니다. Default LocaleDataSet는 번역의 양이 많지 않아 메이커에서 직접 번역 관리가 가능한 경우 사용하기 적합합니다.

[png](16988269998795f9915d9c6c5478d8c93413d8b9bf3e7)

#### WebSync

WebSync LocaleDataSet은 공식 홈페이지의 번역 관리에서 번역한 내용을 진행하고, 메이커의 LocaleDataSet 에디터에서 연동해 확인할 수 있는 LocaleDataSet입니다. 마지막으로 편집한 시점을 저장해 공식 홈페이지의 번역 관리와 연동합니다. WebSync LocaleDataSet은 Key, Source, Note 컬럼만 편집할 수 있으며, 메이커에서 새로운 컬럼을 추가할 수 없습니다. 번역을 동기화하면 수정 불가능한 컬럼이 자동으로 추가됩니다. WebSync LocaleDataSet는 월드를 제작하는 멤버 수가 많아 메이커에 직접적으로 접근하는 사람 수 관리가 필요하거나, 번역 양이 많을 때 사용하기 적합힙니다.

[png](169837602461617090b6ba590446daa40528e6ab4150c)

# LocaleDataSet 에디터

!

[{"번호":"\u0001","설명":"컬럼 셀각 열의 데이터명, 즉 컬럼 명을 정의합니다. 기본 고정 컬럼 셀은 삭제가 불가능합니다Key: 식별자 키입니다. 중복된 Key를 사용할 수 없습니다.Source: 원본 언어, 원본 내용Note: 번역자에게 제공할 추가 정보를 입력합니다. 해당 정보는 공식 홈페이지 번역 관리에서 확인할 수 있습니다."},{"번호":"\u0001","설명":"셀각 셀은 인풋 텍스트 필드로 각 열의 정의에 적합한 데이터 값을 입력합니다. 입력한 데이터 값은 모두 스트링으로 저장됩니다."},{"번호":"\u0001","설명":"행 추가 버튼버튼을 누르면 행 끝에 새로운 행을 추가합니다."},{"번호":"\u0001","설명":"열 추가 버튼버튼을 누르면 열 끝에 새로운 행을 추가합니다. LocaleDataSet이 Default인 경우 번역할 언어를 선택해 언어 코드를 추가할 수 있고, WebSync인 경우 비활성화 됩니다."},{"번호":"[png](1656660216362c29ce81d29e6400caa25bc648b91058a)CSV 파일 불러오기컴퓨터에 저장된 CSV 파일을 가져와 현재 데이터에 덮어씌울 수 있습니다. utf-8로 저장된 CSV 파일만 정상적으로 가져올 수 있습니다.불러오기 기능을 사용할 때는 LocaleDataSet 형식에 맞춰 Key, Source, Note 순으로 머릿글 행이 채워져 있어야 합니다. 그 외 다른 데이터는 모두 삭제된 채로 데이터를 가져옵니다.CSV 파일 내보내기메이커의 LocaleDataSet을 CSV 파일로 내보내어 컴퓨터에 저장할 수 있습니다.","설명":"nan"}]

# 번역 관리

메이커에서 생성한 WebSync 타입의 LocaleDataSet은 공식 홈페이지에서 번역할 수 있습니다. 또한 번역 관리에서 번역한 내용을 메이커의 LocaleDataSet 에디터에 연동할 수 있습니다. 번역 관리는 공식 홈페이지 - 월드 만들기 - 크리에이터의 제작 월드 - 번역 관리 - 월드 콘텐츠에 있습니다. 메이플스토리 월드는 현재 한국어, 영어 번역만 지원하고 있습니다.

[png](1698372454364d4e1b9829c23441eac692acab07bfcc1)

[{"번호":"\u0001","설명":"월드 콘텐츠 메이커에서 생성한 LocaleDataSet의 목록입니다."},{"번호":"\u0001","설명":"Sync UP버튼을 눌러, 메이커에서 생성한 LocaleDataSet의 정보를 최신화할 수 있습니다."},{"번호":"\u0001","설명":"검색창 Key, Source, Note를 기준으로 번역문을 검색할 수 있습니다."},{"번호":"\u0001","설명":"번역문 Key, Source, Note 정보를 함께 확인하고, 번역할 수 있습니다. [저장] 버튼을 눌러 번역을 저장할 수 있습니다. 메이커에 번역을 연동 시킬 때는 메이커에서 [Sync] 버튼을 눌러야합니다."}]
액세서리

# 학습 과정 소개

액세서리 제작 시트를 소개합니다. [아바타 아이템 제작의 기본 개념](/docs?postId=588%7B%22target%22:%22_self%22%7D)을 참고해 아바타의 기본 개념을 먼저 숙지하길 바랍니다.

# 사전 준비

아이템 제작을 위해선 Adobe Photoshop이 필요합니다. 해당 소프트웨어를 설치한 후 아래 파일을 받아 엽니다. $$attachFile {"name":"Avatar_Cap_C1.psd","url":"https://mod-file.dn.nexoncdn.co.kr/storage/Avatar_psd/version4_230221/Avatar_Cap_C1.psd"} $$ $$attachFile {"name":"Avatar_Cap_C2.psd","url":"https://mod-file.dn.nexoncdn.co.kr/storage/Avatar_psd/version4_230221/Avatar_Cap_C2.psd"} $$

> Tip. 모자는 아바타 발을 기준으로 최대 120x120 안에서 제작을 권장합니다. [png](16769485559327fc72cf03aca4128998b423540c3e049)

# 제작 시트 설명

모자 제작 시트는 회색, 빨간색, 노란색 레이어로 구분되어 있습니다. 회색 레이어는 이미지를 데이터화하기 위한 data 폴더, 이미지 제작에 도움을 줄 아바타 body 레이어, 가이드용 선 레이어가 있습니다. 회색 레이어는 모두 잠겨있고, 작업 중에 수정은 필요하지 않습니다. 노란색 레이어는 크리에이터가 실제 아이템을 그릴 수 있는 곳입니다. 빨간색 레이어는 모자와 헤어의 우선순위를 결정하는 역할을 합니다.

> Tip. 모든 레이어의 이름을 수정하거나 삭제할 경우 아이템을 제작할 수 없습니다. 절대 이름을 변경하지 마세요.

### 회색 레이어/빨간색 레이어

  1. data 폴더: data 폴더는 아이템을 데이터화하기 위해 필요한 폴더입니다. 이미지 제작에는 영향을 주지 않습니다. 
     * data:origin: 빨간색 점으로 데이터의 중심을 표시합니다.
     * data:type:Cap: 어떤 종류의 데이터인지 알 수 있습니다.
     * data:use_zmap_preset1: 아바타를 구성하는 이미지는 층층이 쌓여 만들어집니다. 아바타의 다양한 몸동작을 만들기 위해 몸을 바탕으로 옷, 방패, 무기 순으로 이미지가 층층이 위에 쌓이는 구조입니다.  
아이템에 따라 분리된 각 Sprite의 높이가 순서대로 정리된 파일이 zmap입니다.
  2. guide_ 레이어: 아바타의 샘플 body입니다. 레이어 이름 뒤에 붙은 숫자가 클수록 아래에 배치되는 내부적인 규칙입니다.  

     * guide_backHairBelowCapWide_samples_94, guide_backHairBelowCapNarrow_samples_95는 모자 모양에 따른 넓은 헤어와 좁은 헤어의 샘플입니다.  
[png](16457811937535becc03a37664135889302106c115bff)
  3. (빨간색 레이어) data:vslot:Cp: 빨간색 레이어는 모자에만 있는 개념입니다. 모자 제작 파일에서의 vslot 레이어는 머리카락과 모자가 겹칠 경우 보이는 우선순위를 결정하는 역할을 합니다. [png](1677032925906d8d5e7d2d4fc4919abdd056f95e86764)



### 노란색 레이어

노란색 레이어에 크리에이터가 제작할 모자를 그릴 수 있습니다.

#### 헤어 앞 액세서리(Avatar_Cap_C1)

  1. case1.front: 액세서리 앞모습을 그릴 레이어와 앞모습의 일반 헤어와 눌린 헤어 샘플이 포함되어 있습니다. 
     * edithere:cap_cap_34: 액세서리의 앞모습을 그릴 수 있는 레이어입니다.
  2. case2.back: 액세서리의 뒷모습을 그릴 레이어와 뒷모습의 일반 헤어와 눌린 헤어 샘플이 포함되어 있습니다. 
     * edithere:cap_backCap_92: 액세서리의 뒷모습을 그릴 수 있는 레이어입니다. [png](1677033049022e937abd5539d482db48c27edb17f2f5d)



#### 헤어 뒤 액세서리 (Avatar_Cap_C2)

  1. case1.front: 액세서리 앞모습을 그릴 레이어와 앞모습의 일반 헤어와 눌린 헤어 샘플이 포함되어 있습니다. 
     * edithere:cap_cap_34: 액세서리의 앞모습을 그릴 수 있는 레이어입니다.
  2. case2.back: 액세서리의 뒷모습을 그릴 레이어와 뒷모습의 일반 헤어와 눌린 헤어 샘플이 포함되어 있습니다. 
     * edithere:cap_backCapAccessory_118: 액세서리의 뒷모습을 그릴 수 있는 레이어입니다 [png](167703307307804e247f40f174b1291de064b6b72eacb)