CREATE YOUR WORLD
Interacting with Objects
Interacting with an object
16min
implement an interaction button that appears when a zepeto character approaches an object step 1 environment set up you can download the animation and button resources used in the interaction sample and guide from the following link 📘 zepeto interaction sample zepeto interaction module https //github com/naverz/zepeto multiplay example/tree/main/assets/zepeto%20interaction%20module implement the zepeto character creation code in scene as a default 📘 please refer to the following guide \[ zepeto player docid\ i xlh9pm 9fxgnw50gf9n ] step 2 setting the object set the object to interact with the zepeto character 1\) place the object that the zepeto character will interact with 2\) create a hierarchy > create empty object and rename it dockpoint this is the point the zepeto character will interact with adjust the position of the object check that the transform gizmo toggle button at the top of the unity editor is local and rotate the z axis(blue arrow) to the outside of the object after adding the collider component, check the istrigger adjust the size of the collider to match the range where the player can interact with the object 3\) create hierarchy > create empty object as a child of dockpoint and rename it iconpos step 3 setting the ui 1\) create hierachy > ui > canvas as the child of the object that the zepeto character will interact with and rename it preficoncanvas set render mode to world space set width and height to 1 respectively uncheck the ignore reversed graphics option on the graphic raycaster component 2\) create hierachy > ui > button as a child of preficoncanvas 3\) once the setting is complete, make it prefab and delete the remaining preficoncanvas in the hierarchy step 4 writing a script step 4 1 interactionicon 1\) create project > create > zepeto > typescript and rename it interactionicon 2\) write a sample script like below 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 { // icon @header("\[icon]") @serializefield() private preficoncanvas gameobject; @serializefield() private iconposition transform; // unity event @header("\[unity event]") 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(); } } the flow of the script is as follows update() call the updateiconrotation() custom function to rotate the icon canvas to match the camera rotation ontriggerenter(), ontriggerexit() when you enter the collider area and detect a trigger, call the showicon() custom function to activate the icon when you go out of the collider area, call the hideicon() custom function to disable the icon 3\) after completing the script creation, add the script to the dockpoint object 4\) assign pref icon canvas, icon position from the inspector step 4 2 gestureinteraction 1\) create project > create > zepeto > typescript and rename it to gestureinteraction 2\) write a sample script like below 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(()=> { // when onclick interaction icon this interactionicon hideicon(); this dointeraction(); }); } private dointeraction() { this outposition = this transform position; if (this issnapbone) { // is place empty if (this allowoverlap || this findotherplayernum() < 1) { this localcharacter setgesture(this animationclip); this startcoroutine(this snapbone()); this startcoroutine(this waitforexit()); } else { // the seats are full 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++; // calibrate position during 5 frames of animation if (idx > 5) { return; } } } // the exact method must go through the server code, // but it is calculated by the local client for server optimization 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; } } } } the flow of the script is as follows start() when the icon is clicked, it deactivates and calls the dointeraction() custom function dointeraction() if issnapbone is checked, if the seat is empty (allowoverlap is checked, or findotherplayernum() custom function return value is less than 1) take the gesture assigned to the animationclip start the snapbone() coroutine and attach the bodybone of the zepeto character to the targettranform start waitforexit() coroutine when the zepeto character jumps or moves, or goes out of the collider area, cancel the gesture and activate the icon activate the icon when the seating capacity is full if issnapbone is not checked, take the gesture assigned to the animationclip start waitforexit() coroutine findotherplayernum() finds the zepeto character that exists in the location of the object where the script is attached, and returns how many there are you can check if the seat is empty or not 3\) after completing the script creation, add the script to the dockpoint object 4\) assign the animation clip, is snap bone, body bone, and allow overlap in the inspector assign the animation clip these are gestures to take with interaction checks is snapbone ensure that the part assigned to the body bone is positioned as a dockpoint set body bone to hips make sure that the hip is positioned in the dockpoint because it will be a sitting gesture allow overlap allows you to determine whether multiple people can sit in a single seat step 5 play the button will appear when the zepeto character approaches the object, and disappear when it moves away if the gesture you set plays when approaching and interacting with the button, it's a success in addition to gestures, various events can be implemented to occur after interaction the following is an example of implementing an event that creates an item after an interaction https //www youtube com/watch?v=ooazdb4 lgo https //www youtube com/watch?v=ooazdb4 lgo 📘 zepeto world sample chapter 3 interaction sample https //github com/naverz/zepeto world sample/tree/main/assets/chapter3 https //github com/naverz/zepeto world sample/tree/main/assets/chapter3