---
title: 음성 채팅 기능 사용하기
slug: world-sdk-guide-kr/CNgRC3G_Shf-mk9kVkZGL
docTags: 
createdAt: 2024-09-02T08:34:13.134Z
---

Zepeto.Voice.Chat 미리보기 버전 패키지는 월드에서 음성 채팅을 가능하게 합니다.



## ZEPETO.Voice.Chat 설치 및 모드 설정

1\) 윈도우 설치 → 패키지 관리자 → ZEPETO.Voice.Chat.

:::hint{type="danger"}
- 이 기능은 멀티플레이어 요소가 구현된 월드에서 사용할 수 있습니다.
- 테스트는 모바일 테스트에서만 가능하며, 에디터 테스트에서는 사용할 수 없습니다.
- iOS 정책으로 인해 음성 채팅이 활성화된 월드에서 마이크가 켜져 있을 때 장치 볼륨이 0으로 떨어지지 않습니다.
  마이크가 꺼지면 장치 볼륨이 0으로 떨어집니다.
:::



![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-DtPOyH4MoNNIsunfy4HmO-20240904-102425.png)

2\) 개발 중인 유니티 프로젝트 화면의 중간 메뉴에서 \[▼] 버튼을 클릭합니다.
팝업 메뉴에서 \[월드 설정 열기] 버튼을 클릭합니다.

::Image[]{src="https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-FKLyhsT_NpGSs69hIudps-20240904-102425.png" size="72" width="362" height="224" position="center" showCaption="false"}



3\) ZEPETO.Voice.Chat 패키지가 올바르게 설치되면, 음성 채팅 모드 옵션이 추가된 것을 볼 수 있습니다.

::Image[]{src="https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-mT_dx8P_ZKL9pl4pcE7-X-20240904-102425.png" size="62" width="416" height="453" position="center" showCaption="false"}



4\) 음성 채팅 모드에서 원하는 모드를 설정해 주세요.

- 기본: 기본 제공되는 음성 채팅 기능을 사용합니다.
- 스크립트: 음성 채팅 API를 구현하여 음성 채팅 기능을 사용합니다.
  - 런타임에서 음성 채팅을 위한 방 접근 진입/퇴장을 처리할 수 있습니다.
  - 구별된 팀을 위한 음성 채팅 구현 (예: 빨간 팀 음성 채팅 및 파란 팀 음성 채팅)
  - 현재 말하고 있는 사용자 감지



## 기본 모드: 음성 채팅 기능 사용하기

기본 모드 음성 채팅이 있는 월드는 이렇게 생겼습니다.

음성 변조 기능을 사용하여 더 재미있는 음성 채팅을 만들 수 있습니다.

음성 채팅 기능이 있는 월드에 들어가면 음성 채팅이 자동으로 활성화됩니다.

:::hint{type="success"}
**👍&#x20;**&#x5A;EPETO에서 음성 채팅 기능을 처음 사용할 때 마이크 접근에 대한 팝업이 나타납니다.
음성 채팅 기능을 사용하려면 접근을 허용해야 합니다.
:::

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-1uuwxj2Ex-naVmnnoFpi2-20240904-102426.png "음성 채팅 기능을 활성화하면 UI에 버튼이 자동으로 나타납니다.")

![음성 채팅 버튼을 터치하여 사용할 수 있습니다.](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-vVNrp9SxpJYzvoAy8gXAg-20240904-102425.png "음성 채팅 버튼을 터치하여 사용할 수 있습니다.")



:::hint{type="danger"}
- ZEPETO 앱 3.26.000 이상에서는 음성 채팅 스피커 아이콘이 나타나지 않습니다.
- 스피치 아이콘을 나타내고 싶다면 스크립트 모드로 변경하고 가이드를 따라 자신의 코드를 작성하세요.
:::



## 스크립트 모드 : VoiceChat API 사용하기

스크립트 모드는 팀별로 분리된 채널에서 음성 채팅을 가능하게 합니다.

기능을 구현하기 위해 아래의 기능 설명 및 예제를 참조하십시오.

| **API**                                 | **설명**                                                                                                                       |
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| OnInitializedEvent                      | 음성 채팅 API는 초기화 이벤트에서 True 이벤트가 발생한 후 사용할 수 있습니다.<br />이 이벤트가 True이면 음성 채팅을 사용할 수 있으며, False이면 음성 채팅을 사용할 수 없습니다.             |
| EnterRoom(RoomProperty roomProperty)    | 음성 채팅 방에 들어가기 위한 함수<br />- 음성 채팅 방에 성공적으로 들어가면 음성 채팅 버튼이 나타납니다.                                                              |
| ExitRoom()                              | 음성 채팅 방을 나가기 위한 함수                                                                                                           |
| OnRoomConnectedEvent                    | 음성 채팅 방에 들어간 상태를 확인하는 이벤트입니다.<br />이 이벤트가 true이면 사용자가 음성 채팅 방에 들어간 것이고, false이면 사용자가 음성 채팅 방을 나간 것입니다.                       |
| OnSpeechDetectedEvent\<string, boolean> | 현재 음성 채팅에서 말하고 있는 사용자를 감지하는 이벤트입니다.<br />- string은 말하고 있는 사용자의 userId입니다.<br />- boolean은 발화가 시작될 때 true, 발화가 끝날 때 false입니다. |
| ChangeTeamID(number teamid)             | 음성 채팅 방에서 사용되는 TeamId를 변경하는 함수입니다.<br />- 1 이상의 int 값으로만 설정할 수 있습니다.                                                         |

### 룸 속성 클래스

| **API**      | **설명**                                                                                                                                                                                                                                                        |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SetAudioMode | 음성 채팅에서 사용할 오디오 모드<br />- **AudioMode.Omnidirectional:** 거리 기반 음성 감쇠가 없는 음성 채팅 모드<br />(이 모드는 ZEPETO 캐릭터와 함께 또는 없이 사용할 수 있습니다)<br /><br /><br />- **AudioMode.Directional:** 거리 기반 음성 감쇠가 있는 음성 채팅 모드<br />(ZEPETO 캐릭터의 위치에 따라 작동하므로 장면에 ZEPETO 캐릭터가 있어야 합니다) |
| SetTeamID    | 음성 채팅에서 사용될 TeamId로, 동일한 TeamId를 가진 사용자 간의 음성 채팅을 활성화합니다.<br />- 정수 값 1 이상으로만 설정할 수 있습니다                                                                                                                                                                      |

### 팀별 음성 채팅 구현 예시

여기 음성 채팅 API를 시도해 볼 수 있는 예제 코드가 있습니다.

1\) 아래 이미지와 같이 버튼을 추가하고 텍스트를 캔버스에 기록하여 음성 채팅 팀 채널에 들어가고 상태 로그를 표시합니다.

- 버튼
  - Button\_blueTeam : 블루 팀 음성 채팅 입장 버튼
  - Button\_redTeam : 레드 팀 음성 채팅 입장 버튼
  - Button\_exit : 음성 채팅 종료 버튼
- 텍스트
  - Text\_log : 채팅 로그를 표시할 텍스트
  - Text\_team : 같은 음성 채팅 팀 채널의 멤버 목록을 표시할 텍스트
  - Text\_currentSpeaking : 현재 음성 채팅에서 말하고 있는 사용자 ID를 표시할 텍스트.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-euodAMUXM6E3-jg4tSzzL-20240904-102425.png)



2\) 프로젝트 생성 > 생성 > ZEPETO > TypeScript로 이름을 VoiceChatTest로 변경합니다.

3\) 아래와 같이 샘플 스크립트를 작성합니다.

:::CodeblockTabs
VoiceChatTest

```typescript
import { Button, InputField, Text } from 'UnityEngine.UI';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import { AudioMode, RoomProperty, VoiceChatController } from 'ZEPETO.Voice.Chat'
 
export default class VoiceChatTest extends ZepetoScriptBehaviour {
 
    // UI 구성 요소를 공개 멤버로 선언
    public BlueTeamButton: Button;
    public RedTeamButton: Button;
    public exitRoomButton: Button;
    public logText: Text;
    public currentTeamIDText: Text;
    public currentSpeakingUserIDsText: Text;
     
    // 말하는 사용자 ID와 팀 ID 입력 필드를 저장하기 위한 비공개 멤버 선언
    private currentSpeakingUserIDs: string[];   
    private teamIDInputField: InputField;
 
    Start() {
        this.currentSpeakingUserIDs = new Array();
         
        // 버튼 클릭에 대한 이벤트 리스너 추가
        this.BlueTeamButton.onClick.AddListener(()=>{
            this.EnterBlueTeam();
        });
        this.RedTeamButton.onClick.AddListener(()=>{
            this.EnterRedTeam();
        });
        this.exitRoomButton.onClick.AddListener(()=>{
            this.ExitVoiceChatRoom();
        });
 
        // 음성 채팅 컨트롤러에 대한 이벤트 리스너 추가
        VoiceChatController.OnInitializedEvent.AddListener(init => this.OnInitialized(init));
        VoiceChatController.OnRoomConnectedEvent.AddListener(connected => this.OnRoomConnected(connected));
        VoiceChatController.OnSpeechDetectedEvent.AddListener((userId, speechDetected) => this.OnSpeechDetected(userId, speechDetected));
         
        this.logText.text = "[VoiceChat] VoiceChatTest 시작";
    }
 
    // 스크립트가 파괴될 때 호출되는 OnDestroy 메서드
    OnDestroy() {
        VoiceChatController.OnInitializedEvent.RemoveAllListeners();
        VoiceChatController.OnRoomConnectedEvent.RemoveAllListeners();
        VoiceChatController.OnSpeechDetectedEvent.RemoveAllListeners();
    }
 
    // 음성 채팅 시스템이 초기화될 때 호출되는 메서드
    private OnInitialized(initialized: boolean) {
        this.logText.text = "[VoiceChat] 초기화됨: " + initialized;
    }
 
     // 음성 채팅 방이 연결될 때 호출되는 메서드
    private OnRoomConnected(connected: boolean) {
        this.logText.text = "[VoiceChat] 방 연결됨: " + connected;
    }
 
    // 사용자의 음성이 감지되거나 감지되지 않을 때 호출되는 메서드
    private OnSpeechDetected(userId: string, speechDetected: boolean) {
        this.logText.text = "[VoiceChat] 음성 감지됨: " + userId + ", " + speechDetected;
 
        if (speechDetected) {
            this.currentSpeakingUserIDs.push(userId);
        }
        else {
            let index = this.currentSpeakingUserIDs.indexOf(userId);
            this.currentSpeakingUserIDs.splice(index, 1);
        }
 
        let userIds = "[현재 말하는 사용자 IDs]\n";
        for (let i = 0; i < this.currentSpeakingUserIDs.length; i++) {
            userIds += this.currentSpeakingUserIDs[i];
            userIds += "\n";
        }
 
        this.currentSpeakingUserIDsText.text = userIds;
    }
 
    // 블루 팀의 음성 채팅 방에 들어가는 메서드
    public EnterBlueTeam(){
        this.EnterVoiceChatRoom(2);
    }
 
    // 레드 팀의 음성 채팅 방에 들어가는 메서드
    public EnterRedTeam(){
        this.EnterVoiceChatRoom(3);
    }
 
    // 주어진 팀 인덱스에 따라 음성 채팅 방에 들어가는 메서드
    private EnterVoiceChatRoom(teamIndex: number) {
        console.log("[VoiceChat] 음성 채팅 방에 들어감");
 
        // 새로운 RoomProperty 객체를 생성하고 속성을 설정
        let roomProperty = new RoomProperty();
        roomProperty.SetAudioMode(AudioMode.Omnidirectional);
        roomProperty.SetTeamID(teamIndex);
        VoiceChatController.EnterRoom(roomProperty);
        this.currentTeamIDText.text = "팀 ID: " + roomProperty.TeamId.toString();
    }
 
    // 음성 채팅 방에서 나가는 메서드
    public ExitVoiceChatRoom() {
        this.logText.text = "[VoiceChat] 음성 채팅 방에서 나감";
        VoiceChatController.ExitRoom();
    }
 
    // 현재 사용자의 팀 ID를 변경하는 메서드
    public ChangeTeamID() {
        // 입력 필드에서 새로운 팀 ID를 가져오고 정수로 파싱
        let teamID = parseInt(this.teamIDInputField.text);
        this.logText.text = "[VoiceChat] 팀 ID 변경: " + teamID;
        this.currentTeamIDText.text = "팀 ID: " + teamID.toString();
        // 음성 채팅 시스템에서 팀 ID 변경
        VoiceChatController.ChangeTeamID(teamID);
    }
 
}
```
:::



- 코드 설명
  - 스크립트가 실행되면 Start() 함수에서 각 버튼에 이벤트를 등록하고 VoiceChatController에서 OnInitialized, OnRoomConnected 및 OnSpeechDetected 이벤트를 등록합니다.
  - 블루 팀 버튼 또는 레드 팀 버튼이 클릭되면 EnterVoiceChatRoom() 함수가 실행되고 사용자는 지정된 RoomProperty를 입력하여 음성 채팅 방에 들어갑니다. 예제의 방 속성은 다음과 같습니다.
    - **roomProperty.SetAudioMode :** AudioMode.Omnidirectional. Omnidirectional 오디오 모드는 플레이어의 위치에 관계없이 볼륨이 동일하게 들리는 모드입니다. 반면 Directional 모드는 3D 사운드 모드로, 캐릭터의 위치에 따라 볼륨이 다르게 들리므로 캐릭터가 멀어질수록 볼륨이 낮아집니다.
    - **roomProperty.SetTeamID :** 블루 팀은 채널 2, 레드 팀은 채널 3입니다. SetTeamID는 음성 채팅에서 채널처럼 작동합니다. 채널 2에 들어간 팀은 채널 2의 플레이어와만 음성 채팅을 할 수 있으며, 채널 3에 들어간 팀은 채널 3의 플레이어와만 음성 채팅을 할 수 있습니다.
  - ExitVoiceChatRoom()은 현재 음성 채팅 팀 채널에서 나갑니다.
  - OnSpeechDetected()는 현재 팀 채널에서 음성 채팅을 사용 중인 플레이어 ID를 로그 텍스트에 출력합니다.



4\) 스크립트를 완료한 후, Step1에서 생성한 버튼과 텍스트를 검사기에서 할당합니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-IwXdr4PigI9UPRGnz9PjK-20240904-102425.png)



5\) QR 코드나 테스트 링크를 통해 모바일에서 테스트를 실행하면, 팀에 참여 버튼을 누를 때 음성 채팅 활성화 버튼이 생성되고, 플레이어가 말을 할 때마다 로그에 어떤 플레이어가 음성 채팅에 있는지 표시됩니다. 다음 화면과 같이 나타납니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-eqa8jcnJifdqPWRi_gfiS-20240904-102426.gif)



### 음성 채팅 말풍선 이미지 표시 예시

ZEPETO 앱 3.26.000 및 이후 버전에서는 음성 채팅 발화 아이콘이 나타나지 않습니다.
발화 아이콘이 나타나도록 하려면 가이드를 따라 자신의 코드를 작성해 주세요.

1\) 플레이어가 음성 채팅에 있을 때 캐릭터 머리 위에 말풍선을 표시하려면, 말풍선 이미지를 사용하는 프리팹을 생성해야 합니다. 아래와 같이 사용하고자 하는 말풍선 PNG 이미지를 가져오고, 텍스처 유형을 스프라이트로 변경하세요.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-lrXSo7B0WsNgXnrHRdZHB-20240904-102425.png)



2\) 프로젝트 > 생성 > 프리팹을 선택하고 이름을 ChatBubble로 변경합니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-FAeAB-SKLLJO2mSUsRMYI-20240904-102425.png)



3\) ChatBubble 프리팹을 더블 클릭하여 프리팹 편집 모드로 들어갑니다. ChatBubble 프리팹을 선택하고 변환 > 위치 > Y 값을 0.35로 변경합니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-urkq1VvXJ435xClO0o2fb-20240904-102425.png)



4\) 프리팹 객체 안에 캔버스를 생성합니다.

- ChatBubble 프리팹을 더블 클릭하여 프리팹 편집 모드로 들어가고, Hierachy > UI > Canvas를 추가합니다.
- 캔버스의 RectTransform 구성 요소의 값을 다음과 같이 변경합니다.
  - PosX: 0 , posY: 0
  - 너비: 100, 높이: 100
  - ScaleX: 0.005, ScaleY: 0.005, ScaleZ: 0.005
- 캔버스 구성 요소의 RenderMove를 월드 스페이스로 변경합니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-ASRVtrEzxzOstsTiJ7CN--20240904-102425.png)



5\) 캔버스 안에 말풍선 이미지를 생성합니다.

- Hierarchy > UI > Image를 캔버스의 자식으로 추가하고, 이름을 ChatBubbleImage로 변경합니다.
- ChatBubbleImage의 RectTransform 구성 요소의 값을 다음과 같이 변경합니다.
  - 너비 42, 높이: 42
- 1단계에서 가져온 이미지 스프라이트를 이미지 구성 요소의 소스 이미지로 등록합니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-EWBWugOZ_VWpDOXPi3WYc-20240904-102425.png)



:::hint{type="success"}
👍 **팁**

- 말풍선 이미지 스프라이트를 애니메이션으로 만들 수 있습니다.
- 다른 팀을 위해 별도의 말풍선 이미지를 사용할 수도 있습니다.
:::



6\) 프로젝트 > 생성 > ZEPETO > TypeScript를 생성하고 이름을 VoiceChatBubbleController로 변경합니다.

7\) 아래와 같이 샘플 스크립트를 작성하세요.

:::CodeblockTabs
VoiceChatBubbleController

```typescript
import { GameObject, Object } from 'UnityEngine';
import { KnowSockets, ZepetoPlayers } from 'ZEPETO.Character.Controller'
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import { AudioMode, RoomProperty, VoiceChatController } from 'ZEPETO.Voice.Chat';
 
export default class VoiceChatBubbleController extends ZepetoScriptBehaviour {
 
    // Public GameObject variable to store the voice chat bubble prefab
    public voiceChatPrefab: GameObject;
 
    // Private map to store GameObjects associated with user IDs
    private _voiceBubbleMap: Map<string, GameObject> = new Map<string, GameObject>();
 
    Start() {
        // Add event listeners for Voice Chat Controller
        VoiceChatController.OnInitializedEvent.AddListener(init => this.OnInitialized(init));
        VoiceChatController.OnRoomConnectedEvent.AddListener(connected => this.OnRoomConnected(connected));
        VoiceChatController.OnSpeechDetectedEvent.AddListener((userId, speechDetected) => this.OnSpeechDetected(userId, speechDetected));
    }
 
    // Method called when the Voice Chat system is initialized
    private OnInitialized(initialized: boolean) {
        console.log("[VoiceChat] OnInitialized: ");
        this.EnterVoiceChatRoom(1);
    }
 
    // Method called when the Voice Chat room is connected
    private OnRoomConnected(connected: boolean) {
        console.log("[VoiceChat] OnRoomConnected: ");
    }
 
    // Method to enter a Voice Chat room based on the given team index
    private EnterVoiceChatRoom(teamIndex: number) {
        console.log("[VoiceChat] EnterVoiceChatRoom");
 
        // Create a new RoomProperty object and set its properties
        let roomProperty = new RoomProperty();
        roomProperty.SetAudioMode(AudioMode.Omnidirectional);
        VoiceChatController.EnterRoom(roomProperty);
    }
 
    // Method called when a user's speech is detected or not detected
    private OnSpeechDetected(userId: string, speechDetected: boolean) {
        console.log("[VoiceChat] OnSpeechDetected: " + userId + ", " + speechDetected);
 
        // Check if the user ID is not in the voice bubble map and create a voice bubble if it is not
        if (!this._voiceBubbleMap.has(userId)) {
            this.CreateVoiceBubble(userId);
        }
 
        this.SetVoiceBubble(userId, speechDetected);
    }
 
    // Method to set the active state of the voice bubble for a given user ID
    private SetVoiceBubble(userId: string, speechDetected: boolean) {
        const chatBubble = this._voiceBubbleMap.get(userId);
        chatBubble.SetActive(speechDetected);
    }
 
    // Method to create a voice bubble for a given user ID
    private CreateVoiceBubble(userId: string) {
 
        // Get the head socket of the user's character
        const headSocket = ZepetoPlayers.instance.GetPlayerWithUserId(userId).character.GetSocket(KnowSockets.HEAD_UPPER);
 
        // Instantiate the voice chat bubble prefab at the head socket position
        const instanceBubble = Object.Instantiate(this.voiceChatPrefab, headSocket) as GameObject;
 
        // Add the instantiated bubble to the voice bubble map
        this._voiceBubbleMap.set(userId, instanceBubble);
        instanceBubble.SetActive(false);
    }
 
    LateUpdate() {

        // Check if the voice bubble map is empty and return if it is
        if (this._voiceBubbleMap.size === 0) {
            return;
        }

        // Iterate through the voice bubble map and update the rotation of each bubble GameObject
        this._voiceBubbleMap.forEach((bubbleObject: GameObject) => {
            // Set the rotation of the bubble object to match the camera's parent transform rotation
            bubbleObject.transform.rotation = ZepetoPlayers.instance.ZepetoCamera.cameraParent.transform.rotation;
        });

    }
 
}
```
:::



- 코드 설명
  - 스크립트가 실행되면 Start() 함수에서 각 버튼에 이벤트를 등록하고 VoiceChatController의 OnInitialized, OnRoomConnected, OnSpeechDetected 이벤트를 등록합니다.
  - OnInitialized() 후, 플레이어가 세계에 들어가면 음성 채팅 버튼이 화면에 표시되며, 버튼을 누르면 EnterVoiceChatRoom()이 실행되어 음성 채팅 방에 들어갑니다.
  - 플레이어가 VoiceChat을 활성화하고 말을 할 때마다 OnSpeechDetected()가 실행되며, speechDetected가 true이면 BubbleChat 객체가 활성화되고, false이면 비활성화됩니다.
  - OnSpeechDetected() 함수가 처음 활성화될 때, CreateVoiceBubble()이 음성 채팅 프리팹 GameObject를 플레이어의 머리 위에 띄우고 \_voiceBubbleMap에 맵 데이터로 등록합니다.
  - LateUpdate()를 사용하여 매 프레임마다 음성 채팅 버블의 회전을 ZEPETO 세계의 CameraParent에 맞추어 업데이트합니다. 이렇게 하면 말풍선 이미지가 항상 카메라를 향하게 됩니다. 먼저 \_voiceBubbleMap이 비어 있는지 확인하고, 그렇지 않으면 말풍선 이미지 프리팹의 회전을 업데이트합니다.



8\) 스크립트 작성을 마친 후, 검사기에서 VoiceChatPrefab 항목을 STEP 1에서 생성한 말풍선 프리팹으로 지정합니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-tUT4LmLYnUUzyly2cq5d4-20240904-102425.png)



9\) QR 코드나 테스트 링크를 통해 모바일에서 실행하면 플레이어가 음성 채팅을 사용할 때마다 캐릭터 머리 위에 말풍선 이미지가 나타나는 것을 볼 수 있습니다.



## 음성 변조

음성 채팅 버전 0.2.1-preview부터 스크립트 모드에서 음성 채팅 음성을 조절할 수 있습니다.

::embed[[https://www.youtube.com/watch?v=yXfPuQyPbqk](https://www.youtube.com/watch?v=yXfPuQyPbqk)]{url="https://www.youtube.com/watch?v=yXfPuQyPbqk"}



API 가이드와 샘플 코드를 사용하여 음성 조절을 시도해 보세요.

| **음성 유형 (enum)** | **값** | **설명** |
| ---------------- | ----- | ------ |
| 유형00             | 0     | 원본     |
| 유형01             | 1     | 치프먼크   |
| 유형02             | 2     | 삼촌     |
| 유형03             | 3     | 에코     |
| 유형04             | 4     | 딥 보이스  |
| 유형05             | 5     | 로봇     |
| 유형06             | 6     | 방언     |
| 유형07             | 7     | 메가폰    |
| 유형08             | 8     | 짐승     |
| 유형09             | 9     | 기계     |
| 유형10             | 10    | 강한 전류  |
| 유형11             | 11    | 아이     |
| 유형12             | 12    | 고슴도치   |



여기 음성 변조의 샘플 스크립트가 있습니다.

```javascript
import { Button } from 'UnityEngine.UI';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { RoomProperty, VoiceChatController, VoiceType } from 'ZEPETO.Voice.Chat';

export default class VoiceModulationManager extends ZepetoScriptBehaviour {
 
    // 다양한 음성 유형을 설정하기 위한 버튼들
    public buttons: Button[];
 
    Start() {
        const voiceTypes = [
            VoiceType.Type00,
            VoiceType.Type01,
            VoiceType.Type05,
            VoiceType.Type03,
        ];
 
        for (const [index, button] of this.buttons.entries()) {
            button.onClick.AddListener(() => {
                // 클릭된 버튼 인덱스에 해당하는 음성 유형 설정
                VoiceChatController.SetVoiceType(voiceTypes[index]);
            });
        };
 
        // 음성 채팅 시스템이 초기화될 때 호출되는 메서드
        VoiceChatController.OnInitializedEvent.AddListener(
            init => {
                VoiceChatController.EnterRoom(new RoomProperty());;
            }
        );
 
        // 음성 채팅 방에 연결될 때 호출되는 메서드
        VoiceChatController.OnRoomConnectedEvent.AddListener(
            connected => {
                // 초기 음성 유형 설정 및 루프백 활성화
                VoiceChatController.SetVoiceType(voiceTypes[0]);
                VoiceChatController.EnableLoopback(true);
            }
        );
    }
 
    // 음성 채팅 방을 나가는 메서드
    OnDestroy() {
        VoiceChatController.OnInitializedEvent.RemoveAllListeners();
        VoiceChatController.OnRoomConnectedEvent.RemoveAllListeners();
    }
}
```

- 코드 설명:
  - 각 버튼에 대해 클릭 리스너를 추가하여 서로 다른 음성 변조 유형을 설정합니다.
    - 같은 인덱스의 요소는 `버튼` 배열과 `음성 유형` 배열이 일치합니다.
  - 세계에 들어가면, `VoiceChatController`가 초기화되고, 방 연결이 자동으로 설정됩니다.
  - 초기 음성 변조 유형은 `Type00`, 그리고 루프백이 활성화되어 자신의 목소리를 들을 수 있습니다.
    - 다음 호출을 위해 주의하세요: `SetVoiceType` 및 `EnableLoopback`, 방 연결이 필요합니다. 따라서 초기 설정은`OnRoomConnectedEvent`의 리스너 내에서 처리됩니다.



![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-gDF6X-7KgtHpythXUaKcU-20240904-102426.png)



## 로컬 플레이어의 오디오 재생 위치 변경



로컬 플레이어의 음성 채팅 위치를 변경할 수 있습니다. `VoiceChatController.SetLocalPlayerTransform()`.

이를 사용하면 음성 채팅 중 특정 공간이나 객체 위치로 설정하여 다양한 시나리오를 만들 수 있습니다.



:::hint{type="info"}
**📘 팁**
음성 채팅의 변환은 로컬 플레이어의 ZEPETO 캐릭터 변환에 연결되어 있습니다.

따라서 로컬 플레이어의 ZEPETO 캐릭터가 세계에 생성되지 않았다면, 당신의 목소리가 전송되지 않을 수 있습니다.

이 경우, `SetLocalPlayerTransform()`를 사용하여 현재 장면 내에서 음성 재생 위치를 설정하여 음성 채팅을 사용할 수 있습니다.
:::



| **API**                                                                         | **설명**                      |
| ------------------------------------------------------------------------------- | --------------------------- |
| `public static SetLocalPlayerTransform($transform: UnityEngine.Transform):void` | 로컬 플레이어의 오디오 재생 위치를 변경하는 함수 |



:::hint{type="danger"}
**❗️ 주의**
`SetLocalPlayerTransform()`는 음성 채팅이 초기화될 때 `VoiceChatController.OnInitializedEvent(true)` 이후에 호출되어야 합니다.
:::



1\) 아래는 음성 위치 변경 기능을 위한 샘플 스크립트입니다.

:::CodeblockTabs
VoiceTransformSample

```typescript
import { GameObject, Transform, Vector3 } from 'UnityEngine';
import { Button } from 'UnityEngine.UI';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { AudioMode, RoomProperty, VoiceChatController } from 'ZEPETO.Voice.Chat';

export default class VoiceTransformSample extends ZepetoScriptBehaviour {

    // 음성 변환을 가깝게 설정하는 버튼
    public buttonCloserTransform: Button;

    // 음성 변환을 멀리 설정하는 버튼
    public buttonFartherTransform: Button;

    // 음성 위치를 위한 변환
    private voiceTransform: Transform;

    Start() {

        // 새로운 GameObject 생성 및 음성 변환 객체를 저장할 변환 할당
        const voiceTransformObject = new GameObject;  
        this.voiceTransform = voiceTransformObject.transform;

        // 음성 채팅 시스템이 초기화될 때 호출되는 메서드
        VoiceChatController.OnInitializedEvent.AddListener(
            init => {
                let roomProperty = new RoomProperty();

                // 오디오 모드를 방향성 3D 공간 오디오로 설정
                roomProperty.SetAudioMode(AudioMode.Directional);
                VoiceChatController.EnterRoom(roomProperty);  

                // 로컬 플레이어의 음성에 대한 음성 채팅 변환 설정
                VoiceChatController.SetLocalPlayerTransform(this.voiceTransform);
            }
        );
        
        // 'buttonCloseTransform'이 클릭될 때 음성 변환 위치를 (0,0,0)으로 설정
        this.buttonCloserTransform.onClick.AddListener(()=>{
            this.voiceTransform.position = Vector3.zero;
        })
        
        // 'buttonFarTransform'이 클릭될 때 음성 변환 위치를 먼 지점으로 설정
        this.buttonFartherTransform.onClick.AddListener(()=>{
            this.voiceTransform.position = new Vector3(0,0,-10);
        })

    }

    // 음성 채팅 방에서 나가는 메서드
    OnDestroy() {
        VoiceChatController.OnInitializedEvent.RemoveAllListeners();
        VoiceChatController.OnRoomConnectedEvent.RemoveAllListeners();
    }

}
```
:::

코드 설명

- 장면이 시작될 때, `Start()` 함수에서 voiceTransformObject라는 게임 오브젝트를 생성하고 voiceTransform에 Transform을 등록합니다.
- 음성 채팅이 `OnInitialized` 리스너가 호출되면, 다음 설정을 적용해 주세요:
- 음성 채팅을 3D 공간 오디오 모드로 설정합니다: `SetAudioMode(AudioMode.Directional)`.
- 로컬 플레이어의 음성 채팅 위치를 voiceTransform으로 설정합니다: `VoiceChatController.SetLocalPlayerTransform()`.
- buttonCloserTransform은 버튼이 클릭될 때 voiceTransform의 위치를 원점(0, 0, 0)으로 설정합니다.
- buttonFartherTransform은 버튼이 클릭될 때 voiceTransform의 위치를 (10, 0, 0)으로 설정합니다.



2\) 장면의 캔버스에 있는 버튼을 VoiceTransformSample 구성 요소의 buttonCloserTransform 및 buttonFartherTransform에 등록합니다.

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-cr-0SWeexanSXeb-6NHj5-20240904-102426.png)



3\) QR 코드를 사용하거나 테스트 링크를 통해 모바일 폰에서 실행하면 다음을 확인할 수 있습니다.

- Closer 버튼을 누르면, 당신의 목소리가 다른 사람에게 더 가까이 들립니다.
- Farther 버튼을 누르면, 당신의 목소리가 다른 사람에게 멀리 들립니다.



::embed[[https://www.youtube.com/watch?v=Y_pj4RCshts](https://www.youtube.com/watch?v=Y_pj4RCshts)]{url="https://www.youtube.com/watch?v=Y_pj4RCshts"}

