CREATE YOUR WORLD
Gesture
33min
zepetoworldcontent api allows you to set thumbnails for desired gesture/pose categories and enable specific gestures/poses when a thumbnail is clicked to use the zepetoworldcontent api, you must write an import statement as follows import { officialcontenttype, worldservice, zepetoworldcontent, content } from 'zepeto world'; the member variable and function information of the content class containing gesture/pose information are as follows api description public get id() string content unique id public get title() string gesture, pose title text \ the language will automatically translate depending on device language public get thumbnail() unityengine texture2d 2d thumbnail public get animationclip() unityengine animationclip gesture animation clip public get isdownloadedthumbnail() boolean function to determine if you have previously downloaded this thumbnail public get isdownloadedanimation() boolean function to determine if you have previously downloaded this animation clip public downloadanimation($complete system action)\ void an animation clip download function that receives a completed callback with a factor \ if isdownloadedanimation() is false, implement downloadanimation() to be called public downloadthumbnail($complete system action)\ void function to download thumbnails \ if isdownloadedthumbnail() is false, implement downloadthumbnail() to be called officialcontenttype enum type of content (world 1 9 0 and higher) \ gesture = 2 \ pose = 4 \ selfie = 8 \ gesturegreeting = 16 \ gesturepose = 32 \ gestureaffirmation = 64 \ gesturedancing = 128 \ gesturedenial = 256 \ gestureetc = 512 \ all = 14 you can use the existing function, public downloadthumbnail($character zepeto character controller zepetocharacter, $complete system action)\ void , without any issues regarding functionality however, since it no longer accepts zepeto character as an argument, please use the newly modified function public downloadthumbnail($complete system action)\ void instead step 1 set up ui step 1 1 create a gesture button 1\) add hierarchy > ui > canvas and set sort order to 2 to avoid being obscured by other ui 2\) add hierachy > ui > button step 1 2 organize the gesture panel 1\) add hierarchy > create empty object and rename it panelparent 2\) add hierarchy > ui > panel as a child of panelparent 3\) close button after adding ui > button, add onclick event to disable gesture panel 4\) open button please add an onclick event that activates the gesture panel to the open button created above 5\) add an image to serve as the title area 6\) configure a scroll view to display the gesture thumbnail add hierarchy > ui > scroll view check off horizontal and disable the scroll bar image since you will only use vertical scrolls and horizontal scrolls will not be required add grid layout to content in scroll view to align thumbnails in grid pattern add content size fitter to make the size of the object appropriate for the size of the content when you implement a script, you must set the content in scroll view as the parent of the gesture thumbnail (so that the entire area is recognized and scrolled) 7\) configure tabs by gesture type add hierarchy > create empty object as a child oft the panel and rename it to gesturetitle this is the parent object of the toggle button add horizontal layout to align tabs horizontally add the toggle group component 👍 to configure more tabs, add hierarchy > ui > scroll view and check horizontal in the scroll view option 8\) add the text to be used as the toggle button as the child of the gesturetitle, and replace it with all sets the color of the text to gray add the highlighted text that will be displayed when checked as a child of the text set the font content, size, and thickness the same, and set the color to black add the toggle component specify the parent object in the group add highlighted text that you added as a child to graphic check ison only for the all toggle components that will be shown first create both the gesture and pose toggle buttons in the same way step 1 3 make thumbnail prefabs use the method of creating a thumbnail button as a pre fab and then generating it as an instance in a script 1\) add ui > button as a child of content in scroll view and rename it prethumb 2\) please change the name to thumb after adding raw image this image will be a thumbnail adjust the size appropriately 3\) add text set the position to center the bottom of the image adjust the size and thickness of the writing, and add the content size fitter horizontal fit preferred size vertical fit preferred size 4\) if the setting is done, please make it a prefab and put it in the resources folder step 1 4 ui setting guide video https //www youtube com/watch?v=v ias8t8wq0 https //www youtube com/watch?v=v ias8t8wq0 👍 ui size and position values shown in the video are recommended, but you can modify them to the values you want! once the ui setup is complete, proceed to scripting step 2 write a script this script is based on single play step 2 1 thumbnail project > create > zepeto > typescript and rename it thumbnail write a sample script like below this is a script that organizes gesture content information (title, image) into the ui thumbnail import { zepetoscriptbehaviour } from 'zepeto script'; import { content } from 'zepeto world'; import { rawimage, text } from 'unityengine ui'; import { texture2d } from 'unityengine'; export default class thumbnail extends zepetoscriptbehaviour { @hideininspector() public content content; start() { this getcomponentinchildren\<text>() text = this content title; this getcomponentinchildren\<rawimage>() texture = this content thumbnail as texture2d; } } after creating the script, open the prethumb prefab and add the script step 2 2 gestureloader create a hierarchy > create empty object and rename it to gestureloader create a project > create > zepeto > typescript and rename it to gestureloader write a sample script like below gestureloader import { zepetoscriptbehaviour } from 'zepeto script'; import { localplayer, spawninfo, zepetocharacter, zepetoplayers } from 'zepeto character controller'; import { officialcontenttype, worldservice, zepetoworldcontent, content } from 'zepeto world'; import { rawimage, text, button } from 'unityengine ui'; import { gameobject, texture2d, transform, waituntil } from 'unityengine'; import thumbnail from ' /thumbnail'; export default class gestureloader extends zepetoscriptbehaviour { @hideininspector() public contents content\[] = \[]; @hideininspector() public thumbnails gameobject\[] = \[]; @serializefield() private count number = 50; @serializefield() private contentsparent transform; @serializefield() private prefthumb gameobject; private mycharacter zepetocharacter; start() { // creating a character zepetoplayers instance createplayerwithuserid(worldservice userid, new spawninfo(), true); zepetoplayers instance onaddedlocalplayer addlistener(() => { this mycharacter = zepetoplayers instance localplayer zepetoplayer character; // in order to take a thumbnail with my character, you need to request the content after the character is created this contentrequest(); }); } // 1 receive content from the server private contentrequest() { // all type request zepetoworldcontent requestofficialcontentlist(officialcontenttype all, contents => { this contents = contents; for (let i = 0; i < this count; i++) { if (!this contents\[i] isdownloadedthumbnail) { // take a thumbnail photo using my character this contents\[i] downloadthumbnail(() =>{ this createthumbnailobjcet(this contents\[i]); }); } else { this createthumbnailobjcet(this contents\[i]); } } }); } // 2 creating thumbnail objects private createthumbnailobjcet(content content) { const newthumb gameobject = gameobject instantiate(this prefthumb, this contentsparent) as gameobject; newthumb getcomponent\<thumbnail>() content = content; // button listener for each thumbnail newthumb getcomponent\<button>() onclick addlistener(() => { this loadanimation(content); }); this thumbnails push(newthumb); } // 3 loading animation private loadanimation(content content) { // verify animation load if (!content isdownloadedanimation) { // if the animation has not been downloaded, download it content downloadanimation(() => { // play animation clip this mycharacter setgesture(content animationclip); }); } else { this mycharacter setgesture(content animationclip); } } } count is the maximum number of gestures to download on each tab if you set it to a number larger than 100, there may be errors during the thumbnail download process, so please set it only as much as you need the script flows as follows 1\) invoke the contentsrequest() custom function for generating thumbnails after loading the zepeto character the contentsrequest() function receives content information by separating gestures and poses, respectively if there is an existing thumbnail, it is skipped; otherwise, the thumbnail is retrieved the retrieved thumbnail data is stored in respective lists 2\) the createthumbnailobject() custom function is then called the createthumbnailobject() function takes information from the thumbnail list to create a thumbnail button prefab instance in the thumbnail script within the prefab, set the thumbnail information of the content to the thumbnail button image, and set the title information of the content to the thumbnail button text apply the generated thumbnail button to the ui panel click the thumbnail button to call the loadanimation custom function and play the corresponding gesture using the setgesture() function assign content parent, thumbnail prefab, and count to the inspector after completing the script the entry in contents parent is the content in scroll view step 2 3 uicontroller create a hierarchy > create empty object and rename it to uicontoller create project > create > zepeto > typescript and rename it to uicontoller write a sample script like below uicontroller import { zepetoscriptbehaviour } from 'zepeto script'; import { button, rawimage, text, toggle } from 'unityengine ui'; import { localplayer, zepetocharacter, zepetoplayers, zepetoscreentouchpad } from 'zepeto character controller'; import { officialcontenttype, content } from 'zepeto world'; import { object, gameobject, transform } from 'unityengine'; import gestureloader from ' /gestureloader'; import thumbnail from ' /thumbnail'; export default class uicontroller extends zepetoscriptbehaviour { @serializefield() private closebutton button; @serializefield() private typetogglegroup toggle\[]; private gesturelodaer gestureloader; private mycharacter zepetocharacter; start() { this gesturelodaer = object findobjectoftype\<gestureloader>(); zepetoplayers instance onaddedlocalplayer addlistener(() => { this mycharacter = zepetoplayers instance localplayer zepetoplayer character; // if click the touchpad, cancel the gesture object findobjectoftype\<zepetoscreentouchpad>() onpointerdownevent addlistener(() => { this stopgesture(); }); // if click the close button, cancel the gesture this closebutton onclick addlistener(() => { this stopgesture(); }); }); // ui listener this typetogglegroup\[0] onvaluechanged addlistener(() => { this setcategoryui(officialcontenttype all); }); this typetogglegroup\[1] onvaluechanged addlistener(() => { this setcategoryui(officialcontenttype gesture); }); this typetogglegroup\[2] onvaluechanged addlistener(() => { this setcategoryui(officialcontenttype pose); }); } // category toggle ui set private setcategoryui(category officialcontenttype) { if (category == officialcontenttype all) { this gesturelodaer thumbnails foreach((obj) => { obj setactive(true); }); } else { for (let i = 0; i < this gesturelodaer thumbnails length; i++) { const content = this gesturelodaer thumbnails\[i] getcomponent\<thumbnail>() content; if (content keywords includes(category)) { this gesturelodaer thumbnails\[i] setactive(true); } else { this gesturelodaer thumbnails\[i] setactive(false); } } } } private stopgesture() { this mycharacter cancelgesture(); } } the script flows as follows touch the touchpad or close button to cancel the play using the cancelgesture() function tap the tab (toggle button) to invoke the setcategoryui() custom function the setcategoryui() function uses the gesture content information in each thumbnail to set it for each corresponding category enable if it's an applicable type, and disable if not after completing the scripting, assign close button, typetogglegroup to the inspector the entry to the type toggle group is the toggle that is a child of the toggle group in the gesture panel step 3 run ❗️ caution before playing, disable panelparent so that only the open button can be seen when playing step 4 synchronize multi play gestures in the case of multi play, a synchronization code must be added that receives the gesture information value taken by a particular player and applies it to all players accessing the room the key is to send and receive a room message between the server and the client about which player made which gesture step 4 1 client code thumbnail multiplay write the same script as the one implemented in the single play client code thumbnail multiplay import { zepetoscriptbehaviour } from 'zepeto script'; import { content } from 'zepeto world'; import { rawimage, text } from 'unityengine ui'; import { texture2d } from 'unityengine'; export default class thumbnail extends zepetoscriptbehaviour { @hideininspector() public content content; start() { this getcomponentinchildren\<text>() text = this content title; this getcomponentinchildren\<rawimage>() texture = this content thumbnail as texture2d; } } gestureloader multiplay by default, scripts implemented in single play client code are written the same additionally, the client declares the interface to contain the playergestureinfo when sending your information to the server see sendmygesture() custom function when your player presses the thumbnail to make a gesture, send the gesture id to the server using room send() when you cancel a gesture, process it to send the information that you canceled when receiving gesture information from another client from the server "onchangegesture" room message is sent to this room addmessagehandler() within start() synchronization is achieved by having the session id and gesture id in the "onchangegesture" message and making the appropriate player play the gesture import { zepetoscriptbehaviour } from 'zepeto script'; import { localplayer, spawninfo, zepetocharacter, zepetoplayers } from 'zepeto character controller'; import { officialcontenttype, worldservice, zepetoworldcontent, content, zepetoworldmultiplay } from 'zepeto world'; import { rawimage, text, button } from 'unityengine ui'; import { gameobject, texture2d, transform, waituntil } from 'unityengine'; import thumbnail from ' /thumbnail'; import { room, roomdata } from 'zepeto multiplay'; interface playergestureinfo { sessionid string, gestureid string } const cancelmotion = "" as const; export default class gestureloadermultiplay extends zepetoscriptbehaviour { public multiplay zepetoworldmultiplay; @hideininspector() public contents content\[] = \[]; @hideininspector() public thumbnails gameobject\[] = \[]; @serializefield() private count number = 50; @serializefield() private contentsparent transform; @serializefield() private prefthumb gameobject; private mycharacter zepetocharacter; private room room; private contentsmap map\<string, content> = new map\<string, content>(); start() { // creating a character zepetoplayers instance onaddedlocalplayer addlistener(() => { this mycharacter = zepetoplayers instance localplayer zepetoplayer character; // in order to take a thumbnail with my character, you need to request the content after the character is created this contentrequest(); }); // for multiplay this multiplay roomcreated += (room room) => { this room = room; // receive user's gesture information from the server this room addmessagehandler("onchangegesture", (message playergestureinfo) => { let playergestureinfo playergestureinfo = { sessionid message sessionid, gestureid message gestureid }; this loadanimation(playergestureinfo); }); }; } // 1 receive content from the server private contentrequest(){ // all type request zepetoworldcontent requestofficialcontentlist(officialcontenttype all, contents => { this contents = contents; for (let i = 0; i < this count; i++) { if (!this contents\[i] isdownloadedthumbnail) { // take a thumbnail photo using my character this contents\[i] downloadthumbnail(() =>{ this createthumbnailobjcet(this contents\[i]); }); } else { this createthumbnailobjcet(this contents\[i]); } } }); } // 2 creating thumbnail objects private createthumbnailobjcet(content content) { const newthumb gameobject = gameobject instantiate(this prefthumb, this contentsparent) as gameobject; newthumb getcomponent\<thumbnail>() content = content; // create a dictionary to find content with a content id this contentsmap set(content id, content); // button listener for each thumbnail newthumb getcomponent\<button>() onclick addlistener(() => { this sendmygesture(content id); }); // thimnail list this thumbnails push(newthumb); } // for multiplay // send clicked gesture information to the server public sendmygesture(gestureid) { const data = new roomdata(); data add("gestureid", gestureid); this room send("onchangegesture", data getobject()); } // 3 loading animation private loadanimation(playergestureinfo playergestureinfo){ if (!zepetoplayers instance hasplayer(playergestureinfo sessionid)) { console log("player does not exist"); return; } const zepetoplayer = zepetoplayers instance getplayer(playergestureinfo sessionid) character; if (playergestureinfo gestureid == cancelmotion) { zepetoplayer cancelgesture(); return; } else if(!this contentsmap has(playergestureinfo gestureid)) { console log("resource not yet loaded"); return; } const content = this contentsmap get(playergestureinfo gestureid); // verify animation load if (!content isdownloadedanimation) { // if the animation has not been downloaded, download it content downloadanimation(() => { // play animation clip zepetoplayer setgesture(content animationclip); }); } else { zepetoplayer setgesture(content animationclip); } } } after completing the scripting, the inspector will assign an additional object to multiplay with the zepeto world multiplay component uicontroller multiplay by default, scripts implemented in single play client code are written the same the difference from single play client code is the stopgesture() custom function invoke a sendmygesture() custom function within the gestureloadermultiplay process to send information that the gesture has been canceled import { zepetoscriptbehaviour } from 'zepeto script'; import { button, rawimage, text, toggle } from 'unityengine ui'; import { localplayer, zepetocharacter, zepetoplayers, zepetoscreentouchpad } from 'zepeto character controller'; import { officialcontenttype, content } from 'zepeto world'; import { object, gameobject, transform } from 'unityengine'; import gestureloadermultiplay from ' /gestureloadermultiplay'; import thumbnail from ' /thumbnail'; const cancelmotion = "" as const; export default class uicontroller extends zepetoscriptbehaviour { @serializefield() private closebutton button; @serializefield() private typetogglegroup toggle\[]; private gesturelodaer gestureloadermultiplay; private mycharacter zepetocharacter; start() { this gesturelodaer = object findobjectoftype\<gestureloadermultiplay>(); zepetoplayers instance onaddedlocalplayer addlistener(() => { this mycharacter = zepetoplayers instance localplayer zepetoplayer character; // if click the touchpad, cancel the gesture object findobjectoftype\<zepetoscreentouchpad>() onpointerdownevent addlistener(() => { this stopgesture(); }); // if click the close button, cancel the gesture this closebutton onclick addlistener(() => { this stopgesture(); }); }); // ui listener this typetogglegroup\[0] onvaluechanged addlistener(() => { this setcategoryui(officialcontenttype all); }); this typetogglegroup\[1] onvaluechanged addlistener(() => { this setcategoryui(officialcontenttype gesture); }); this typetogglegroup\[2] onvaluechanged addlistener(() => { this setcategoryui(officialcontenttype pose); }); } // category toggle ui set private setcategoryui(category officialcontenttype) { if (category == officialcontenttype all) { this gesturelodaer thumbnails foreach((obj) => { obj setactive(true); }); } else { for (let i = 0; i < this gesturelodaer thumbnails length; i++) { const content = this gesturelodaer thumbnails\[i] getcomponent\<thumbnail>() content; if (content keywords includes(category)) { this gesturelodaer thumbnails\[i] setactive(true); } else { this gesturelodaer thumbnails\[i] setactive(false); } } } } private stopgesture() { this gesturelodaer sendmygesture(cancelmotion); } } step 4 2 server code the server code then declares the interface to contain the playergestureinfo in the same way the server code is based on the server code in the multiplay sample by default it creates an onmessage() callback that sends gesture information to other clients when a gesture changes within oncreate() import { sandbox, sandboxoptions, sandboxplayer } from 'zepeto multiplay'; import { player, transform, vector3 } from 'zepeto multiplay schema'; // define an interface playergestureinfo to represent the information of a player's gesture interface playergestureinfo { sessionid string, gestureid string } export default class extends sandbox { // define a constant object `message type` to store the message types used in the script message type = { onchangegesture "onchangegesture" } oncreate(options sandboxoptions) { // called when the room is created // handle the state or data initialization of the room this onmessage("onchangedtransform", (client, message) => { const player = this state players get(client sessionid); const transform = new transform(); transform position = new vector3(); transform position x = message position x; transform position y = message position y; transform position z = message position z; transform rotation = new vector3(); transform rotation x = message rotation x; transform rotation y = message rotation y; transform rotation z = message rotation z; player transform = transform; }); this onmessage("onchangedstate", (client, message) => { const player = this state players get(client sessionid); player state = message state; player substate = message substate; // character controller v2 }); // when the gesture is changed, this onmessage\<playergestureinfo>(this message type onchangegesture, (client, message) => { let gestureinfo\ playergestureinfo = { sessionid client sessionid, gestureid message gestureid }; // send gestures to other players except the client this broadcast(this message type onchangegesture, gestureinfo); }); } onjoin(client sandboxplayer) { // set the initial value after creating the player object defined in schemas json console log(`\[onjoin] sessionid ${client sessionid}, userid ${client userid}`) const player = new player(); player sessionid = client sessionid; if (client userid) { player zepetouserid = client userid; } // manage the player object using sessionid, a unique key value of the client object // the client can check the information about the player object added by set by adding the add onadd event to the players object this state players set(client sessionid, player); } onleave(client sandboxplayer, consented? boolean) { // by setting allowreconnection, it is possible to maintain connection for the circuit, but clean up immediately in the basic guide // the client can check the information about the deleted player object by adding the add onremove event to the players object this state players delete(client sessionid); } }