Studio GuideWorld SDK Guide
Log In

Applying Vehicles to ZEPETO Characters

By applying the guide on attaching objects to ZEPETO characters, you can implement riding vehicles like cars, airplanes, pets, etc., as if the character is riding them.

📘

Please refer to the following guide [Attaching Objects to ZEPETO Characters]


Setting Up the Animator

To change the pose of the ZEPETO character when riding a vehicle as described above, you need to modify the Animator and set it up under ZEPETOPlayers.

To change the pose of the ZEPETO character when riding a vehicle as described above, you need to modify the Animator and set it up under ZEPETOPlayers.


  1. Create an Animator Controller by going to Project > Create > Animator Controller and rename it to VehicleZepetoAnimator.

  1. In the Animator tab, create an Empty state by selecting Create State > Empty.

  1. Rename it appropriately in the Inspector and assign animation clips to Motion.

  • To use your desired animation clip files, refer to the Custom Animation guide.

📘

Please refer to the following guide [Apply custom animation]


  1. Drag and drop VehicleZepetoAnimator onto ZEPETOPlayers to set it up.

👍

Tips

  • If the camera height needs to change depending on the size of the vehicle, you can preset the Camera LookOffset value in ZEPETOPlayers.

Example Code

  1. Create a TypeScript by going to Project > Create > ZEPETO > TypeScript and rename it to RideVehicle.
  2. Write a sample script as shown below.
    • Note that the Vehicle Transform values in this example should be adjusted to match the Vehicle Prefab when actually applying it.
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(() => {
            // Find the local player and set it to _localCharacter.
            this._localCharacter = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
            // Get the _localCharacter's animator component.
            const animator: Animator = this._localCharacter.ZepetoAnimator;
            // Get the position of the bone to attach the object to.
            const bone: Transform = animator.GetBoneTransform(this.bodyBone);
            // Create the Vehicle prefab.
            this._vehicle = Object.Instantiate(this.vehiclePrefab, bone) as GameObject;
             
            // Set Vehicle Transform
            this._vehicle.transform.localPosition = new Vector3(0.5, 0,0);
            this._vehicle.transform.localRotation = Quaternion.Euler(0, 0,90);
        });
    }
}


  1. After completing script writing, add the script to the GameObject in the Scene.
  2. In the Inspector, assign the vehicle's prefab resources and the Body Bone to be attached.
  3. Click the Play button, and you'll see the ZEPETO character riding on the vehicle.

👍

Tips

  • You can create various vehicles by simply changing the Prefab of the vehicle and the animation clips of the character.

Example of Size Change When Consuming Items

Here is a fun application example: implementing content where the character's size changes when consuming items while riding a vehicle.

  1. In this example, we prepared two types of prefabs and set the Tag of items that increase in size when consumed as "Increase" and items that decrease in size as "Decrease."
  2. Add Colliders to each item and make sure to check IsTrigger.

  1. Create a TypeScript by going to Project > Create > ZEPETO > TypeScript and rename it to ChangeObjectSize.
  2. Write a sample script as shown below.
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);
             
            // Exception Handling: Ensure that the scale does not become smaller than (1, 1, 1)
            if (newScale.x >= 1 && newScale.y >= 1 && newScale.z >= 1) {
                this.gameObject.transform.localScale = newScale;
                 
            }
        }
    }
}


  • The key part of the code is modifying the localScale to change the size of the ZEPETO character when it touches each item.
    • Feel free to adjust the numbers for size change
    • but make sure to handle exceptions to prevent the scale from becoming smaller than (1,1,1).
  1. This script should be added to the ZEPETO character created at runtime.
    Therefore, modify the RideVehicle script that attaches the vehicle as follows.
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(() => {
            // Find the local player and set it to _localCharacter.
            this._localCharacter = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
             
            // Get the _localCharacter's animator component.
            const animator: Animator = this._localCharacter.ZepetoAnimator;
            // Get the position of the bone to attach the object to.
            const bone: Transform = animator.GetBoneTransform(this.bodyBone);
            // Create the Vehicle prefab.
            this._vehicle = Object.Instantiate(this.vehiclePrefab, bone) as GameObject;
             
            // Set Vehicle Transform
            this._vehicle.transform.localPosition = new Vector3(0.5, 0,0);
            this._vehicle.transform.localRotation = Quaternion.Euler(0, 0,90);
             
             
            // Add Script to CharacterController
            const sizeScript = this._localCharacter.gameObject.AddComponent<ChangeObjectSize>();
            // Add the vehicle to the script's inspector
            sizeScript.vehicle = this._vehicle;
        });
    }
}

  • Code to add the ChangeObjectSize script at runtime has been added.
  1. Click the Play button, and you'll see the character's size changing each time it consumes an item.

Interacting with Vehicles

By applying the interaction guide, it is possible to implement boarding a vehicle at a desired location after interacting with it.

📘

Please refer to the following guide. [Interacting with an object]


Setting Up

  1. Set up the DockPoint object and UI prefab on your vehicle prefab in the same way as the interaction guide.
Slime Prefab with Dock Point Set Up

Slime Prefab with Dock Point Set Up


  1. In the Unity editor, make sure the transform gizmo toggle button at the top is set to Local, and rotate it so that the Z-axis (blue arrow) points outward from the object.
Slime Prefab with Collider Set Up

Slime Prefab with Collider Set Up


  1. Add a Collider component and check the isTrigger option.
  2. Adjust the size of the Collider to the range within which the player can interact with the object.
Completed UI Button Setup

Completed UI Button Setup


  1. Create an empty object as a child of DockPoint by going to Hierarchy > Create Empty Object, and rename it to IconPos.

  1. Set up PrefIconCanvas in the same way as the object interaction guide, and then make it into a prefab.

  1. Additional Step: Create a Dismount Button
    • Create a button as a child of the vehicle object by going to Hierarchy > UI > Button, and rename it to Get Off Button.

Adding Boarding Animation to the Vehicle

When a character gets on or off a vehicle, they need a natural animator setup to match.

The guide below will help you through the animator settings required for getting on or off.

Refer to the guide on applying custom animations to add a sitting animation state to your custom animator.

📘

Please refer to the following guide. [Applying Custom Animation]


  1. Right-click on the added animation state, click Make Transition to create a transition from Idle to Sitting and back from Sitting to Idle. Select the created transition and uncheck the Has Exit Time option.

  1. In the custom animator → Parameters → [ + ], add a Bool Condition and rename it to isRiding.

  1. For the transition from Sitting to Idle state, click Conditions → [ + ] and add the isRiding Condition with the option set to false.

  1. For the transition from Idle to Sitting state, click Conditions → [ + ] and add the isRiding Condition with the option set to true.

Script

Write the InteractionIcon.ts script, same as the one in the interaction guide, which displays the interaction UI when entering the trigger area of the vehicle prefab.

Add the written InteractionIcon ZEPETOScript to the DockPoint object, and assign Pref Icon Canvas and Icon Position in the inspector.

Refer to the RideVehicleExample below for sample code on how to board the vehicle after interacting with the UI.

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(() => {
            // When the interaction icon is clicked, hide the icon and perform the interaction
            this._interactionIcon.HideIcon();
            this.DoInteraction();
        });
 
        // Set up the click event for the getOffBtn (button to exit the vehicle).
        this.getOffBtn.onClick.AddListener(() => {
            if(this.isRiding) {
                this.StopInteraction();
                this._interactionIcon.ShowIcon();
            }
        });
 
        this.getOffBtn.gameObject.SetActive(false);
 
    }
 
    //Method for performing the riding logic
    private DoInteraction() {
 
        // Set the Animator's transition value to switch the current animation state to Sitting state.
        this._animator.SetBool("isRiding", true);
        this.StartCoroutine(this.SnapBone());
 
        // To fix current animation, deactivate the Animator.
        this._localCharacter.StateMachine.constraintStateAnimation = true; 
 
        // Set the vehicle object's parent to local character.
        this.vehicleObj.transform.parent = this._localCharacter.transform;
        // Activate the getOffButton.
        this.getOffBtn.gameObject.SetActive(true);
        this.isRiding = true;
 
    }
 
    private StopInteraction() {
        // Set the Animator's transition value to switch the animation state to Idle.
        this._animator.SetBool("isRiding", false);
        // Activate the Animator again.
        this._localCharacter.StateMachine.constraintStateAnimation = false;
        this.isRiding = false;
        // Set the Vehicle Object's parent to null.
        this.vehicleObj.transform.parent = null;
        // Deactivate the getOuntButton.
        this.getOffBtn.gameObject.SetActive(false);
    }
 
    // Coroutine for snapping the bone to the vehicle
    private *SnapBone() {
        const animator: Animator = this._localCharacter.ZepetoAnimator;
        const bone: Transform = animator.GetBoneTransform(this.bodyBone);
 
        let idx = 0;
        while (true) {
            // Calculate the distance between the bone and character position.
            const distance = Vector3.op_Subtraction(bone.position, this._localCharacter.transform.position);
            const newPos: Vector3 = Vector3.op_Subtraction(this.transform.position, distance);
 
             // Update the player sitting position and set the character's position and rotation.
            this._playerSittingPosition = newPos;
            this._localCharacter.transform.position = this._playerSittingPosition;
            this._localCharacter.transform.rotation = this.transform.rotation;
       
 
             yield new WaitForEndOfFrame();
             idx++;
 
            // Calibrate position during 5 frames of animation.
            if (idx > 5) {
                return;
            }
        }
    }
}

  • The flow of the script is as follows:
    • Start()
      • When the icon is clicked, this._interactionIcon is deactivated, and the DoInteraction() function is called.
    • DoInteraction()
      • Set the animator's transition values to play the Sitting animation.
      • Start the Snap Bone() coroutine to attach the ZEPETO character's bodyBone to the targetTransform.
      • Use the function this._localCharacter.StateMachine.constraintStateAnimation = true; to disable the current animator's transition, locking in the currently playing animation.
      • The Parent of the vehicle object changes to the ZEPETO character.
      • The Get Off button in the top right is activated.
    • StopInteraction()
      • Set the animator's transition values to play the Idle animation.
      • Re-enable the animator transition so that the appropriate animation plays according to the situation.
      • Set the Parent of the vehicle object to null.
      • Deactivate the Get Off button.
    • this.getOffBtn.onClick
      • The StopInteraction() function is called.
      • this._interactionIcon is activated.

Add the written RideVehicleExample ZEPETOScript to DockPoint, and assign the Get Off Button and vehicle prefab in the inspector.


[▶︎(play)] Press the play button to try boarding the vehicle.

Resulting screen

Resulting screen


👍

Tips

  • The above sample is an example of content not considered for multiplayer synchronization.
  • To implement multiplayer synchronization, you need to synchronize information such as which vehicle each player is riding, and the size and location of the vehicle, as Room State.