# Unity SDK

## 요구 사양 <a href="#undefined" id="undefined"></a>

Game Chat Unity SDK를 사용하기 위한 요구 사양은 다음과 같습니다.

* 최소 사양: 2018.4.0 이상\
  (하위 버전의 Unity 지원이 필요할 경우 '<cs@nbase.io>' 이메일로 문의해 주십시오. )
* 2019.4.X / 2020.3.X / 2021.1.X 버전의 Unity 에디터 사용자는 2019.4.29f1 이상 / 2020.3.15f2 이상 / 2021.1.16f1 이상 버전을 사용해 주십시오(AAB 버전 빌드 시 Unity 에디터 버그 수정 버전).

## SDK 설치 및 환경 구성 <a href="#sdk" id="sdk"></a>

Game Chat Unity SDK를 다운로드하고 Unity에서 프로젝트를 구성하는 방법은 다음과 같습니다.

1. **Game Chat 대시보드 > 설정** > **SDK 다운로드** 메뉴를 차례대로 클릭한 후 **Unity SDK 다운로드**를 클릭해 주십시오.
2. Unity 프로그램을 실행한 후 프로젝트를 생성해 주십시오.
3. Unity에서 **Assets** > **Import Package** > **Custom Package...** 메뉴를 차례대로 클릭해 주십시오.
4. 대시보드에서 다운로드한 'GameChatUnitySDK\_xxxxxxxx' 파일을 불러와 주십시오.
5. 패키지에 있는 모든 파일을 선택한 후 **\[Import]** 버튼을 클릭해 주십시오.
6. 프로젝트를 저장해 주십시오.

## 인증

### Game Chat 인스턴스 초기화 <a href="#game-chat" id="game-chat"></a>

Game Chat 프로젝트 아이디를 활용하여 GameChat 인스턴스를 초기화하려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.initialize(PROJECT_ID);

// 싱가폴 리전 사용 시
GameChat.setRegion("sg");
GameChat.initialize(PROJECT_ID);
```

| ID          | type   | desc     |
| ----------- | ------ | -------- |
| PROJECT\_ID | string | 프로젝트 아이디 |

### Game Chat 소켓 서버 연결 <a href="#gamechat" id="gamechat"></a>

Game Chat 소켓 서버에 연결하는 방법은 다음과 같습니다.

1. 채팅 사용자 아이디를 사용하여 Game Chat 소켓 서버에 접속해 주십시오.
   * Game Chat 프로젝트에서 채팅 사용자 아이디는 고유한 값입니다.
2. API를 사용하기 위한 토큰값을 획득해 주십시오.
   * GameChat.connect 이후에 갱신된 토큰값을 확인할 수 있습니다.
3. 토큰값을 획득한 후 현재 접속 디바이스에 대한 채팅 사용자 정보가 갱신되었는지 확인해 주십시오.
   * GameChat.connect의 콜백으로 전달받는 Member는 갱신된 데이터입니다.

GameChat 소켓 서버에 연결하려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.connect(USER_ID,  (Member User, GameChatException Exception)=> 
{

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }
});
```

| ID       | type   | desc          |
| -------- | ------ | ------------- |
| USER\_ID | string | 채팅 사용자 고유 아이디 |

### Game Chat 서버 연결 해제

Game Chat 소켓 서버와의 연결을 해제하려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.disconnect();
```

### 채팅 사용자 정보 업데이트

connect 성공이 후 채팅 사용자 정보를 저장하고 업데이트하려면 아래 코드를 사용해 주십시오.

#### **닉네임 수정**

```csharp
GameChat.setNickname(USER_ID, NickName, (member, exception) =>
{
    if (exception != null)
    {
        // Error 핸들링
        return;
    }
    
});
```

| ID       | type   | desc          |
| -------- | ------ | ------------- |
| USER\_ID | string | 채팅 사용자 고유 아이디 |
| NickName | string | 채팅 사용자 닉네임    |

#### **Profile URL 수정**

```csharp
GameChat.setProfileUrl(USER_ID, ProfileUrl, (member, exception) =>
{
    if (exception != null)
    {
        // Error 핸들링
        return;
    }
    
});
```

| ID         | type   | desc              |
| ---------- | ------ | ----------------- |
| USER\_ID   | string | 채팅 사용자 고유 아이디     |
| ProfileUrl | string | 채팅 사용자 ProfileUrl |

## 채널 구독 및 구독 해제 <a href="#undefined" id="undefined"></a>

특정 채널에 Subscribe하거나 Unsubscribe하려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.subscribe(CHANNEL_ID);

GameChat.unsubscribe(CHANNEL_ID);
```

| ID          | type   | desc   |
| ----------- | ------ | ------ |
| CHANNEL\_ID | string | 채널 아이디 |

## 메시지 송신 <a href="#undefined" id="undefined"></a>

특정 채널에 메시지를 보내려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.sendMessage(CHANNEL_ID, MESSAGE);
```

| ID          | type   | desc       |
| ----------- | ------ | ---------- |
| CHANNEL\_ID | string | 채널 아이디     |
| MESSAGE     | string | 전송 메시지 텍스트 |

MESSAGE 파라미터에 @\[유저아이디] 공백 \[메시지 내용]으로 입력 시

```csharp
@user_id 메시지본문
```

위 케이스에서 유저아이디가 로그인된 이력이 있는 경우에 메시지 상세 정보 중 mentions의 정보는 유저아이디입니다.

## 이벤트 등록 및 해제 <a href="#undefined" id="undefined"></a>

Game Chat 소켓 서버로부터 수신되는 이벤트에 대해, 커스텀 핸들러를 등록하거나 해제하려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.dispatcher.(EVENT_NAME) += (CALLBACK_FUNCTION);
```

```csharp
public delegate void onConnectedCallback(string data);
public onConnectedCallback onConnected;
//'connect' Event에 대한, callback

public delegate void onDisconnectedCallback(string reason);
public onDisconnectedCallback onDisconnected;
//'disconnect' Event에 대한, callback

public delegate void onMessageReceivedCallback(Message message);
public onMessageReceivedCallback onMessageReceived;
//'message' Event에 대한, callback

public delegate void onUserAddedCallback(UserInfo userinfo);
public onUserAddedCallback onUserAdded;
//'userAdded' Event에 대한, callback

public delegate void onUserRemovedCallback(Message message);
public onUserRemovedCallback onUserRemoved;
//'userRemoved' Event에 대한, callback

public delegate void onErrorReceivedCallback(string result, GameChatException exception);
public onErrorReceivedCallback onErrorReceived;
//'error' Event에 대한, callback
```

## 예외 사항 <a href="#undefined" id="undefined"></a>

Game Chat API 사용 중에 발생하는 Exception에 대한 공통 처리 Class는 다음과 같습니다.

```csharp
public class GameChatException
{
    // Detail Error Code
   
    // 알 수 없는 Error
    public static readonly int CODE_UNKNOWN_ERROR           = 0;
    // 초기화 실패
    public static readonly int CODE_NOT_INITALIZE           = 1;
    // 파라미터가 올바르지 않은 경우
    public static readonly int CODE_INVAILD_PARAM           = 2;  
    // 소켓서버로부터 발생한 오류
    public static readonly int CODE_SOCKET_SERVER_ERROR     = 500;
    //소켓으로부터 발생한 오류
    public static readonly int CODE_SOCKET_ERROR = -501;
    // 네트워크 연결 오류 및 타임아웃 발생 시
    public static readonly int CODE_SERVER_NETWORK_ERROR    = 4002;
    // 서버에서 받은 데이터를 파싱할 때 오류
    public static readonly int CODE_SERVER_PARSING_ERROR    = 4003;

    // HTTP 에러의 경우, 해당 상태코드가 응답코드로 전달됩니다. (400, 403 ...)

    // Error Code
    public int code { get; set; }
    // Error Message
    public string message { get; set; }
}
```

## Client API <a href="#clientapi" id="clientapi"></a>

### 채널 구독 <a href="#undefined" id="undefined"></a>

**Subscription Data Class (per Unit)**

```csharp
public class Subscription
{
    public string id;
    public string channel_id;
    public string user_id;
    public string created_at;
}
```

| ID          | type   | desc          |
| ----------- | ------ | ------------- |
| id          | string | 유니크 아이디       |
| channel\_id | string | 채널 아이디        |
| user\_id    | string | 채팅 사용자 고유 아이디 |
| created\_at | string | 생성 일자         |

#### **채널 Subscription 목록 가져오기**

특정 채널의 Subscription 데이터를 목록 형태로 가져오려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.getSubscriptions(CHANNEL_ID, OFFSET, LIMIT, (List<Subscription> Subscriptions, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }

    foreach(Subscription elem in Subscriptions)
    {
        //handling each subscription instance
    }
}));
```

### 채널 <a href="#undefined" id="undefined"></a>

**Channel Data Class (per Unit)**

```csharp
public class Channel
{
    public string id;
    public string project_id;
    public string unique_id;
    public string name;
    public string user_id;
    public string created_at;
    public string updated_at;
}
```

<table><thead><tr><th width="158">ID</th><th width="137">type</th><th>desc</th></tr></thead><tbody><tr><td>id</td><td>string</td><td>채널 아이디(unique)</td></tr><tr><td>project_id</td><td>string</td><td>프로젝트 아이디</td></tr><tr><td>unique_id</td><td>string</td><td>개발사에서 설정 가능한 채널 아이디 (unique)</td></tr><tr><td>name</td><td>string</td><td>채널 이름</td></tr><tr><td>user_id</td><td>string</td><td>(채널 생성한) 채팅 사용자 아이디</td></tr><tr><td>created_at</td><td>string</td><td>생성 일자</td></tr><tr><td>updated_at</td><td>string</td><td>갱신 일자</td></tr></tbody></table>

#### **채널 목록 가져오기**

프로젝트의 채널 데이터를 목록 형태로 가져오려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.getChannels(OFFSET, LIMIT, (List<Channel> Channels, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }

    foreach(Channel elem in Channels)
    {
        //handling each channelInfo instance
    }
});
```

<table><thead><tr><th width="162">ID</th><th width="163">type</th><th>desc</th></tr></thead><tbody><tr><td>OFFSET</td><td>int</td><td>전체 채널 목록에서 가져올 채널의 시작 위치 (index)</td></tr><tr><td>LIMIT</td><td>int</td><td>가져올 채널 개수</td></tr></tbody></table>

#### **채널 데이터 가져오기**

채널 ID 및 UniqueID를 활용하여 채널 데이터를 가져오려면 아래 코드를 사용해 주십시오.

```csharp
//CHANNEL_ID로만 Search 할 경우, CHANNEL_UNIQUE_ID 파라미터에 null을 넣어 주십시오.

//CHANNEL_ID와 CHANNEL_UNIQUE_ID값이 함께 존재하면 CHANNEL_UNIQUE_ID 값을 우선으로 Search합니다.

GameChat.getChannel(CHANNEL_ID, CHANNEL_UNIQUE_ID,  (Channel Channel, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }

    //handling channelInfo instance
});
```

```csharp
GameChat.getChannel(CHANNEL_UNIQUE_ID, (Channel Channel, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }

    //handling channelInfo instance
});
```

<table><thead><tr><th width="222">ID</th><th width="150">type</th><th>desc</th></tr></thead><tbody><tr><td>CHANNEL_ID</td><td>string</td><td>채널 아이디 (auto generated)</td></tr><tr><td>CHANNEL_UNIQUE_ID</td><td>string</td><td>채널 (고유) 아이디 (customizing available)</td></tr></tbody></table>

### 채널 생성 및 삭제 <a href="#undefined" id="undefined"></a>

프로젝트 내 새로운 채널을 생성 및 삭제하려면 Open API를 활용해야 합니다. 보안 문제로 인해 채널 생성, 업데이트 등을 Open API를 활용하여 Server to Server로 직접 생성 하시는걸 추천 드립니다. 자세한 내용은 [Game Chat API 가이드](https://api.ncloud-docs.com/docs/game-gamechat){target="\_blank"}를 참고해 주십시오.

### 메시지 <a href="#undefined" id="undefined"></a>

**(Received) Message Data Class (per Unit)**

```csharp
public class Message
{
    public class User
    {
        public string id;
        public string name;
        public string profile;
    }

    public string message_id;
    public string channel_id;
    public string message_type;
    public string content;

    public string[] mentions;
    public bool mentions_everyone;
    public User sender;
    public string created_at;
}
```

<table><thead><tr><th width="181">ID</th><th width="116">type</th><th>desc</th></tr></thead><tbody><tr><td>message_id</td><td>string</td><td>메시지 유니크 아이디</td></tr><tr><td>channel_id</td><td>string</td><td>채널 아이디</td></tr><tr><td>message_type</td><td>string</td><td>메시지 타입</td></tr><tr><td>content</td><td>string</td><td>메시지 내용 (json string)</td></tr><tr><td>mentions</td><td>string</td><td>멘션(태그)</td></tr><tr><td>created_at</td><td></td><td>string</td></tr></tbody></table>

#### **메시지 목록 가져오기**

특정 채널에 대한 메시지 데이터를 목록 형태로 가져오려면 아래 코드를 사용해 주십시오.

```csharp
GameChat.getMessages(CHANNEL_ID, OFFSET, LIMIT, SEARCH, QUERY, SORT, (List<Message> Messages, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }

    foreach(Message elem in Messages)
    {
        //handling each message instance
    }
});
```

<table><thead><tr><th width="164">ID</th><th width="118">type</th><th>desc</th></tr></thead><tbody><tr><td>CHANNEL_ID</td><td>string</td><td>채널 아이디</td></tr><tr><td>OFFSET</td><td>string</td><td>전체 메시지 목록에서 가져올 메시지의 시작 위치</td></tr><tr><td>LIMIT</td><td>string</td><td>가져올 메시지 개수</td></tr><tr><td>SEARCH</td><td>string</td><td>메시지 검색 기준 key. &#x3C;예시> content.text<br>빈 문자열 전달 시, full scan</td></tr><tr><td>QUERY</td><td>string</td><td><p>메시지 검색 value. 완전 일치만 검색 가능. 빈 문자열 전달 시, </p><p>full scan</p></td></tr><tr><td>SORT</td><td>string</td><td>메시지 정렬 순서 (default : desc - 가장 최근 순) (optional : asc)</td></tr></tbody></table>

#### **메시지 번역**

자동 번역 기능이 활성화되어 있을 경우, 임의의 텍스트를 지정한 언어로 번역할 수 있습니다. 자동 번역 기능은 [Papago Translation](https://www.ncloud.com/product/aiService/papagoTranslation){target="\_blank"} 서비스와 연동한 후에 사용할 수 있습니다.

**(Received) Translation Data Class (per Unit)**

```csharp
public class Translation
{
    public string detectLang = "";
    public string lang = "";
    public bool translated = false;
    public string message = "";
}
```

<table><thead><tr><th width="192">ID</th><th width="149">type</th><th>desc</th></tr></thead><tbody><tr><td>detectLang</td><td>string</td><td>출발 언어 코드</td></tr><tr><td>lang</td><td>string</td><td>도착 언어 코드</td></tr><tr><td>translated</td><td>bool</td><td>번역 성공 여부</td></tr><tr><td>message</td><td>string</td><td>결과 메시지 내용 (json string)</td></tr></tbody></table>

> 참고
>
> 출발 언어 코드와 도착 언어 코드에 대한 설명은 [Papago Text Translation API 가이드](https://api.ncloud-docs.com/docs/ai-naver-papagonmt-translation){target="\_blank"}를 참고해 주십시오.&#x20;

```csharp
GameChat.translateMessage(CHANNEL_ID, SORCE_LANG, TARTGET_LANG, TEXT, (List<Translation> Translations, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }

    foreach(Translation elem in Translations)
    {
        //handling each Translation instance
    }
});

GameChat.translateMessage(SORCE_LANG, TARTGET_LANG, TEXT, (List<Translation> Translations, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }

    foreach(Translation elem in Translations)
    {
        //handling each Translation instance
    }
});
```

<table><thead><tr><th width="178">ID</th><th width="102">type</th><th>desc</th></tr></thead><tbody><tr><td>CHANNEL_ID</td><td>string</td><td>채널 아이디</td></tr><tr><td>SORCE_LANG</td><td>string</td><td>송신할 텍스트 언어명 (auto: 자동감지)<br><a href="https://api.ncloud-docs.com/docs/ai-naver-papagonmt-translation">API Guide</a>{target="_blank"} 참고</td></tr><tr><td>TARTGET_LANG</td><td>string</td><td>(번역 수신할) 텍스트 언어 코드<br>(","로 구분하여 복수 입력 가능. &#x3C;예시> "en, fr, th")<br><a href="https://api.ncloud-docs.com/docs/ai-naver-papagonmt-translation">Papago Text Translation API 가이드 </a>{target="blank"} 참고</td></tr><tr><td>TEXT</td><td>string</td><td>송신할 텍스트</td></tr></tbody></table>

### 채팅 사용자 <a href="#undefined" id="undefined"></a>

**(Received) Member Data Class (per Unit)**

```csharp
public class Member
{
    public string id = "";
    public string project_id = "";
    public string nickname = "";
    public string profile_url = "";
    public string country = "";
    public string remoteip = "";
    public string adid = "";
    public string device = "";
    public string network = "";
    public string version = "";
    public string model = "";
    public string logined_at = "";
    public string created_at = "";
    public string updated_at = "";
}
```

<table><thead><tr><th width="191">ID</th><th width="137">type</th><th>desc</th></tr></thead><tbody><tr><td>id</td><td>string</td><td>채팅 사용자 고유 아이디</td></tr><tr><td>project_id</td><td>string</td><td>로그인한 Game Chat 프로젝트 아이디</td></tr><tr><td>nickname</td><td>string</td><td>채팅 사용자 닉네임</td></tr><tr><td>profile_url</td><td>string</td><td>프로필 이미지 URL</td></tr><tr><td>country</td><td>string</td><td>접속 국가</td></tr><tr><td>remoteip</td><td>string</td><td>접속 IP</td></tr><tr><td>adid</td><td>string</td><td>광고 식별자</td></tr><tr><td>device</td><td>string</td><td>접속 디바이스 환경</td></tr><tr><td>network</td><td>string</td><td>접속 네트워크 타입(CELLULAR, WIFI)</td></tr><tr><td>version</td><td>string</td><td>접속 앱 버전</td></tr><tr><td>model</td><td>string</td><td>접속 디바이스 모델</td></tr><tr><td>logined_at</td><td>string</td><td>로그인한 일자</td></tr><tr><td>created_at</td><td>string</td><td>채팅 사용자 생성 일자</td></tr><tr><td>updated_at</td><td>string</td><td>채팅 사용자 정보 갱신 일자</td></tr></tbody></table>

#### **채팅 사용자 정보 업데이트**

채팅 서버의 사용자 정보를 업데이트할 수 있습니다.

```csharp

// 채팅 사용자 닉네임 업데이트
// 닉네임 허용 문자열은 whitespace(spaces, tabs, line breaks)를 포함하지 않는 2~128자입니다.
GameChat.setName(MEMBER_ID, NAME, (Member member, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }
    //handling updated Member instance
});

//채팅 사용자 프로필 이미지 url 업데이트
GameChat.setProfileUrl(MEMBER_ID, PROFILE_URL, (Member member, GameChatException Exception) => {

    if(Exception != null)
    {
        // Error 핸들링
        return;
    }
    //handling updated Member instance
});

```

<table><thead><tr><th width="183">ID</th><th width="138">type</th><th>desc</th></tr></thead><tbody><tr><td>MEMBER_ID</td><td>string</td><td>채팅 사용자 고유 아이디</td></tr><tr><td>NAME</td><td>string</td><td>채팅 사용자 닉네임 혹은 이름</td></tr><tr><td>PROFILE</td><td>string</td><td>프로필 이미지 URL</td></tr></tbody></table>

## GameChatExtension (Emoji, HyperLink) <a href="#gamechatextensionemojihyperlink" id="gamechatextensionemojihyperlink"></a>

수신 메시지에 포함된 Emoji와 HyperLink 텍스트를 쉽게 다룰 수 있도록 도와주는 Helper Class 입니다.

* TMP\_GameChatTextUGUI는 Unity Built-In Asset인 TextMeshPro를 확장한 클래스이므로 먼저 Package Manager를 이용하여 TextMeshPro를 설치해야 합니다.
* TextMeshPro Asset의 경우, Unity 2018.2 이상의 버전부터 Built-In Asset으로 포함됩니다.
* Emoji Sprite Sheet의 경우, Emoji version 13(Android)를 기준으로 기본 출력되며 Sprite Sheet를 변경하여 커스터마이징이 가능합니다.

```csharp
namespace GameChatUnity.Extension
{
    public class TMP_GameChatTextUGUI : TextMeshProUGUI
    {
        public bool isHyperLinked { get; set; }    // link 형태 주소를 hyperlink 처리 여부 (append html tag)
        public string LinkTextColor { get; set; }  // hyperlink text color
    }
}
```

**<예시>**

```csharp
using GameChatUnity.Extension;

TMP_GameChatTextUGUI message = msgObject.GetComponent<TMP_GameChatTextUGUI>();

//hyperlink 인식 및 처리를 위해, text는 setMessage를 통해 넣어 주십시오.
message.setMessage(MESSAGE_CONTENT);
message.color = Color.green;
message.isHyperLinked = true;

...

msgObject = Instantiate(msgObject) as GameObject;

...

// hyperlink에 대한 click event listener는, 직접 구현해 주십시오.

//Handling with TMP_LinkInfo
TMP_LinkInfo linkInfoArr = message.textInfo.linkInfo[LINK_INDEX];

...
```
