TUTORIELS
Tutoriel Multijoueur
15min
projet d'exemple 📘 exemple multiplay https //github com/naverz/zepeto multiplay example https //github com/naverz/zepeto multiplay example résumé résumé de la création d'un serveur multiplay à un client, configurez l'environnement nécessaire pour développer un monde multiplay difficulté intermédiaire temps requis 1 heure partie 1 installation de multiplay de la création d'un serveur multiplay à un client, configurez l'environnement nécessaire pour développer un monde multiplay https //www youtube com/watch?v=s68b1tma a8 https //www youtube com/watch?v=s68b1tma a8 partie 2 écriture de la logique du monde apprenez le schéma nécessaire pour la communication entre le serveur et le client, et définissez les types de schéma et l'état de la salle https //www youtube com/watch?v=tfky rabov0 https //www youtube com/watch?v=tfky rabov0 veuillez noter que le script du serveur utilisé dans la vidéo, qui inclut du contenu lié à hashcode, n'est plus supporté par conséquent, excluez le code suivant lors de l'écriture if (client hashcode) { 	player zepetohash = client hashcode; } partie 3 écriture de la logique du monde 2 écrivez la logique du monde nécessaire pour synchroniser l'emplacement du joueur à la sortie du joueur https //www youtube com/watch?v=h92gd2g9dhm https //www youtube com/watch?v=h92gd2g9dhm partie 4 exécution du serveur et connexion à multiplay exécutez le serveur et connectez vous à multiplay https //www youtube com/watch?v=fmk6jnlsjja https //www youtube com/watch?v=fmk6jnlsjja partie 5 scripts schemas json schemas json { "état" {"joueurs" {"carte" "joueur"}}, "joueur" {"sessionid" "chaîne","zepetouserid" "chaîne","transform" "transforme","état" "nombre","sousétat" "nombre"}, "transforme" {"position" "vector3","rotation" "vector3"}, "vector3" {"x" "nombre","y" "nombre","z" "nombre"} } code du serveur index ts import {sandbox, sandboxoptions, sandboxplayer} from "zepeto multiplay"; import {datastorage} from "zepeto multiplay datastorage"; import {player, transform, vector3} from "zepeto multiplay schema"; export default class extends sandbox { storagemap\ map\<string,datastorage> = new map\<string, datastorage>(); constructor() { super(); } oncreate(options sandboxoptions) { // appelé lorsque l'objet room est créé // gérer l'état ou l'initialisation des données de l'objet 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; if (player) { player transform = transform; } }); this onmessage("onchangedstate", (client, message) => { const player = this state players get(client sessionid); if (player) { player state = message state; player substate = message substate; // contrôleur de personnage v2 } }); } async onjoin(client sandboxplayer) { // créer l'objet joueur défini dans schemas json et définir la valeur initiale console log(`\[onjoin] sessionid ${client sessionid}, userid ${client userid}`) const player = new player(); player sessionid = client sessionid; if (client userid) { player zepetouserid = client userid; } // \[datastorage] chargement de datastorage du joueur entré const storage datastorage = client loaddatastorage(); this storagemap set(client sessionid,storage); let visit cnt = await storage get("visitcount") as number; if (visit cnt == null) visit cnt = 0; console log(`\[onjoin] compte de visites de ${client sessionid} ${visit cnt}`) // \[datastorage] mettre à jour le compte de visites du joueur puis enregistrer le stockage await storage set("visitcount", ++visit cnt); // gérer l'objet joueur en utilisant sessionid, une valeur clé unique de l'objet client // le client peut vérifier les informations sur l'objet joueur ajouté par set en ajoutant l'événement add onadd à l'objet players this state players set(client sessionid, player); } ontick(deltatime number) void { // il est appelé de manière répétée à chaque heure définie sur le serveur, et un événement à intervalle certain peut être géré en utilisant deltatime } async onleave(client sandboxplayer, consented? boolean) { 	 // en définissant allowreconnection, il est possible de maintenir la connexion pour le circuit, mais de nettoyer immédiatement dans le guide de base // le client peut vérifier les informations sur l'objet joueur supprimé en ajoutant l'événement add onremove à l'objet players this state players delete(client sessionid); } } code client clientstarter ts import {zepetoscriptbehaviour} from 'zepeto script' import {zepetoworldmultiplay} from 'zepeto world' import {room, roomdata} from 'zepeto multiplay' import {player, state, vector3} from 'zepeto multiplay schema' import {characterstate, spawninfo, zepetoplayers, zepetoplayer, characterjumpstate} from 'zepeto character controller' import as unityengine from "unityengine"; export default class clientstarterv2 extends zepetoscriptbehaviour { public multiplay zepetoworldmultiplay; private room room; private currentplayers map\<string, player> = new map\<string, player>(); private zepetoplayer zepetoplayer; private start() { this multiplay roomcreated += (room room) => { this room = room; }; this multiplay roomjoined += (room room) => { room onstatechange += this onstatechange; }; this startcoroutine(this sendmessageloop(0 04)); } // envoyer la transformation du personnage local au serveur à l'heure prévue private sendmessageloop(tick number) { while (true) { yield new unityengine waitforseconds(tick); if (this room != null && this room isconnected) { const hasplayer = zepetoplayers instance hasplayer(this room sessionid); if (hasplayer) { const character = zepetoplayers instance getplayer(this room sessionid) character; this sendtransform(character transform); this sendstate(character currentstate); } } } } private onstatechange(state state, isfirst boolean) { // lorsque le premier événement onstatechange est reçu, un instantané complet de l'état est enregistré if (isfirst) { // \[charactercontroller] (local) appelé lorsque l'instance du joueur est entièrement chargée dans la scène zepetoplayers instance onaddedlocalplayer addlistener(() => { const myplayer = zepetoplayers instance localplayer zepetoplayer; this zepetoplayer = myplayer; }); // \[charactercontroller] (local) appelé lorsque l'instance du joueur est entièrement chargée dans la scène zepetoplayers instance onaddedplayer addlistener((sessionid string) => { const islocal = this room sessionid === sessionid; if (!islocal) { const player player = this currentplayers get(sessionid); // \[roomstate] appelé chaque fois que l'état de l'instance du joueur est mis à jour player onchange += (changevalues) => this onupdateplayer(sessionid, player); } }); } let join = new map\<string, player>(); let leave = new map\<string, player>(this currentplayers); state players foreach((sessionid string, player player) => { if (!this currentplayers has(sessionid)) { join set(sessionid, player); } leave delete(sessionid); }); // \[roomstate] créer une instance de joueur pour les joueurs qui entrent dans la salle join foreach((player player, sessionid string) => this onjoinplayer(sessionid, player)); // \[roomstate] supprimer l'instance du joueur pour les joueurs qui quittent la salle leave foreach((player player, sessionid string) => this onleaveplayer(sessionid, player)); } private onjoinplayer(sessionid string, player player) { console log(`\[onjoinplayer] joueurs sessionid ${sessionid}`); this currentplayers set(sessionid, player); const spawninfo = new spawninfo(); const position = this parsevector3(player transform position); const rotation = this parsevector3(player transform rotation); spawninfo position = position; spawninfo rotation = unityengine quaternion euler(rotation); const islocal = this room sessionid === player sessionid; zepetoplayers instance createplayerwithuserid(sessionid, player zepetouserid, spawninfo, islocal); } private onleaveplayer(sessionid string, player player) { console log(`\[onremove] joueurs sessionid ${sessionid}`); this currentplayers delete(sessionid); zepetoplayers instance removeplayer(sessionid); } private onupdateplayer(sessionid string, player player) { const position = this parsevector3(player transform position); const zepetoplayer = zepetoplayers instance getplayer(sessionid); var movedir = unityengine vector3 op subtraction(position, zepetoplayer character transform position); movedir = new unityengine vector3(movedir x, 0, movedir z); if (movedir magnitude < 0 05) { if (player state === characterstate moveturn) return; zepetoplayer character stopmoving(); } else { zepetoplayer character movecontinuously(movedir); } if (player state === characterstate jump) { if (zepetoplayer character currentstate !== characterstate jump) { zepetoplayer character jump(); } if (player substate === characterjumpstate jumpdouble) { zepetoplayer character doublejump(); } } } private sendtransform(transform unityengine transform) { const data = new roomdata(); const pos = new roomdata(); pos add("x", transform localposition x); pos add("y", transform localposition y); pos add("z", transform localposition z); data add("position", pos getobject()); const rot = new roomdata(); rot add("x", transform localeulerangles x); rot add("y", transform localeulerangles y); rot add("z", transform localeulerangles z); data add("rotation", rot getobject()); this room send("onchangedtransform", data getobject()); } private sendstate(state characterstate) { const data = new roomdata(); data add("state", state); if(state === characterstate jump) { data add("substate", this zepetoplayer character motionv2 currentjumpstate); } this room send("onchangedstate", data getobject()); } private parsevector3(vector3 vector3) unityengine vector3 { return new unityengine vector3 ( vector3 x, vector3 y, vector3 z ); } }