---
title: การใช้ฟังก์ชันแชทด้วยเสียง
slug: world-sdk-guide-th/Xp9H5KEVAMprPsd89u09v
docTags: 
createdAt: 2024-09-02T08:34:13.134Z
---

Zepeto.Voice.Chat รุ่นพรีวิวช่วยให้สามารถสนทนาด้วยเสียงในโลกได้.



## ติดตั้ง ZEPETO.Voice.Chat และตั้งค่าโหมด

1\) ติดตั้ง Windows → Package Manager → ZEPETO.Voice.Chat.

:::hint{type="danger"}
- ฟีเจอร์นี้สามารถใช้ได้ในโลกที่มีการใช้งานองค์ประกอบหลายผู้เล่น.
- การทดสอบสามารถทำได้เฉพาะในมือถือเท่านั้น ไม่สามารถทดสอบในตัวแก้ไขได้.
- เนื่องจากนโยบายของ iOS เสียงของอุปกรณ์จะไม่ลดลงเป็นศูนย์เมื่อไมโครโฟนเปิดในโลกที่เปิดใช้งานการสนทนาด้วยเสียง.
  เสียงของอุปกรณ์จะลดลงเป็นศูนย์เมื่อไมโครโฟนปิด.
:::



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

2\) คลิกปุ่ม \[▼] ในเมนูกลางของหน้าจอโปรเจกต์ Unity ที่กำลังพัฒนา.
คลิกปุ่ม \[เปิดการตั้งค่าโลก] ในเมนูป๊อปอัพที่ปรากฏขึ้น.

::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 อย่างถูกต้อง คุณจะเห็นตัวเลือกโหมด 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\) ในโหมด Voice Chat กรุณาตั้งค่าโหมดที่ต้องการ

- พื้นฐาน: ใช้ฟีเจอร์การสนทนาด้วยเสียงที่มีให้โดยค่าเริ่มต้น
- สคริปต์: ใช้ฟีเจอร์การสนทนาด้วยเสียงโดยการใช้ API การสนทนาด้วยเสียง
  - คุณสามารถจัดการการเข้าหรือออกจากห้องสำหรับการสนทนาด้วยเสียงในระหว่างการทำงาน
  - ดำเนินการสนทนาด้วยเสียงสำหรับทีมที่แตกต่างกัน (เช่น การสนทนาด้วยเสียงของทีมแดงและการสนทนาด้วยเสียงของทีมฟ้า)
  - ตรวจจับผู้ใช้ที่กำลังพูดอยู่ในขณะนี้



## โหมดพื้นฐาน: การใช้ฟีเจอร์แชทเสียง

โลกที่มีการแชทเสียงในโหมดพื้นฐานมีลักษณะดังนี้.

คุณยังสามารถสร้างการแชทเสียงที่สนุกสนานมากขึ้นโดยใช้ฟีเจอร์การปรับเสียง.

การแชทเสียงจะเปิดใช้งานโดยอัตโนมัติเมื่อเข้าสู่โลกที่มีฟังก์ชันการแชทเสียง.

:::hint{type="success"}
**👍&#x20;**&#xE40;มื่อคุณใช้ฟีเจอร์แชทเสียงใน ZEPETO เป็นครั้งแรก จะมีป๊อปอัพเกี่ยวกับการเข้าถึงไมโครโฟนปรากฏขึ้น.
คุณต้องอนุญาตการเข้าถึงเพื่อใช้ฟังก์ชันการแชทเสียง.
:::

![](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 />หากเหตุการณ์นี้เป็นจริง ผู้ใช้ได้เข้าสู่ห้องสนทนาด้วยเสียง; หากเป็นเท็จ ผู้ใช้ได้ออกจากห้องสนทนาด้วยเสียง.                           |
| OnSpeechDetectedEvent\<string, boolean> | เหตุการณ์เพื่อตรวจจับผู้ใช้ที่กำลังพูดในห้องสนทนาด้วยเสียง<br />- string คือ userId ของผู้ใช้ที่พูด<br />- boolean เป็นจริงเมื่อเริ่มพูด และเป็นเท็จเมื่อพูดจบ                                   |
| ChangeTeamID(number teamid)             | ฟังก์ชันเพื่อเปลี่ยน TeamId ที่ใช้ในห้องสนทนาด้วยเสียง<br />- สามารถตั้งค่าเป็นค่าจำนวนเต็ม 1 หรือมากกว่าเท่านั้น                                                                                |

### คลาส RoomProperty

| **API**      | **คำอธิบาย**                                                                                                                                                                                                                                                                                                                                                    |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SetAudioMode | โหมดเสียงที่จะใช้ในการสนทนาทางเสียง<br />- **AudioMode.Omnidirectional:** โหมดสนทนาทางเสียงที่ไม่มีการลดเสียงตามระยะทาง<br />(คุณสามารถใช้มันได้ทั้งกับหรือไม่มีตัวละคร ZEPETO)<br /><br /><br />- **AudioMode.Directional:** โหมดสนทนาทางเสียงที่มีการลดเสียงตามระยะทาง<br />(ตัวละคร ZEPETO ต้องมีอยู่ในฉาก เนื่องจากโหมดนี้ทำงานตามตำแหน่งของตัวละคร ZEPETO) |
| SetTeamID    | TeamId ที่จะใช้ในการสนทนาทางเสียงเพื่อเปิดใช้งานการสนทนาทางเสียงระหว่างผู้ใช้ที่มี TeamId เดียวกัน<br />- สามารถตั้งค่าเป็นค่า int ที่ 1 หรือสูงกว่าเท่านั้น                                                                                                                                                                                                    |

### ตัวอย่างการใช้งานการสนทนาทางเสียงเฉพาะทีม

นี่คือตัวอย่างโค้ดเพื่อทดลองใช้ Voice Chat API.

1\) เพิ่มปุ่มและบันทึกข้อความลงบนผืนผ้าใบตามที่แสดงในภาพด้านล่างเพื่อเข้าสู่ช่องทีม Voice Chat และแสดงบันทึกสถานะ

- ปุ่ม
  - Button\_blueTeam : ปุ่มเข้าสู่การสนทนาของทีมสีน้ำเงิน
  - Button\_redTeam : ปุ่มเข้าสู่การสนทนาของทีมสีแดง
  - Button\_exit : ปุ่มออกจากการสนทนา
- ข้อความ
  - Text\_log : ข้อความเพื่อแสดงบันทึกการสนทนา
  - Text\_team : ข้อความเพื่อแสดงรายชื่อสมาชิกในช่องทีม Voice Chat เดียวกัน
  - Text\_currentSpeaking : ข้อความเพื่อแสดง UserId ของผู้ที่กำลังพูดใน Voice Chat

![](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 components เป็นสมาชิกสาธารณะ
    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();
         
        // เพิ่ม event listeners สำหรับการคลิกปุ่ม
        this.BlueTeamButton.onClick.AddListener(()=>{
            this.EnterBlueTeam();
        });
        this.RedTeamButton.onClick.AddListener(()=>{
            this.EnterRedTeam();
        });
        this.exitRoomButton.onClick.AddListener(()=>{
            this.ExitVoiceChatRoom();
        });
 
        // เพิ่ม event listeners สำหรับ 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));
         
        this.logText.text = "[VoiceChat] VoiceChatTest เริ่มต้น";
    }
 
    // OnDestroy method ถูกเรียกเมื่อสคริปต์ถูกทำลาย
    OnDestroy() {
        VoiceChatController.OnInitializedEvent.RemoveAllListeners();
        VoiceChatController.OnRoomConnectedEvent.RemoveAllListeners();
        VoiceChatController.OnSpeechDetectedEvent.RemoveAllListeners();
    }
 
    // Method ที่ถูกเรียกเมื่อระบบ Voice Chat ถูกเริ่มต้น
    private OnInitialized(initialized: boolean) {
        this.logText.text = "[VoiceChat] OnInitialized: " + initialized;
    }
 
     // Method ที่ถูกเรียกเมื่อห้อง Voice Chat เชื่อมต่อ
    private OnRoomConnected(connected: boolean) {
        this.logText.text = "[VoiceChat] OnRoomConnected: " + connected;
    }
 
    // Method ที่ถูกเรียกเมื่อการพูดของผู้ใช้ถูกตรวจพบหรือไม่ถูกตรวจพบ
    private OnSpeechDetected(userId: string, speechDetected: boolean) {
        this.logText.text = "[VoiceChat] OnSpeechDetected: " + userId + ", " + speechDetected;
 
        if (speechDetected) {
            this.currentSpeakingUserIDs.push(userId);
        }
        else {
            let index = this.currentSpeakingUserIDs.indexOf(userId);
            this.currentSpeakingUserIDs.splice(index, 1);
        }
 
        let userIds = "[CurrentSpeakingUserIDs]\n";
        for (let i = 0; i < this.currentSpeakingUserIDs.length; i++) {
            userIds += this.currentSpeakingUserIDs[i];
            userIds += "\n";
        }
 
        this.currentSpeakingUserIDsText.text = userIds;
    }
 
    // Method เพื่อเข้าสู่ห้อง Voice Chat ของทีมสีน้ำเงิน
    public EnterBlueTeam(){
        this.EnterVoiceChatRoom(2);
    }
 
    // Method เพื่อเข้าสู่ห้อง Voice Chat ของทีมสีแดง
    public EnterRedTeam(){
        this.EnterVoiceChatRoom(3);
    }
 
    // Method เพื่อเข้าสู่ห้อง Voice Chat ตามดัชนีทีมที่กำหนด
    private EnterVoiceChatRoom(teamIndex: number) {
        console.log("[VoiceChat] EnterVoiceChatRoom");
 
        // สร้างวัตถุ RoomProperty ใหม่และตั้งค่าคุณสมบัติของมัน
        let roomProperty = new RoomProperty();
        roomProperty.SetAudioMode(AudioMode.Omnidirectional);
        roomProperty.SetTeamID(teamIndex);
        VoiceChatController.EnterRoom(roomProperty);
        this.currentTeamIDText.text = "TeamID: " + roomProperty.TeamId.toString();
    }
 
    // Method เพื่อออกจากห้อง Voice Chat
    public ExitVoiceChatRoom() {
        this.logText.text = "[VoiceChat] ExitVoiceChatRoom";
        VoiceChatController.ExitRoom();
    }
 
    // Method เพื่อเปลี่ยน Team ID สำหรับผู้ใช้ปัจจุบัน
    public ChangeTeamID() {
        // รับ Team ID ใหม่จากฟิลด์ป้อนและแปลงเป็นจำนวนเต็ม
        let teamID = parseInt(this.teamIDInputField.text);
        this.logText.text = "[VoiceChat] ChangeTeamID: " + teamID;
        this.currentTeamIDText.text = "TeamID: " + teamID.toString();
        // เปลี่ยน Team ID ในระบบ Voice Chat
        VoiceChatController.ChangeTeamID(teamID);
    }
 
}
```
:::



- คำอธิบายโค้ด
  - เมื่อสคริปต์ทำงาน มันจะลงทะเบียนเหตุการณ์บนปุ่มแต่ละปุ่มในฟังก์ชัน Start() และลงทะเบียนเหตุการณ์ OnInitialized, OnRoomConnected, และ OnSpeechDetected บน VoiceChatController.
  - เมื่อปุ่มทีมสีน้ำเงินหรือปุ่มทีมสีแดงถูกคลิก ฟังก์ชัน EnterVoiceChatRoom() จะถูกเรียกใช้และผู้ใช้จะเข้าสู่ห้องแชทเสียงโดยการป้อน RoomProperty ที่กำหนดไว้ คุณสมบัติของห้องในตัวอย่างมีดังนี้
    - **roomProperty.SetAudioMode :** AudioMode.Omnidirectional. โหมดเสียง Omnidirectional เป็นโหมดที่เสียงมีระดับเสียงเหมือนกันไม่ว่าจะอยู่ในตำแหน่งใดของผู้เล่น ในทางตรงกันข้าม โหมด Directional เป็นโหมดเสียง 3D ซึ่งหมายความว่าระดับเสียงจะฟังแตกต่างกันขึ้นอยู่กับตำแหน่งของตัวละคร ดังนั้นยิ่งตัวละครอยู่ไกล เสียงก็จะยิ่งเบาลง.
    - **roomProperty.SetTeamID :** ช่อง 2 สำหรับทีมสีน้ำเงิน ช่อง 3 สำหรับทีมสีแดง SetTeamID ทำหน้าที่เหมือนช่องใน Voice Chat ทีมที่เข้าสู่ช่อง 2 สามารถแชทเสียงกับผู้เล่นในช่อง 2 เท่านั้น และทีมที่เข้าสู่ช่อง 3 สามารถแชทเสียงกับผู้เล่นในช่อง 3 เท่านั้น.
  - ExitVoiceChatRoom() จะออกจากช่องทีมแชทเสียงปัจจุบัน.
  - OnSpeechDetected() จะส่งออก ID ของผู้เล่นที่กำลังใช้แชทเสียงในช่องทีมไปยังข้อความบันทึก.



4\) หลังจากเสร็จสิ้นสคริปต์ ให้กำหนดปุ่มและข้อความที่สร้างขึ้นในขั้นตอนที่ 1 ใน Inspector.

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



5\) ทดสอบบนมือถือผ่าน QR code หรือลิงก์ทดสอบ และคุณจะเห็นว่าปุ่มเปิดใช้งานการสนทนาด้วยเสียงถูกสร้างขึ้นเมื่อกดปุ่ม Enter Team และเมื่อใดก็ตามที่ผู้เล่นพูด บันทึกจะแสดงว่าผู้เล่นคนใดอยู่ในการสนทนาด้วยเสียง ตามที่แสดงในหน้าจอด้านล่างนี้

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



### ตัวอย่างการแสดงภาพฟองสนทนาด้วยเสียง

ในแอป ZEPETO เวอร์ชัน 3.26.000 และเวอร์ชันที่ใหม่กว่า ไอคอนการพูดคุยด้วยเสียงจะไม่ปรากฏ
หากคุณต้องการให้ไอคอนการพูดคุยปรากฏ กรุณาทำตามคำแนะนำและเขียนโค้ดของคุณเอง

1\) เพื่อแสดงฟองพูดเหนือหัวตัวละครเมื่อผู้เล่นอยู่ในการสนทนาด้วยเสียง คุณจะต้องสร้าง prefab ที่ใช้ภาพฟองพูด นำเข้าภาพฟองพูด PNG ที่คุณต้องการใช้ตามที่แสดงด้านล่างและเปลี่ยนประเภท Texture Type เป็น Sprite

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



2\) โครงการ > สร้าง > Prefab และเปลี่ยนชื่อเป็น ChatBubble

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



3\) ดับเบิลคลิกที่ prefab ChatBubble เพื่อเข้าสู่โหมดแก้ไข prefab เลือก prefab ChatBubble และเลือก Transform > Position > เปลี่ยนค่า Y เป็น 0.35

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



4\) สร้าง Canvas ภายในวัตถุ prefab.

- ดับเบิลคลิกที่ prefab ChatBubble เพื่อเข้าสู่โหมดแก้ไข prefab จากนั้นเพิ่ม Hierachy > UI > Canvas.
- เปลี่ยนค่าของส่วนประกอบ RectTransform ของ Canvas เป็นดังนี้
  - PosX: 0 , posY: 0
  - ความกว้าง: 100, ความสูง: 100
  - ScaleX: 0.005, ScaleY: 0.005, ScaleZ: 0.005
- เปลี่ยน RenderMove ของส่วนประกอบ Canvas เป็น World Space.

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



5\) สร้างภาพพูดคุยภายใน Canvas.

- เพิ่ม Hierarchy > UI > Image เป็นลูกของ Canvas และเปลี่ยนชื่อเป็น ChatBubbleImage.
- เปลี่ยนค่าของส่วนประกอบ RectTransform ของ ChatBubbleImage เป็นดังนี้
  - ความกว้าง 42, ความสูง:42
- ลงทะเบียน Sprite รูปภาพที่คุณนำเข้ามาในขั้นตอนที่ 1 เป็น Source Image ในส่วนประกอบ Image.

![](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 {
 
    // ตัวแปร GameObject สาธารณะเพื่อเก็บ prefab ของ voice chat bubble
    public voiceChatPrefab: GameObject;
 
    // แผนที่ส่วนตัวเพื่อเก็บ GameObjects ที่เกี่ยวข้องกับ user IDs
    private _voiceBubbleMap: Map<string, GameObject> = new Map<string, GameObject>();
 
    Start() {
        // เพิ่ม event listeners สำหรับ 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));
    }
 
    // เมธอดที่เรียกเมื่อระบบ Voice Chat ถูกเริ่มต้น
    private OnInitialized(initialized: boolean) {
        console.log("[VoiceChat] OnInitialized: ");
        this.EnterVoiceChatRoom(1);
    }
 
    // เมธอดที่เรียกเมื่อห้อง Voice Chat เชื่อมต่อ
    private OnRoomConnected(connected: boolean) {
        console.log("[VoiceChat] OnRoomConnected: ");
    }
 
    // เมธอดเพื่อเข้าสู่ห้อง Voice Chat ตามดัชนีทีมที่กำหนด
    private EnterVoiceChatRoom(teamIndex: number) {
        console.log("[VoiceChat] EnterVoiceChatRoom");
 
        // สร้างวัตถุ RoomProperty ใหม่และตั้งค่าคุณสมบัติของมัน
        let roomProperty = new RoomProperty();
        roomProperty.SetAudioMode(AudioMode.Omnidirectional);
        VoiceChatController.EnterRoom(roomProperty);
    }
 
    // เมธอดที่เรียกเมื่อการพูดของผู้ใช้ถูกตรวจพบหรือไม่ถูกตรวจพบ
    private OnSpeechDetected(userId: string, speechDetected: boolean) {
        console.log("[VoiceChat] OnSpeechDetected: " + userId + ", " + speechDetected);
 
        // ตรวจสอบว่าหมายเลขผู้ใช้ไม่ได้อยู่ในแผนที่ voice bubble และสร้าง voice bubble หากยังไม่มี
        if (!this._voiceBubbleMap.has(userId)) {
            this.CreateVoiceBubble(userId);
        }
 
        this.SetVoiceBubble(userId, speechDetected);
    }
 
    // เมธอดเพื่อกำหนดสถานะการใช้งานของ voice bubble สำหรับหมายเลขผู้ใช้ที่กำหนด
    private SetVoiceBubble(userId: string, speechDetected: boolean) {
        const chatBubble = this._voiceBubbleMap.get(userId);
        chatBubble.SetActive(speechDetected);
    }
 
    // เมธอดเพื่อสร้าง voice bubble สำหรับหมายเลขผู้ใช้ที่กำหนด
    private CreateVoiceBubble(userId: string) {
 
        // รับ socket หัวของตัวละครผู้ใช้
        const headSocket = ZepetoPlayers.instance.GetPlayerWithUserId(userId).character.GetSocket(KnowSockets.HEAD_UPPER);
 
        // สร้าง prefab ของ voice chat bubble ที่ตำแหน่ง socket หัว
        const instanceBubble = Object.Instantiate(this.voiceChatPrefab, headSocket) as GameObject;
 
        // เพิ่ม bubble ที่สร้างขึ้นในแผนที่ voice bubble
        this._voiceBubbleMap.set(userId, instanceBubble);
        instanceBubble.SetActive(false);
    }
 
    LateUpdate() {

        // ตรวจสอบว่าแผนที่ voice bubble ว่างเปล่าหรือไม่และคืนค่าหากว่างเปล่า
        if (this._voiceBubbleMap.size === 0) {
            return;
        }

        // ทำการวนซ้ำผ่านแผนที่ voice bubble และอัปเดตการหมุนของแต่ละ GameObject bubble
        this._voiceBubbleMap.forEach((bubbleObject: GameObject) => {
            // ตั้งค่าการหมุนของวัตถุ bubble ให้ตรงกับการหมุนของ transform ของกล้อง
            bubbleObject.transform.rotation = ZepetoPlayers.instance.ZepetoCamera.cameraParent.transform.rotation;
        });

    }
 
}
```
:::



- คำอธิบายโค้ด
  - เมื่อสคริปต์ถูกเรียกใช้งาน มันจะลงทะเบียนเหตุการณ์บนปุ่มแต่ละปุ่มในฟังก์ชัน Start() และเหตุการณ์ OnInitialized, OnRoomConnected, และ OnSpeechDetected บน VoiceChatController.
  - หลังจาก OnInitialized() ปุ่ม Voice Chat จะถูกแสดงบนหน้าจอของผู้เล่นเมื่อพวกเขาเข้าสู่โลก และเมื่อพวกเขากดปุ่ม EnterVoiceChatRoom() จะถูกเรียกใช้งานเพื่อเข้าสู่ห้อง Voice Chat.
  - เมื่อใดก็ตามที่ผู้เล่นเปิดใช้งาน VoiceChat และพูด OnSpeechDetected() จะถูกเรียกใช้งาน และถ้า speechDetected เป็น true วัตถุ BubbleChat จะถูกเปิดใช้งาน และถ้า false จะถูกปิดใช้งาน.
  - เมื่อฟังก์ชัน OnSpeechDetected() ถูกเรียกใช้งานครั้งแรก CreateVoiceBubble() จะสร้าง GameObject voiceChatPrefab เพื่อให้ลอยอยู่เหนือหัวของผู้เล่นและลงทะเบียนเป็นข้อมูลแผนที่ใน \_voiceBubbleMap.
  - เราใช้ LateUpdate() เพื่ออัปเดตการหมุนของฟองเสียงทุกเฟรมให้ตรงกับ CameraParent ของโลก ZEPETO ซึ่งทำให้แน่ใจว่าภาพฟองเสียงจะหันไปทางกล้องเสมอ มันจะตรวจสอบก่อนว่า \_voiceBubbleMap ว่างหรือไม่ ถ้าไม่ว่างก็จะอัปเดตการหมุนของภาพฟองเสียง prefab.



8\) หลังจากเขียนสคริปต์เสร็จแล้ว ให้กำหนด VoiceChatPrefab ใน inspector เป็น prefab ฟองเสียงที่คุณสร้างในขั้นตอนที่ 1.

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



9\) เรียกใช้งานบนมือถือผ่าน QR code หรือลิงก์ทดสอบ และคุณควรเห็นภาพฟองเสียงปรากฏอยู่เหนือหัวของตัวละครเมื่อผู้เล่นใช้เสียงแชท.



## การปรับเสียง

เริ่มต้นด้วย Voice Chat เวอร์ชัน 0.2.1-preview คุณสามารถปรับเสียง Voice Chat ในโหมดสคริปต์ได้

::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]);
            });
        };
 
        // เมธอดที่เรียกเมื่อระบบ Voice Chat ถูกเริ่มต้น
        VoiceChatController.OnInitializedEvent.AddListener(
            init => {
                VoiceChatController.EnterRoom(new RoomProperty());;
            }
        );
 
        // เมธอดที่เรียกเมื่อห้อง Voice Chat เชื่อมต่อ
        VoiceChatController.OnRoomConnectedEvent.AddListener(
            connected => {
                // ตั้งค่าประเภทเสียงเริ่มต้นและเปิดใช้งาน loopback
                VoiceChatController.SetVoiceType(voiceTypes[0]);
                VoiceChatController.EnableLoopback(true);
            }
        );
    }
 
    // เมธอดเพื่อออกจากห้อง Voice Chat
    OnDestroy() {
        VoiceChatController.OnInitializedEvent.RemoveAllListeners();
        VoiceChatController.OnRoomConnectedEvent.RemoveAllListeners();
    }
}
```

- คำอธิบายโค้ด:
  - เพิ่มผู้ฟัง onClick สำหรับแต่ละปุ่มเพื่อตั้งค่าประเภทการปรับเสียงที่แตกต่างกัน.
    - องค์ประกอบของดัชนีเดียวกันใน `ปุ่ม` และ `ประเภทเสียง` จะถูกจับคู่.
  - เมื่อเข้าสู่โลก, `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;

        // เมธอดที่เรียกเมื่อระบบ Voice Chat ถูกเริ่มต้น
        VoiceChatController.OnInitializedEvent.AddListener(
            init => {
                let roomProperty = new RoomProperty();

                // ตั้งค่าโหมดเสียงเป็นเสียง 3D ทิศทาง
                roomProperty.SetAudioMode(AudioMode.Directional);
                VoiceChatController.EnterRoom(roomProperty);  

                // ตั้งค่าการเปลี่ยนตำแหน่งเสียงสำหรับเสียงของผู้เล่นท้องถิ่น
                VoiceChatController.SetLocalPlayerTransform(this.voiceTransform);
            }
        );
        
        // ตั้งค่าตำแหน่งการเปลี่ยนตำแหน่งเสียงเป็น (0,0,0) เมื่อคลิก 'buttonCloseTransform'
        this.buttonCloserTransform.onClick.AddListener(()=>{
            this.voiceTransform.position = Vector3.zero;
        })
        
        // ตั้งค่าตำแหน่งการเปลี่ยนตำแหน่งเสียงเป็นจุดที่ห่างเมื่อคลิก 'buttonFarTransform'
        this.buttonFartherTransform.onClick.AddListener(()=>{
            this.voiceTransform.position = new Vector3(0,0,-10);
        })

    }

    // เมธอดเพื่อออกจากห้อง Voice Chat
    OnDestroy() {
        VoiceChatController.OnInitializedEvent.RemoveAllListeners();
        VoiceChatController.OnRoomConnectedEvent.RemoveAllListeners();
    }

}
```
:::

คำอธิบายโค้ด

- เมื่อฉากเริ่มต้น สร้างวัตถุเกมที่เรียกว่า voiceTransformObject ใน `Start()` ฟังก์ชันและลงทะเบียน Transform ใน voiceTransform.
- เมื่อการสนทนาเสียง `OnInitialized` ถูกเรียก กรุณาใช้การตั้งค่าต่อไปนี้:
- ตั้งค่าการสนทนาเสียงเป็นโหมดเสียง 3D ผ่าน `SetAudioMode(AudioMode.Directional)`.
- ตั้งค่าตำแหน่งการสนทนาเสียงของผู้เล่นท้องถิ่นเป็น voiceTransform ผ่าน `VoiceChatController.SetLocalPlayerTransform()`.
- buttonCloserTransform ตั้งค่าตำแหน่งของ voiceTransform เป็นจุดกำเนิด (0, 0, 0) เมื่อปุ่มถูกคลิก.
- buttonFartherTransform ตั้งค่าตำแหน่งของ voiceTransform เป็น (10, 0, 0) เมื่อปุ่มถูกคลิก.



2\) ลงทะเบียนปุ่มบน Canvas ในฉากไปยัง buttonCloserTransform และ buttonFartherTransform ของคอมโพเนนต์ VoiceTransformSample.

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



3\) หากคุณใช้งานบนโทรศัพท์มือถือโดยใช้ QR code หรือลิงก์ทดสอบ คุณสามารถตรวจสอบสิ่งต่อไปนี้ได้.

- เมื่อคุณกดปุ่ม 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"}

