---
title: 使用语音聊天功能
slug: world-sdk-guide-ch/7PM4uC2GEEoZLfT6h_aD3
docTags: 
createdAt: 2024-09-02T08:34:13.134Z
---

Zepeto.Voice.Chat 预览版本包启用世界中的语音聊天。



## 安装 ZEPETO.Voice.Chat 并设置模式

1\) 安装 Window → 包管理器 → 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 包，您将看到添加的语音聊天模式选项。

::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;**&#x7B2C;一次在ZEPETO中使用语音聊天功能时，会出现关于麦克风访问的弹出窗口。
您必须允许访问才能使用语音聊天功能。
:::

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-1uuwxj2Ex-naVmnnoFpi2-20240904-102426.png "当您启用语音聊天功能时，按钮会自动出现在用户界面中")

![您可以通过触摸语音聊天按钮来使用它](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-vVNrp9SxpJYzvoAy8gXAg-20240904-102425.png "您可以通过触摸语音聊天按钮来使用它")



:::hint{type="danger"}
- 在 ZEPETO 应用程序 3.26.000 及以上版本中，语音聊天扬声器图标不显示。
- 如果您想让语音图标显示，请切换到脚本模式并按照指南编写自己的代码。
:::



## 脚本模式：使用 VoiceChat API

脚本模式在团队分隔的频道中启用语音聊天。

请参考下面的功能描述和示例来实现该功能。

| **API**                                 | **描述**                                                                               |
| --------------------------------------- | ------------------------------------------------------------------------------------ |
| OnInitializedEvent                      | 在初始化事件中，当发生True事件时，语音聊天API可用。<br />如果此事件为True，则语音聊天可用；如果为False，则语音聊天不可用。             |
| EnterRoom(RoomProperty roomProperty)    | 进入语音聊天房间的功能<br />- 成功进入语音聊天房间后，语音聊天按钮将出现                                             |
| ExitRoom()                              | 离开语音聊天房间的功能                                                                          |
| OnRoomConnectedEvent                    | 检查进入语音聊天房间状态的事件。<br />如果此事件为真，则用户已进入语音聊天房间；如果为假，则用户已离开语音聊天房间。                        |
| OnSpeechDetectedEvent\<string, boolean> | 检测当前在语音聊天中说话的用户的事件。<br />- string是说话用户的userId<br />- boolean在发言开始时为true，在发言结束时为false |
| ChangeTeamID(number teamid)             | 更改语音聊天房间中使用的TeamId的功能<br />- 只能设置为1或更大的整数值                                           |

### 房间属性类

| **API**      | **描述**                                                                                                                                                                                                       |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| SetAudioMode | 在语音聊天中使用的音频模式<br />- **AudioMode.Omnidirectional:** 语音聊天模式，不基于距离进行语音衰减<br />（您可以与或不与ZEPETO角色一起使用）<br /><br /><br />- **AudioMode.Directional:** 语音聊天模式，基于距离进行语音衰减<br />（ZEPETO角色必须存在于场景中，因为此模式基于ZEPETO角色的位置） |
| SetTeamID    | 在语音聊天中使用的TeamId，以启用具有相同TeamId的用户之间的语音聊天。<br />- 只能设置为1或更高的整数值                                                                                                                                                |

### 特定团队语音聊天实现示例

这里有一些示例代码来尝试语音聊天API。

1\) 添加一个按钮并将文本记录到画布上，如下图所示，以进入语音聊天团队频道并显示状态日志。

- 按钮
  - 按钮\_蓝队 : 蓝队语音聊天入口按钮
  - 按钮\_红队 : 红队语音聊天入口按钮
  - 按钮\_退出 : 退出语音聊天按钮
- 文本
  - 文本\_日志 : 显示聊天日志的文本
  - 文本\_团队 : 显示同一语音聊天团队频道成员列表的文本
  - 文本\_当前发言 : 显示当前在语音聊天中发言的用户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] OnInitialized: " + initialized;
    }
 
     // 当语音聊天房间连接时调用的方法
    private OnRoomConnected(connected: boolean) {
        this.logText.text = "[VoiceChat] OnRoomConnected: " + connected;
    }
 
    // 当检测到用户的语音或未检测到时调用的方法
    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 = "[当前说话用户 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\) 完成脚本后，在检查器中分配步骤 1 中创建的按钮和文本。

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



5\) 通过二维码或测试链接在移动设备上运行测试，您可以看到在按下“进入团队”按钮时创建了语音聊天激活按钮，并且每当玩家说话时，日志会显示哪个玩家在语音聊天中，如下屏幕所示。

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



### 显示语音聊天气泡图像的示例

在ZEPETO App 3.26.000及更高版本中，语音聊天发言图标不显示。
如果您想处理发言图标的显示，请遵循指南并编写自己的代码。

1\) 当玩家在语音聊天中时，要在角色头上显示气泡，您需要创建一个使用气泡图像的预制件。导入您想使用的气泡PNG图像，如下所示，并将其纹理类型更改为Sprite。

![](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 预制件以进入预制编辑模式，然后添加层级 > UI > 画布。
- 将画布的 RectTransform 组件的值更改为以下内容
  - PosX: 0 , posY: 0
  - 宽度: 100, 高度: 100
  - 缩放X: 0.005, 缩放Y: 0.005, 缩放Z: 0.005
- 将画布组件的 RenderMove 更改为世界空间。

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



5\) 在画布内创建一个对话气泡图像。

- 将层级 > UI > 图像添加为画布的子项，并将其重命名为 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 {
 
    // 公共 GameObject 变量用于存储语音聊天气泡预制件
    public voiceChatPrefab: GameObject;
 
    // 私有映射用于存储与用户 ID 相关的 GameObjects
    private _voiceBubbleMap: Map<string, GameObject> = new Map<string, GameObject>();
 
    Start() {
        // 为语音聊天控制器添加事件监听器
        VoiceChatController.OnInitializedEvent.AddListener(init => this.OnInitialized(init));
        VoiceChatController.OnRoomConnectedEvent.AddListener(connected => this.OnRoomConnected(connected));
        VoiceChatController.OnSpeechDetectedEvent.AddListener((userId, speechDetected) => this.OnSpeechDetected(userId, speechDetected));
    }
 
    // 当语音聊天系统初始化时调用的方法
    private OnInitialized(initialized: boolean) {
        console.log("[VoiceChat] OnInitialized: ");
        this.EnterVoiceChatRoom(1);
    }
 
    // 当语音聊天房间连接时调用的方法
    private OnRoomConnected(connected: boolean) {
        console.log("[VoiceChat] OnRoomConnected: ");
    }
 
    // 根据给定的团队索引进入语音聊天房间的方法
    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);
 
        // 检查用户 ID 是否不在语音气泡映射中，如果不在则创建一个语音气泡
        if (!this._voiceBubbleMap.has(userId)) {
            this.CreateVoiceBubble(userId);
        }
 
        this.SetVoiceBubble(userId, speechDetected);
    }
 
    // 设置给定用户 ID 的语音气泡的活动状态的方法
    private SetVoiceBubble(userId: string, speechDetected: boolean) {
        const chatBubble = this._voiceBubbleMap.get(userId);
        chatBubble.SetActive(speechDetected);
    }
 
    // 为给定用户 ID 创建语音气泡的方法
    private CreateVoiceBubble(userId: string) {
 
        // 获取用户角色的头部插槽
        const headSocket = ZepetoPlayers.instance.GetPlayerWithUserId(userId).character.GetSocket(KnowSockets.HEAD_UPPER);
 
        // 在头部插槽位置实例化语音聊天气泡预制件
        const instanceBubble = Object.Instantiate(this.voiceChatPrefab, headSocket) as GameObject;
 
        // 将实例化的气泡添加到语音气泡映射中
        this._voiceBubbleMap.set(userId, instanceBubble);
        instanceBubble.SetActive(false);
    }
 
    LateUpdate() {

        // 检查语音气泡映射是否为空，如果为空则返回
        if (this._voiceBubbleMap.size === 0) {
            return;
        }

        // 遍历语音气泡映射并更新每个气泡 GameObject 的旋转
        this._voiceBubbleMap.forEach((bubbleObject: GameObject) => {
            // 设置气泡对象的旋转以匹配相机的父变换旋转
            bubbleObject.transform.rotation = ZepetoPlayers.instance.ZepetoCamera.cameraParent.transform.rotation;
        });

    }
 
}
```
:::



- 代码描述
  - 当脚本执行时，它将在Start()函数中注册每个按钮的事件，并在VoiceChatController的OnInitialized、OnRoomConnected和OnSpeechDetected事件中注册事件。
  - 在OnInitialized()之后，当玩家进入世界时，语音聊天按钮将显示在玩家的屏幕上，当他们按下按钮时，将执行EnterVoiceChatRoom()以进入语音聊天房间。
  - 每当玩家激活语音聊天并说话时，OnSpeechDetected()将被执行，如果speechDetected为true，则启用BubbleChat对象，如果为false，则禁用它。
  - 第一次激活OnSpeechDetected()函数时，CreateVoiceBubble()将实例化一个voiceChatPrefab游戏对象，漂浮在玩家的头顶，并将其注册为\_map数据。
  - 我们使用LateUpdate()每帧更新语音聊天气泡的旋转，以匹配ZEPETO世界的CameraParent。这确保了气泡图像始终面向相机。它首先检查\_voiceBubbleMap是否为空，否则更新气泡图像预制件的旋转。



8\) 完成脚本编写后，在检查器中将VoiceChatPrefab条目分配为您在步骤1中创建的气泡预制件。

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



9\) 通过二维码或测试链接在移动设备上运行，您应该会看到每当玩家使用语音聊天时，气泡图像出现在角色的头顶。



## 语音调制

从语音聊天版本 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 指南和示例代码尝试语音调制。

| **语音类型 (枚举)** | **值** | **描述** |
| ------------- | ----- | ------ |
| 类型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();
    }
}
```

- 代码描述：
  - 为每个按钮添加一个 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;

        // 当语音聊天系统初始化时调用的方法
        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` 监听器被调用时，请应用以下设置：
- 通过 `SetAudioMode(AudioMode.Directional)` 将语音聊天设置为 3D 空间音频模式。
- 通过 `VoiceChatController.SetLocalPlayerTransform()` 将本地玩家的语音聊天位置设置为 voiceTransform。
- buttonCloserTransform 在按钮被点击时将 voiceTransform 的位置设置为原点 (0, 0, 0)。
- buttonFartherTransform 在按钮被点击时将 voiceTransform 的位置设置为 (10, 0, 0)。



2\) 将场景中的 Canvas 上的按钮注册到 VoiceTransformSample 组件的 buttonCloserTransform 和 buttonFartherTransform。

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



3\) 如果您使用二维码或测试链接在手机上运行它，您可以检查以下内容。

- 当您按下更近按钮时，您的声音会听起来更接近其他人。
- 当您按下更远按钮时，您的声音会被听到得离其他人很远。



::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"}

