> For the complete documentation index, see [llms.txt](/ko/llms.txt)


# 캐릭터를 모으고 성장시키기

숲에서 동료를 얻고, 뽑기와 경험치 강화를 추가한 뒤 세 칸의 파티를 구성합니다.



숲 탐험을 클리어하면 기사 한 명을 얻고, 뽑기에서는 기사나 마법사를 얻도록 만듭니다. 같은 기사를 두 명 가지고 있어도 각각 성장시키고 파티에 넣을 수 있게 합니다.

## 1. 캐릭터 종류와 보유 공간 준비하기


**Studio**

**Add features** 에서 **Characters** 를 추가합니다. **Tables → Characters** 에서 다음 세 표를 편집합니다. 일반 셀은 선택 후 Enter를 누르고, 배열 셀은 더블 클릭해서 편집합니다.

| 표 | 행 | 입력할 내용 |
| --- | --- | --- |
| 캐릭터 보유 공간(CharacterCollection) | **Add row** 클릭(ID 입력 불필요) | 초기 보유 공간 `20`, 최대 보유 공간 `100` |
| 캐릭터 경험치(CharacterExperience) | **Add row** 클릭(ID 입력 불필요) | 필요 경험치 표에 `1`, `10`, `20`을 순서대로 추가. 초기 레벨 상한과 최대 레벨 상한은 모두 `4` |
| 캐릭터(Character) | 행을 추가하고 ID를 `knight`로 설정 | 표시 순서 `1` |
| 캐릭터(Character) | 행을 추가하고 ID를 `mage`로 설정 | 표시 순서 `2` |

**Publish → Calculate server differences** 에서 배포한 다음 **Update the Unity project** 를 누릅니다. 보유 캐릭터는 `Character`, 경험치는 `CharacterExperience` 네임스페이스에 저장됩니다.


**SDK (Unity)**

<a href="/samples/adventure/ko/extensions/characters.json" download>캐릭터 설정 템플릿</a>을 다운로드합니다. 관리 콘솔의 Tokyo 리전에서 **Deploy → Stack list → Create stack** 을 엽니다. 이름을 `adventure-characters`로 지정하고 **Template data** 에서 다운로드한 파일을 선택한 뒤 생성합니다.

이 스택에는 보유 공간, 경험치, 뽑기, 파티 편성 설정이 들어 있습니다. 기존 `adventure-game` 스택은 그대로 사용합니다.

| 데이터 | 네임스페이스 | 설정 이름 |
| --- | --- | --- |
| 보유 캐릭터 | `adventure-character` | Inventory `Character`, Item `knight` / `mage` |
| 경험치 | `adventure-character-experience` | Experience `Experience` |
| 캐릭터 뽑기 | `adventure-character-gacha` | Lottery `normal`, Showcase `Showcase`, 상품 `normal` |
| 파티 편성 | `adventure-character-formation` | Mold `CharacterFormation`, 저장 슬롯 `0` |

초기 보유 공간은 20, 최대는 100이며 캐릭터 한 명당 스택 수는 1입니다. 같은 종류의 캐릭터를 여러 번 얻을 수 있도록 `allowMultipleStacks`를 `true`로 설정했습니다.




### 숲 클리어 보상에 기사 추가하기


**Studio**

**Tables → Quests → Quest** 의 `forest`에서 **Completion rewards** 를 엽니다. 코인, 미션 카운터, 회복약 액션은 그대로 두고 **Add item → Not set** 을 누릅니다. `AcquireItemSetByUserId`를 선택하고 다음 값을 입력합니다.

| 항목 | 값 |
| --- | --- |
| namespaceName | `Character` |
| userId | `#{userId}` |
| inventoryName | `Character` |
| itemName | `knight` |
| acquireCount | `1` |

저장한 뒤 서버에 배포합니다.


**SDK (Unity)**

`adventure-game`의 현재 템플릿을 엽니다. `main`의 `forest`에서 `contents` → `completeAcquireActions` 끝에 다음 항목을 추가합니다. 기존 코인, 미션 카운터, 회복약 항목은 그대로 둡니다. 수정한 템플릿 전체로 스택을 업데이트합니다.

```yaml
- action: Gs2Inventory:AcquireItemSetByUserId
  request: |-
    {"namespaceName":"adventure-character","userId":"#{userId}","inventoryName":"Character","itemName":"knight","acquireCount":1}
```




### Unity에 보유 캐릭터 표시하기

Unity의 재생을 멈추고 `Extensions`에서 **Adventure Items** 의 체크를 해제합니다. 왼쪽 모험 화면은 그대로 사용합니다.


**Studio**

<a href="/samples/adventure/ko/extensions/studio/AdventureCharacters.cs" download>AdventureCharacters.cs</a>를 `Assets/AdventureExtensions`에 넣고 `Extensions`에 **Adventure Characters** 를 추가합니다. 로그인된 `Gs2ClientHolder`와 `Gs2GameSessionHolder`를 사용합니다.


**SDK (Unity)**

<a href="/samples/adventure/ko/extensions/sdk/AdventureCharacters.cs" download>AdventureCharacters.cs</a>를 `Assets/AdventureExtensions`에 넣고 `Extensions`에 **Adventure Characters** 를 추가합니다. **Login** 에 씬의 `AdventureLogin`을 할당합니다.




재생한 뒤 왼쪽에서 숲 모험을 새로 시작하고 클리어합니다. 오른쪽 보유 목록에 `knight`와 개체를 구분하는 값이 표시됩니다. 숲을 한 번 더 클리어하면 `knight`가 두 행에 각각 나타납니다. 배포 후 보상 변경이 적용되기까지 최대 5분이 걸릴 수 있습니다. 기사가 늘지 않으면 잠시 기다린 뒤 숲 모험을 새로 시작해 클리어합니다.

처음에는 다음 호출로 목록을 가져오고, 이후에는 `SubscribeItemSets()`로 추가와 삭제를 전달받습니다. `ItemName`은 캐릭터 종류, `ItemSetId`는 개별 캐릭터를 식별하는 값입니다.

```csharp
var inventory = Client.Inventory.Namespace(InventoryNamespace)
    .Me(Session).Inventory("Character");
await foreach (var item in inventory.ItemSetsAsync())
{
    if (item != null) owned.Add(item);
}
```

## 2. 20코인으로 캐릭터 뽑기


**Studio**

**Add features** 에서 **Character Gacha** 를 추가합니다. **Tables → Character Gacha** 에서 다음 순서대로 행을 만듭니다.

| 표 | ID | 입력할 내용 |
| --- | --- | --- |
| 희귀도(GachaRarity) | `common` | ID만 입력 |
| 뽑기(Gacha) | `normal` | 개최 일정은 비워 둠 |
| 희귀도 확률(GachaRarityRate) | 행 추가 | 대상 뽑기 `normal`, 희귀도 `common`, 추첨 가중치 `1` |
| 캐릭터 확률(CharacterRate) | 행 추가 | 희귀도 `common`, 캐릭터 `knight`, 추첨 가중치 `1` |
| 캐릭터 확률(CharacterRate) | 행 하나 더 추가 | 희귀도 `common`, 캐릭터 `mage`, 추첨 가중치 `1` |

확률 표에서는 참조 대상을 선택하면 ID가 자동으로 정해집니다. ID 셀에는 입력하지 않습니다.

`normal`의 **Cost per draw** 를 열고 `WithdrawByUserId` 액션을 하나 추가합니다. `namespaceName = Currency`, `userId = #{userId}`, `slot = 0`, `withdrawCount = 20`, `paidOnly = false`로 설정합니다. 저장한 뒤 서버와 Unity 프로젝트에 반영합니다.


**SDK (Unity)**

처음 배포한 `characters.json`의 `LotteryMaster`와 `ShowcaseMaster`에 이 설정이 들어 있습니다. 두 경품의 가중치는 모두 1이며, 상품은 20코인을 소비하고 획득 액션으로 `Gs2Lottery:DrawByUserId`를 실행합니다.




왼쪽 모험 화면에서 코인을 20개 이상 모은 뒤 오른쪽의 **20코인으로 캐릭터 뽑기** 를 누릅니다. 보유 목록에 기사나 마법사가 한 명 추가됩니다. 왼쪽 코인이 20개 줄었는지도 확인합니다.

구매에는 Showcase를 사용하고, 그 획득 액션에서 Lottery 뽑기를 실행합니다.

```csharp
private async UniTask Draw(string requestId)
{
    var transaction = await Client.Showcase.Namespace(GachaNamespace).Me(Session)
        .Showcase("Showcase").DisplayItem("normal").BuyAsync(
            1, speculativeExecute: false, duplicationAvoider: requestId);
    if (transaction != null) await transaction.WaitAsync(all: true);
}
```

## 3. 선택한 캐릭터에게 경험치 주기

강화에는 10코인 소비와 경험치 10 지급을 묶은 Exchange를 사용합니다.


**Studio**

<a href="/samples/adventure/ko/extensions/character-training-studio.json" download>Studio 캐릭터용 강화 설정</a>을 다운로드합니다. 관리 콘솔에서 **Deploy → Create stack** 을 열고 이름을 `adventure-character-training`으로 지정합니다. **Template data** 에서 파일을 선택해 생성합니다. Studio가 만든 `Currency`와 `CharacterExperience`를 참조하는 설정입니다.


**SDK (Unity)**

<a href="/samples/adventure/ko/extensions/character-training-sdk.json" download>SDK 캐릭터용 강화 설정</a>을 다운로드합니다. **Deploy → Create stack** 에서 이름을 `adventure-character-training`으로 지정하고 **Template data** 에서 파일을 선택해 생성합니다. `adventure-coin`과 `adventure-character-experience`를 참조합니다.




1. 오른쪽 보유 목록에서 강화할 개체를 선택합니다.
2. **선택한 개체의 경험치 읽기** 를 누르고 현재 값을 확인합니다.
3. **10코인으로 경험치 10 추가** 를 누릅니다. 경험치 표시가 자동으로 갱신되며 10 증가합니다.
4. 다른 개체를 선택해 경험치를 읽고, 그 개체의 경험치는 늘지 않았는지 확인합니다.

버튼을 누른 시점에 선택되어 있던 `ItemSetId`를 `Train()`에 전달합니다. 코드는 이 값에 `:level`을 붙여 경험치 저장 대상을 지정합니다. `config`의 값은 템플릿의 `#{propertyId}`에 들어갑니다.

```csharp
private async UniTask Train(string itemSetId, string requestId)
{
    var transaction = await Client.Exchange.Namespace("adventure-character-training").Me(Session).Exchange()
        .ExchangeAsync("train", 1, config: new[] {
            new Gs2.Unity.Gs2Exchange.Model.EzConfig { Key = "propertyId", Value = itemSetId + ":level" }
        }, speculativeExecute: false, duplicationAvoider: requestId);
    if (transaction != null) await transaction.WaitAsync(all: true);
}
```

## 4. 세 칸의 파티 저장하기


**Studio**

**Add features** 에서 **Party Formation** 을 추가합니다. **Tables → Party Formation** 에 다음 값을 입력하고 서버와 Unity 프로젝트에 반영합니다.

| 표 | 행 | 입력할 내용 |
| --- | --- | --- |
| 편성(CharacterFormation) | **Add row** 클릭(ID 입력 불필요) | 초기 저장 슬롯 `1`, 최대 저장 슬롯 `1` |
| 편성 슬롯(CharacterFormationSlot) | `slot1`, `slot2`, `slot3` 행 추가 | 모든 행의 배치 조건 `.*` |


**SDK (Unity)**

`characters.json`의 `FormationMaster`에는 세 칸이 설정되어 있습니다. `CharacterFormation`의 저장 슬롯 `0`을 사용합니다.




1. **편성 불러오기** 를 누른 뒤 목록에서 한 명을 선택하고 **선택한 개체를 슬롯 1에 넣기** 를 누릅니다.
2. 다른 개체를 선택해 슬롯 2에 넣습니다. 세 명이 있다면 슬롯 3에도 넣습니다. 빈 슬롯이 있어도 저장할 수 있습니다.
3. **편성 저장하기** 를 누릅니다.
4. 재생을 멈췄다가 다시 시작하고 같은 플레이어로 로그인한 뒤 **편성 불러오기** 를 누릅니다. 각 슬롯에 이전과 같은 개체 ID가 표시됩니다.

저장할 때는 Inventory의 `GetItemWithSignatureAsync()`로 소유를 증명하는 서명을 받아 Formation의 `SetFormAsync()`에 전달합니다. 코드에는 같은 개체를 두 슬롯에 넣었는지 확인하는 처리도 들어 있습니다.

[다음: 플레이어 연결하기]()



