---
title: 将车辆应用到ZEPETO角色
slug: world-sdk-guide-ch/Jcn4-zepeto
docTags: 
createdAt: 2024-09-02T08:34:13.134Z
---

通过应用关于将物体附加到ZEPETO角色的指南，您可以实现像汽车、飞机、宠物等骑乘车辆，就像角色正在骑它们一样。

:::hint{type="info"}
**📘&#x20;**&#x8BF7;参考以下指南 \[[将物体附加到ZEPETO角色](docId\:R6xCzNe-z5w8UyCBGa18u)]
:::



## 设置动画师

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-NKEJxzlJFdOiobXRRrQQj-20240904-102415.png "要在骑乘车辆时更改ZEPETO角色的姿势，如上所述，您需要修改动画师并在ZEPETOPlayers下进行设置。")



1\) 通过转到项目 > 创建 > 动画控制器来创建一个动画控制器，并将其重命名为VehicleZepetoAnimator。

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



2\) 在动画师选项卡中，通过选择创建状态 > 空状态来创建一个空状态。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-9ePXPw9ZhCJMPE2vpXtxN-20240904-102415.png)



3\) 在检查器中适当地重命名，并将动画剪辑分配给运动。

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



- 要使用您所需的动画剪辑文件，请参考自定义动画指南。

:::hint{type="info"}
**📘&#x20;**&#x8BF7;参考以下指南 \[[应用自定义动画](docId\:GVco8Q0TWGTEBrFodORYe)]
:::



4\) 拖放 VehicleZepetoAnimator 到 ZEPETOPlayers 上以进行设置。

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

:::hint{type="success"}
**👍 提示**

- 如果相机高度需要根据车辆的大小进行更改，您可以在 ZEPETOPlayers 中预设相机 LookOffset 值。
:::



## 示例代码

1\) 通过访问项目 > 创建 > ZEPETO > TypeScript 创建一个 TypeScript，并将其重命名为 RideVehicle。

2\) 编写如下所示的示例脚本。

- 请注意，此示例中的车辆变换值应调整以匹配实际应用时的车辆预制件。

```typescript
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoCharacter, ZepetoPlayers } from 'ZEPETO.Character.Controller';
import { Transform, Animator, GameObject, HumanBodyBones, Object, Vector3, Quaternion } from 'UnityEngine';

export default class RideVehicle extends ZepetoScriptBehaviour {
    public vehiclePrefab : GameObject;
    public bodyBone: HumanBodyBones;
 
    private _localCharacter: ZepetoCharacter;
    private _vehicle : GameObject;
    Start() {
 
        ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
            // 找到本地玩家并将其设置为 _localCharacter。
            this._localCharacter = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
            // 获取 _localCharacter 的动画组件。
            const animator: Animator = this._localCharacter.ZepetoAnimator;
            // 获取附加对象的骨骼位置。
            const bone: Transform = animator.GetBoneTransform(this.bodyBone);
            // 创建车辆预制件。
            this._vehicle = Object.Instantiate(this.vehiclePrefab, bone) as GameObject;
             
            // 设置车辆变换
            this._vehicle.transform.localPosition = new Vector3(0.5, 0,0);
            this._vehicle.transform.localRotation = Quaternion.Euler(0, 0,90);
        });
    }
}

```



3\) 完成脚本编写后，将脚本添加到场景中的GameObject。

4\) 在检查器中，分配车辆的预制资源和要附加的身体骨骼。

5\) 点击播放按钮，您将看到ZEPETO角色骑在车辆上。

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



:::hint{type="success"}
**👍 提示**

- 您可以通过简单地更改车辆的预制件和角色的动画片段来创建各种车辆。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-7gV7D0SF1m1ik5oLtPDIS-20240904-102415.png)
:::



## 消费物品时大小变化的示例

这是一个有趣的应用示例：实现角色在骑乘车辆时消费物品时大小变化的内容。

1\) 在这个示例中，我们准备了两种类型的预制件，并将消费后增大尺寸的物品的标签设置为“增大”，将减小尺寸的物品的标签设置为“减小”。

2\) 为每个物品添加碰撞体，并确保勾选IsTrigger。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-odhbiMJtZC7hUwf-9KJE7-20240904-102415.png)

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-2oltB2KVfX1-OCZ7j_CTA-20240904-102415.png)



3\) 通过访问项目 > 创建 > ZEPETO > TypeScript 创建一个 TypeScript，并将其重命名为 ChangeObjectSize。

4\) 编写如下所示的示例脚本。

```typescript
import { Collider, GameObject, Vector3 } from 'UnityEngine';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
 
export default class ChangeObjectSize extends ZepetoScriptBehaviour {
 
    public vehicle: GameObject;
 
    Start() {
    }
 
    OnTriggerEnter(collider: Collider) {
        if (collider.tag === "Increase") {
            this.gameObject.transform.localScale += new Vector3(0.1, 0.1, 0.1);
            GameObject.Destroy(collider.gameObject);
        }
 
        if (collider.tag === "Decrease") {
            const decreaseAmount = new Vector3(0.1, 0.1, 0.1);
            const newScale = this.gameObject.transform.localScale - decreaseAmount;
            GameObject.Destroy(collider.gameObject);
             
            // 异常处理：确保缩放不小于 (1, 1, 1)
            if (newScale.x >= 1 && newScale.y >= 1 && newScale.z >= 1) {
                this.gameObject.transform.localScale = newScale;
                 
            }
        }
    }
}

```



- 代码的关键部分是修改 localScale，以便在 ZEPETO 角色接触每个物品时改变其大小。
  - 可以自由调整数字以改变大小
  - 但要确保处理异常，以防缩放变小于 (1,1,1)。

5\) 此脚本应添加到运行时创建的 ZEPETO 角色上。
因此，请按如下方式修改附加车辆的 RideVehicle 脚本。

```typescript
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoCharacter, ZepetoPlayers } from 'ZEPETO.Character.Controller';
import { Transform, Animator, GameObject, HumanBodyBones, Object, Vector3, Quaternion } from 'UnityEngine';
import ChangeObjectSize from './ChangeObjectSize';
export default class RideVehicle extends ZepetoScriptBehaviour {
    public vehiclePrefab : GameObject;
    public bodyBone: HumanBodyBones;
 
    private _localCharacter: ZepetoCharacter;
    private _vehicle : GameObject;
    Start() {
 
        ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
            // 找到本地玩家并将其设置为 _localCharacter。
            this._localCharacter = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
             
            // 获取 _localCharacter 的动画组件。
            const animator: Animator = this._localCharacter.ZepetoAnimator;
            // 获取附加对象的骨骼位置。
            const bone: Transform = animator.GetBoneTransform(this.bodyBone);
            // 创建车辆预制件。
            this._vehicle = Object.Instantiate(this.vehiclePrefab, bone) as GameObject;
             
            // 设置车辆变换
            this._vehicle.transform.localPosition = new Vector3(0.5, 0,0);
            this._vehicle.transform.localRotation = Quaternion.Euler(0, 0,90);
             
             
            // 将脚本添加到 CharacterController
            const sizeScript = this._localCharacter.gameObject.AddComponent<ChangeObjectSize>();
            // 将车辆添加到脚本的检查器中
            sizeScript.vehicle = this._vehicle;
        });
    }
}
```



- 已添加在运行时添加ChangeObjectSize脚本的代码。

6\) 点击播放按钮，您会看到每次消耗物品时角色的大小都会变化。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N--lB-ZVtXJPDluuvzhI0Eo-20240904-102416.gif)



## 与车辆互动

通过应用互动指南，可以在与车辆互动后，在所需位置实现登车。

:::hint{type="info"}
**📘&#x20;**&#x8BF7;参考以下指南。\[[与物体互动](docId:_hOHoqb4fwAl8VVLlryXW)]
:::



### 设置

1\) 按照交互指南，在您的车辆预制件上设置 DockPoint 对象和 UI 预制件。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-gDbiGYX_71u17t1MoEW2M-20240904-102415.png "带有 Dock Point 设置的 Slime 预制件")



2\) 在 Unity 编辑器中，确保顶部的变换小工具切换按钮设置为本地，并旋转它，使 Z 轴（蓝色箭头）指向对象外部。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-0_85030ExbJJtqqPxSRZ0-20240904-102415.png "带有碰撞器设置的 Slime 预制件")



3\) 添加一个碰撞器组件并检查 isTrigger 选项。

4\) 调整碰撞器的大小，以便玩家可以与对象进行交互的范围。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-9YZO_8JW0wsNEB8Jy8xeU-20240904-102415.png "完成的 UI 按钮设置")



5\) 创建一个空对象作为 DockPoint 的子对象，方法是转到层级 > 创建空对象，并将其重命名为 IconPos。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-T26BGfsHwhLQ5na3HZ-BL-20240904-102415.png)



6\) 以与对象交互指南相同的方式设置 PrefIconCanvas，然后将其制作成预制件。

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



7\) 额外步骤：创建一个下车按钮

- 通过转到层级 > UI > 按钮，创建一个作为车辆对象的子按钮，并将其重命名为下车按钮。



### 为车辆添加登车动画

当角色上车或下车时，他们需要一个自然的动画设置来匹配。

下面的指南将帮助您完成上车或下车所需的动画设置。

请参考关于应用自定义动画的指南，以将坐下动画状态添加到您的自定义动画器中。

:::hint{type="info"}
**📘&#x20;**&#x8BF7;参考以下指南。 \[[应用自定义动画](docId\:GVco8Q0TWGTEBrFodORYe)]
:::



1\) 右键单击添加的动画状态，单击制作过渡以创建从闲置到坐下的过渡，并从坐下到闲置的过渡。选择创建的过渡并取消选中“有退出时间”选项。

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



2\) 在自定义动画器 → 参数 → \[ + ]，添加一个布尔条件并将其重命名为isRiding。

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



3\) 对于从坐下到闲置状态的过渡，单击条件 → \[ + ] 并添加isRiding条件，选项设置为false。

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



4\) 对于从闲置到坐下状态的过渡，单击条件 → \[ + ] 并添加isRiding条件，选项设置为true。

![](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-7QMiNzrOJIVZD8oMv63kA-20240904-102415.png)



### 脚本

编写InteractionIcon.ts脚本，与交互指南中的脚本相同，当进入车辆预制件的触发区域时显示交互UI。

将编写的 InteractionIcon ZEPETOScript 添加到 DockPoint 对象，并在检查器中分配 Pref Icon Canvas 和 Icon Position。

请参阅下面的 RideVehicleExample 以获取有关如何在与 UI 交互后登上车辆的示例代码。

```typescript
import { Animator, GameObject, HumanBodyBones, Physics, Transform, Vector3, WaitForEndOfFrame } from 'UnityEngine';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoPlayers, ZepetoCharacter, CharacterStateMachine } from "ZEPETO.Character.Controller";
import { Button } from 'UnityEngine.UI';
import InteractionIcon from './InteractionIcon';
 
export default class RideVehicleExample extends ZepetoScriptBehaviour {
 
    @SerializeField() private bodyBone: HumanBodyBones;
 
    public isRiding: bool;
    public getOffBtn: Button;
    public vehicleObj: GameObject;
 
    private _interactionIcon: InteractionIcon;
    private _localCharacter: ZepetoCharacter;
    private _playerSittingPosition: Vector3;
    private _animator: Animator;
 
     
    private Start() {
        ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
            this._localCharacter = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
            this._animator = this._localCharacter.ZepetoAnimator;
 
        });
 
        this._interactionIcon = this.transform.GetComponent<InteractionIcon>();
        this._interactionIcon.OnClickEvent.AddListener(() => {
            // 当交互图标被点击时，隐藏图标并执行交互
            this._interactionIcon.HideIcon();
            this.DoInteraction();
        });
 
        // 设置 getOffBtn（退出车辆的按钮）的点击事件。
        this.getOffBtn.onClick.AddListener(() => {
            if(this.isRiding) {
                this.StopInteraction();
                this._interactionIcon.ShowIcon();
            }
        });
 
        this.getOffBtn.gameObject.SetActive(false);
 
    }
 
    //执行骑乘逻辑的方法
    private DoInteraction() {
 
        // 设置 Animator 的过渡值以切换当前动画状态到坐着状态。
        this._animator.SetBool("isRiding", true);
        this.StartCoroutine(this.SnapBone());
 
        // 为了修复当前动画，停用 Animator。
        this._localCharacter.StateMachine.constraintStateAnimation = true; 
 
        // 将车辆对象的父级设置为本地角色。
        this.vehicleObj.transform.parent = this._localCharacter.transform;
        // 激活 getOffButton。
        this.getOffBtn.gameObject.SetActive(true);
        this.isRiding = true;
 
    }
 
    private StopInteraction() {
        // 设置 Animator 的过渡值以切换动画状态到闲置。
        this._animator.SetBool("isRiding", false);
        // 再次激活 Animator。
        this._localCharacter.StateMachine.constraintStateAnimation = false;
        this.isRiding = false;
        // 将车辆对象的父级设置为 null。
        this.vehicleObj.transform.parent = null;
        // 停用 getOffButton。
        this.getOffBtn.gameObject.SetActive(false);
    }
 
    // 用于将骨骼固定到车辆的协程
    private *SnapBone() {
        const animator: Animator = this._localCharacter.ZepetoAnimator;
        const bone: Transform = animator.GetBoneTransform(this.bodyBone);
 
        let idx = 0;
        while (true) {
            // 计算骨骼与角色位置之间的距离。
            const distance = Vector3.op_Subtraction(bone.position, this._localCharacter.transform.position);
            const newPos: Vector3 = Vector3.op_Subtraction(this.transform.position, distance);
 
             // 更新玩家坐着的位置并设置角色的位置和旋转。
            this._playerSittingPosition = newPos;
            this._localCharacter.transform.position = this._playerSittingPosition;
            this._localCharacter.transform.rotation = this.transform.rotation;
       
 
             yield new WaitForEndOfFrame();
             idx++;
 
            // 在 5 帧动画期间校准位置。
            if (idx > 5) {
                return;
            }
        }
    }
}
```



- 脚本的流程如下：
  - 开始()
    - 当点击图标时，this.\_interactionIcon 被停用，并调用 DoInteraction() 函数。
  - DoInteraction()
    - 设置动画师的过渡值以播放坐下动画。
    - 启动 Snap Bone() 协程，将 ZEPETO 角色的 bodyBone 附加到 targetTransform。
    - 使用函数 this.\_localCharacter.StateMachine.constraintStateAnimation = true; 来禁用当前动画师的过渡，锁定当前播放的动画。
    - 车辆对象的父级更改为 ZEPETO 角色。
    - 右上角的下车按钮被激活。
  - 停止交互()
    - 设置动画师的过渡值以播放闲置动画。
    - 重新启用动画师过渡，以便根据情况播放适当的动画。
    - 将车辆对象的父级设置为 null。
    - 停用下车按钮。
  - this.getOffBtn.onClick
    - 调用 StopInteraction() 函数。
    - this.\_interactionIcon 被激活。



将编写的 RideVehicleExample ZEPETOScript 添加到 DockPoint，并在检查器中分配下车按钮和车辆预制件。

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



\[▶︎(播放)] 按下播放按钮尝试登上车辆。

![结果屏幕](https://api.archbee.com/api/optimize/fCt3n1oCa8rgNJ8fw9I2N-MVI0xatAB9KLTTaLJkpaw-20240904-102415.gif "结果屏幕")



:::hint{type="success"}
**👍 提示**

- 上述示例是一个不考虑多人同步的内容示例。
- 要实现多人同步，您需要同步信息，例如每个玩家骑乘的车辆，以及车辆的大小和位置，作为房间状态。
:::

