チュートリアル
マルチプレイを作る
15分
サンプルプロジェクト 📘 マルチプレイサンプル https //github com/naverz/zepeto multiplay example https //github com/naverz/zepeto multiplay example 概要 概要 マルチプレイサーバーからクライアントを作成し、マルチプレイワールドを開発するために必要な環境を設定します。 難易度 中級 必要な時間 1時間 パート1 マルチプレイのインストール マルチプレイサーバーからクライアントを作成し、マルチプレイワールドを開発するために必要な環境を設定します。 https //www youtube com/watch?v=s68b1tma a8 https //www youtube com/watch?v=s68b1tma a8 第2部 世界ロジックの記述 サーバーとクライアント間の通信に必要なスキーマについて学び、スキーマタイプとルームステートを定義します。 https //www youtube com/watch?v=tfky rabov0 https //www youtube com/watch?v=tfky rabov0 ビデオで使用されているサーバースクリプトは、hashcodeに関連するコンテンツを含んでおり、もはやサポートされていないことに注意してください。 したがって、記述時には以下のコードを除外してください。 if (client hashcode) { 	player zepetohash = client hashcode; } 第3部 世界ロジックの記述2 プレイヤーの位置からプレイヤーの出口に同期するために必要な世界ロジックを記述します。 https //www youtube com/watch?v=h92gd2g9dhm https //www youtube com/watch?v=h92gd2g9dhm パート4 サーバーの実行とマルチプレイへの接続 サーバーを実行し、マルチプレイに接続します。 https //www youtube com/watch?v=fmk6jnlsjja https //www youtube com/watch?v=fmk6jnlsjja パート5 スクリプト schemas json schemas json { "state" {"players" {"map" "プレイヤー"}}, "player" {"sessionid" "文字列","zepetouserid" "文字列","transform" "変換","state" "数値","substate" "数値"}, "transform" {"position" "ベクトル3","rotation" "ベクトル3"}, "vector3" {"x" "数値","y" "数値","z" "数値"} } サーバーコード 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) { // ルームオブジェクトが作成されたときに呼び出されます。 // ルームオブジェクトの状態またはデータの初期化を処理します。 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; // キャラクターコントローラー v2 } }); } async onjoin(client sandboxplayer) { // 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; } // \[datastorage] 入力されたプレイヤーのデータストレージをロードします 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] ${client sessionid}の訪問回数 ${visit cnt}`) // \[datastorage] プレイヤーの訪問回数を更新し、その後ストレージを保存します await storage set("visitcount", ++visit cnt); // セッションidを使用してプレイヤーオブジェクトを管理します。これはクライアントオブジェクトの一意のキー値です。 // クライアントは、プレイヤーオブジェクトに追加された情報を確認できます。 this state players set(client sessionid, player); } ontick(deltatime number) void { // サーバーの各設定時間で繰り返し呼び出され、特定の間隔イベントをdeltatimeを使用して管理できます。 } async onleave(client sandboxplayer, consented? boolean) { 	 // allowreconnectionを設定することで、回路の接続を維持できますが、基本ガイドではすぐにクリーンアップします。 // クライアントは、削除されたプレイヤーオブジェクトに関する情報を確認できます。 this state players delete(client sessionid); } } クライアントコード 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)); } // send the local character transform to the server at the scheduled interval time 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) { // when the first onstatechange event is received, a full state snapshot is recorded if (isfirst) { // \[charactercontroller] (local) called when the player instance is fully loaded in scene zepetoplayers instance onaddedlocalplayer addlistener(() => { const myplayer = zepetoplayers instance localplayer zepetoplayer; this zepetoplayer = myplayer; }); // \[charactercontroller] (local) called when the player instance is fully loaded in scene zepetoplayers instance onaddedplayer addlistener((sessionid string) => { const islocal = this room sessionid === sessionid; if (!islocal) { const player player = this currentplayers get(sessionid); // \[roomstate] called whenever the state of the player instance is updated 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] create a player instance for players that enter the room join foreach((player player, sessionid string) => this onjoinplayer(sessionid, player)); // \[roomstate] remove the player instance for players that exit the room leave foreach((player player, sessionid string) => this onleaveplayer(sessionid, player)); } private onjoinplayer(sessionid string, player player) { console log(`\[onjoinplayer] players 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] players 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 ); } }