Interagir avec un objet
Implémentez un bouton d'interaction qui apparaît lorsqu'un personnage ZEPETO s'approche d'un objet.

ÉTAPE 1 : Configuration de l'environnement
- Vous pouvez télécharger les ressources d'animation et de bouton utilisées dans l'exemple d'interaction et le guide à partir du lien suivant.
📘 Exemple d'interaction ZEPETO Module d'interaction Zepeto
- Implémentez le code de création de personnage ZEPETO dans la scène par défaut.
📘 Veuillez vous référer au guide suivant. [ZEPETO Player]
ÉTAPE 2 : Configuration de l'objet
Définissez l'objet pour interagir avec le personnage ZEPETO.
1) Placez l'objet avec lequel le personnage ZEPETO interagira.
2) Créez une hiérarchie > Créer un objet vide et renommez-le DockPoint.
- C'est le point avec lequel le personnage ZEPETO interagira. Ajustez la position de l'objet.

- Vérifiez que le bouton de basculement du gizmo de transformation en haut de l'éditeur Unity est sur Local et faites pivoter l'axe Z (flèche bleue) vers l'extérieur de l'objet.

- Après avoir ajouté le composant Collider, vérifiez le isTrigger.
- Ajustez la taille du Collider pour correspondre à la portée où le joueur peut interagir avec l'objet.

3) Créez Hiérarchie > Créer un objet vide en tant qu'enfant de DockPoint et renommez-le IconPos.

ÉTAPE 3 : Configuration de l'interface utilisateur
1) Créer Hiérarchie > UI > Canvas en tant qu'enfant de l'objet avec lequel le personnage ZEPETO interagira et le renommer PrefIconCanvas.
- Définir le mode de rendu sur Espace Monde.
- Définir la largeur et la hauteur à 1 respectivement.
- Décochez l'option Ignorer les graphiques inversés sur le composant Graphic Raycaster.

2) Créer Hiérarchie > UI > Bouton en tant qu'enfant de PrefIconCanvas.

3) Une fois la configuration terminée, faites-en un Prefab et supprimez le PrefIconCanvas restant dans la hiérarchie.

ÉTAPE 4 : Écriture d'un script
ÉTAPE 4 - 1 : InteractionIcon
1) Créer un projet > Créer > ZEPETO > TypeScript et le renommer InteractionIcon.
2) Écrire un script d'exemple comme ci-dessous.
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { Camera, Canvas, Collider, GameObject, Transform, Object } from 'UnityEngine';
import { Button } from 'UnityEngine.UI';
import { UnityEvent } from 'UnityEngine.Events';
import { ZepetoPlayers } from 'ZEPETO.Character.Controller';
export default class InteractionIcon extends ZepetoScriptBehaviour {
// Icône
@Header("[Icône]")
@SerializeField() private prefIconCanvas: GameObject;
@SerializeField() private iconPosition: Transform;
// Événement Unity
@Header("[Événement Unity]")
public OnClickEvent: UnityEvent;
public OnTriggerEnterEvent: UnityEvent;
public OnTriggerExitEvent: UnityEvent;
private _button: Button;
private _canvas: Canvas;
private _cachedWorldCamera: Camera;
private _isIconActive: boolean = false;
private _isDoneFirstTrig: boolean = false;
private Update() {
if (this._isDoneFirstTrig && this._canvas?.gameObject.activeSelf) {
this.UpdateIconRotation();
}
}
private OnTriggerEnter(coll: Collider) {
if (coll != ZepetoPlayers.instance.LocalPlayer?.zepetoPlayer?.character.GetComponent<Collider>()) {
return;
}
this.ShowIcon();
this.OnTriggerEnterEvent?.Invoke();
}
private OnTriggerExit(coll: Collider) {
if (coll != ZepetoPlayers.instance.LocalPlayer?.zepetoPlayer?.character.GetComponent<Collider>()) {
return;
}
this.HideIcon();
this.OnTriggerExitEvent?.Invoke();
}
public ShowIcon(){
if (!this._isDoneFirstTrig) {
this.CreateIcon();
this._isDoneFirstTrig = true;
}
else {
this._canvas.gameObject.SetActive(true);
}
this._isIconActive = true;
}
public HideIcon() {
this._canvas?.gameObject.SetActive(false);
this._isIconActive = false;
}
private CreateIcon() {
if (this._canvas === undefined) {
const canvas = GameObject.Instantiate(this.prefIconCanvas, this.iconPosition) as GameObject;
this._canvas = canvas.GetComponent<Canvas>();
this._button = canvas.GetComponentInChildren<Button>();
this._canvas.transform.position = this.iconPosition.position;
}
this._cachedWorldCamera = Object.FindObjectOfType<Camera>();
this._canvas.worldCamera = this._cachedWorldCamera;
this._button.onClick.AddListener(() => {
this.OnClickIcon();
});
}
private UpdateIconRotation() {
this._canvas.transform.LookAt(this._cachedWorldCamera.transform);
}
private OnClickIcon() {
this.OnClickEvent?.Invoke();
}
}- Le flux du script est le suivant :
- Mettre à jour()
- Appeler la fonction personnalisée UpdateIconRotation() pour faire pivoter le canevas de l'icône afin de correspondre à la rotation de la caméra.
- OnTriggerEnter(), OnTriggerExit()
- Lorsque vous entrez dans la zone du Collider et détectez un déclencheur, appelez la fonction personnalisée ShowIcon() pour activer l'icône.
- Lorsque vous sortez de la zone du collider, appelez la fonction personnalisée HideIcon() pour désactiver l'icône.
3) Après avoir terminé la création du script, ajoutez le script à l'objet DockPoint.
4) Assignez le Canvas d'icône de préference, la position de l'icône depuis l'inspecteur.

ÉTAPE 4 - 2 : Interaction par geste
1) Créer Projet > Créer > ZEPETO > TypeScript et renommez-le en Interaction par geste.
2) Écrivez un script d'exemple comme ci-dessous.
import { AnimationClip, Animator, HumanBodyBones, Physics, Transform, Vector3, WaitForEndOfFrame} from 'UnityEngine';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script';
import { ZepetoPlayers, ZepetoCharacter } from "ZEPETO.Character.Controller";
import InteractionIcon from './InteractionIcon';
export default class GestureInteraction extends ZepetoScriptBehaviour {
@SerializeField() private animationClip: AnimationClip;
@SerializeField() private isSnapBone: boolean = true;
@SerializeField() private bodyBone: HumanBodyBones;
@SerializeField() private allowOverlap: boolean = false;
private _interactionIcon: InteractionIcon;
private _isFirst: boolean = true;
private _localCharacter: ZepetoCharacter;
private _outPosition: Vector3;
private _playerGesturePosition: Vector3;
private Start() {
this._interactionIcon = this.transform.GetComponent<InteractionIcon>();
ZepetoPlayers.instance.OnAddedLocalPlayer.AddListener(() => {
this._localCharacter = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character;
});
this._interactionIcon.OnClickEvent.AddListener(()=> {
// Lorsque l'icône d'interaction est cliquée
this._interactionIcon.HideIcon();
this.DoInteraction();
});
}
private DoInteraction() {
this._outPosition = this.transform.position;
if (this.isSnapBone) {
// Est-ce que l'emplacement est vide
if (this.allowOverlap || this.FindOtherPlayerNum() < 1) {
this._localCharacter.SetGesture(this.animationClip);
this.StartCoroutine(this.SnapBone());
this.StartCoroutine(this.WaitForExit());
} else {
// Les sièges sont pleins.
this._interactionIcon.ShowIcon();
}
} else {
this._localCharacter.SetGesture(this.animationClip);
this.StartCoroutine(this.WaitForExit());
}
}
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._playerGesturePosition = newPos;
this._localCharacter.transform.position = this._playerGesturePosition;
this._localCharacter.transform.rotation = this.transform.rotation;
yield new WaitForEndOfFrame();
idx++;
// Calibrer la position pendant 5 images d'animation.
if (idx > 5) {
return;
}
}
}
// La méthode exacte doit passer par le code serveur,
// mais elle est calculée par le client local pour l'optimisation du serveur.
private FindOtherPlayerNum() {
const hitInfos = Physics.OverlapSphere(this.transform.position, 0.1);
let playerNum = 0;
if (hitInfos.length > 0) {
hitInfos.forEach((hitInfo) => {
if (hitInfo.transform.GetComponent<ZepetoCharacter>()) {
playerNum ++;
}
});
}
return playerNum;
}
private *WaitForExit() {
if (this._localCharacter) {
while (true) {
if (this._localCharacter.tryJump || this._localCharacter.tryMove) {
this._localCharacter.CancelGesture();
this.transform.position = this._outPosition;
this._interactionIcon.ShowIcon();
break;
} else if(this.isSnapBone && this._playerGesturePosition != this._localCharacter.transform.position){
this._interactionIcon.ShowIcon();
break;
}
yield;
}
}
}
}- Le flux du script est le suivant :
- Démarrer()
- Lorsque l'icône est cliquée, elle se désactive et appelle la fonction personnalisée DoInteraction().
- DoInteraction()
- Si isSnapBone est coché,
- Si le siège est vide (allowOverlap est coché, ou la valeur de retour de la fonction personnalisée FindOtherPlayerNum() est inférieure à 1)
- Prenez le geste assigné à l'animationClip.
- Démarrez la Coroutine SnapBone() et attachez le bodyBone du personnage ZEPETO au targetTranform.
- Démarrez la Coroutine WaitForExit().
- Lorsque le personnage ZEPETO saute ou se déplace, ou sort de la zone du collider, annulez le geste et activez l'icône.
- Activez l'icône lorsque la capacité d'accueil est pleine.
- Si isSnapBone n'est pas coché,
- Prenez le geste assigné à l'animationClip.
- Démarrez la Coroutine WaitForExit().
3) Après avoir terminé la création du script, ajoutez le script à l'objet DockPoint.
4) Assignez le clip d'animation, Is Snap Bone, Body Bone et Allow Overlap dans l'inspecteur.
- Assignez le clip d'animation. Ce sont des gestes à adopter lors de l'interaction.
- Vérifiez Is SnapBone. Assurez-vous que la partie assignée au corps est positionnée comme un DockPoint.
- Définissez Body Bone sur Hips. Assurez-vous que la hanche est positionnée dans le DockPoint car cela sera un geste assis.
- Allow Overlap vous permet de déterminer si plusieurs personnes peuvent s'asseoir sur un seul siège.

ÉTAPE 5 : Jouer
Le bouton apparaîtra lorsque le personnage ZEPETO s'approchera de l'objet, et disparaîtra lorsqu'il s'en éloignera.
Si le geste que vous avez défini se joue lorsque vous vous approchez et interagissez avec le bouton, c'est un succès.
En plus des gestes, divers événements peuvent être mis en œuvre pour se produire après l'interaction.
Voici un exemple de mise en œuvre d'un événement qui crée un élément après une interaction.
📘 Exemple de Zepeto World - Chapitre 3 Exemple d'Interaction https://github.com/naverz/zepeto-world-sample/tree/main/Assets/Chapter3