あなたの世界を作りなさい
プレイヤーとキャラクター:基本

ZEPETO Players

18min
zepetoplayers は、zepetoプレイヤーとzepetoキャラクターの両方を制御するために設計されたマネージャー(シングルトン)クラスです。 zepetoplayersをシーンに追加することで、zepetoプレイヤーを作成、削除、操作できます。 zepetoplayer は、マルチプレイヤーの世界で直接制御するプレイヤーと他のプレイヤーを管理するために使用されるzepetoキャラクターの個別のインスタンスを表します。 マルチプレイヤーの世界で作成されたすべてのzepetoplayerには一意のセッションidが割り当てられ、このセッションidを使用して管理されます。 zepetoplayerはzepetocharacterの属性を持っているため、zepetocharacterに関連する関数を使用して制御できます。 zepetoplayerには3種類があります: zepetoplayer 説明 ローカルプレイヤー ローカルユーザーによって直接制御されるzepetoキャラクターインスタンスです。 \ キャラクターコントローラー/zepetoカメラコンポーネントが付属しています。 ネットワークプレイヤー(リモートプレイヤー) マルチプレイコンテンツで読み込んで利用できるzepetoキャラクターインスタンスです。 \ キャラクターコントローラー/zepetoカメラコンポーネントは付属していません。 ボットプレイヤー マルチプレイコンテンツ用のzepetoキャラクターインスタンスですが、実際のユーザーではなくボットによって制御されます。 プレイヤーが不足している状態でマルチプレイヤーワールドを開始する際や、プレイヤーがプレイ中に退出した場合の代替として使用されます。 \ キャラクターコントローラー/zepetoカメラコンポーネントは付属していません。 以下のガイドを参照してください。 \[ zepeto players docid\ hy3lzm6zcipjlvcznze5c ] \[ ボットプレイヤー作成ガイド docid\ obi qzwdc101x krzfohv ] zepetocharacter は、world sceneで読み込んで制御できるzepetoキャラクターの基本インスタンスユニットです。 zepetocharacterは、zepetoアプリを通じて作成されたアバターの外見を持っています。 以下のガイドを参照してください。 \[ zepetoキャラクター docid\ lmi7uxuecfhlfsvgcsrlo ] zepetoplayersの追加 hierarchyウィンドウで、zepeto → zepetoplayersタブを選択します。 シーンに追加できるようになりました。 zepetoplayersを追加するだけでは、zepeto playerをシーンに持ち込むことはできません。zepetoplayersのキャラクター作成apiを使用してスクリプトを実装する必要があります。 シーンでローカルプレイヤーだけを迅速に作成して試したい場合は、ガイドを参照してください: 以下のガイドを参照してください。 \[ zepetoキャラクターを作成する docid\ x3hpjvyg3lww0pu1r8k1s ] zepetoプレイヤーの名前とプロフィール写真を表示する方法のサンプルについては、ガイドを参照してください: 以下のガイドを参照してください。 \[ ユーザー情報 docid\ u7ddubms3cujnfxizpcqv ] zepetoプレイヤーapi zepetoplayers apiに興味がある場合は、ドキュメントを参照してください: 以下のガイドを参照してください。 \[ zepeto character controller api ] このガイドは、主にマルチプレイヤーシナリオでのzepetoプレイヤーの使用例をカバーしています。 zepetoプレイヤーを使用したマルチプレイ位置同期の実装 シングルプレイヤーの世界では、ローカルプレイヤーを作成する必要があり、ローカルプレイヤーのみが画面に表示されるため、同期は不要です。 しかし、マルチプレイの世界では、直接操作するローカルプレイヤーだけでなく、ネットワークプレイヤーと呼ばれる他のプレイヤーも画面に表示する必要があります。 ネットワークプレイヤーのすべてのアクション 移動、ジャンプ、特定のジェスチャーを行う は、あなたの画面に同じように表示される必要があります。 このプロセスは同期と呼ばれます。 同期スクリプトを実装しないと、ネットワークプレイヤーの外見や動きを見ることができません。 同期なしのマルチプレイワールドでは、他のクライアントがルームに入ったかどうかを知る唯一の方法はホームボタンを通じてです。 ステップ 1 マルチプレイ環境の設定 マルチプレイチュートリアルビデオを通じて、基本設定と概念を理解することから始めることをお勧めします。 以下のガイドを参照してください。 \[ マルチプレイを作る docid\ zp83r5fc6n0bi1i84p4k3 ] \[ マルチプレイ docid 0rsogfdzraxefyex5jva6 ] ステップ 2 他のプレイヤーを画面に表示する あなたのローカルプレイヤーは、他の誰かのデバイス上でネットワークプレイヤーとして扱われます。 これは、あなたのローカルプレイヤーも同期のためにサーバーに情報を送信しなければならないことを意味します。 マルチプレイワールドに接続されているすべてのプレイヤーは、自分の情報を共有する必要があります。 マルチプレイルームに接続されているすべてのクライアントは、マルチプレイルームの状態データを共有します。 このルーム状態データは、schemas jsonで定義されたスキーマに従います。 (スキーマをデータ構造として考慮してください) このガイドでは、ルームステートデータを通じてプレイヤーの位置を同期します。したがって、各プレイヤーの位置データを表すことができるスキーマをschemas jsonに定義してください。 schema json { "state" {"players" {"map" "player"}}, "player" {"sessionid" "string","zepetouserid" "string","transform" "transform","state" "number","substate" "number"}, "transform" {"position" "vector3","rotation" "vector3"}, "vector3" {"x" "number","y" "number","z" "number"} } 以下のガイドを参照してください。 \[ マルチプレイルームステート docid\ af6f0ntwrvehfvlld zcg ] 👍 ヒント サーバーとすべてのクライアントが共有すべき情報をすべて、 マルチプレイルームステートに保存してください。 レベル、経験値、スコアなど、各プレイヤーの個別データを保存するには、 データストレージを使用してください。 サーバースクリプトは、別のプレイヤーがルームに入ったときにそれを認識し、その情報をクライアントに送信して、そのプレイヤーをシーンにロードすることができます。 ステップ 2 1 基本サーバースクリプト プレイヤーがマルチプレイワールドのルームに入ると、onjoin()が呼び出されます。 サーバースクリプトでは、接続されたプレイヤーの情報をルームステートのプレイヤーに追加します。 また、プレイヤーがルームを退出すると、onleave()が呼び出されます。 サーバースクリプトは、ルームステートのプレイヤーから退出したプレイヤーの情報を削除します。 import {sandbox, sandboxoptions, sandboxplayer} from "zepeto multiplay"; import {player} from "zepeto multiplay schema"; export default class extends sandbox { oncreate(options sandboxoptions) { } async onjoin(client sandboxplayer) { const player = new player(); player sessionid = client sessionid; if (client userid) { player zepetouserid = client userid; } // セッションidを使用してプレイヤーオブジェクトを管理します。これはクライアントオブジェクトのユニークなキー値です。 // クライアントは、プレイヤーオブジェクトに追加された情報を、プレイヤーオブジェクトにadd onaddイベントを追加することで確認できます。 this state players set(client sessionid, player); } async onleave(client sandboxplayer, consented? boolean) { this state players delete(client sessionid); } } ステップ 2 2 基本クライアントスクリプト マルチプレイワールドを作成する際には、サーバーと通信するためのクライアントスクリプトが必要です。 以下は、マルチプレイ用の基本的なクライアントスクリプトの例です。 クライアント側では、 currentplayers マップデータ構造を使用して、表示するプレイヤーデータを管理します。 以下に重要なコードの行を示します: zepetoplayers instance createplayerwithuserid(sessionid, player zepetouserid, spawninfo, islocal); クライアントがサーバーのルームステートからプレイヤー情報を受信し、新しいプレイヤーがルームに参加すると、セッションidが割り当てられます。作成されるプレイヤーのセッションidが自分のセッションidと一致する場合、そのプレイヤーはローカルと見なされます。この場合、プレイヤーは次のようにインスタンス化されます: islocal = true , これはローカルプレイヤーであることを示しています。 その後、ローカルでないプレイヤーは islocal = false これにより、ローカルプレイヤーと非ローカルプレイヤーのすべての外観が画面に表示されることが保証されます。 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 multiplayclientcode 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; }; } private onstatechange(state state, isfirst boolean) { // 最初のonstatechangeイベントが受信されたとき、完全な状態スナップショットが記録されます。 if (isfirst) { // \[charactercontroller] (ローカル) プレイヤーインスタンスがシーンに完全にロードされたときに呼び出されます zepetoplayers instance onaddedlocalplayer addlistener(() => { const myplayer = zepetoplayers instance localplayer zepetoplayer; this zepetoplayer = myplayer; }); // \[charactercontroller] (ローカル) プレイヤーインスタンスがシーンに完全にロードされたときに呼び出されます zepetoplayers instance onaddedplayer addlistener((sessionid string) => { const islocal = this room sessionid === sessionid; if (!islocal) { const player player = this currentplayers get(sessionid); } }); } 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] ルームに入るプレイヤーのインスタンスを作成します join foreach((player player, sessionid string) => this onjoinplayer(sessionid, player)); // \[roomstate] ルームを退出するプレイヤーのインスタンスを削除します 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 islocal = this room sessionid === player sessionid; zepetoplayers instance createplayerwithuserid(sessionid, player zepetouserid, new spawninfo(), islocal); } private onleaveplayer(sessionid string, player player) { console log(`\[onremove] players sessionid ${sessionid}`); this currentplayers delete(sessionid); zepetoplayers instance removeplayer(sessionid); } } 今、プレイヤーが入ると、zepetoキャラクターが画面に作成されたことを確認できます。 しかし、プレイヤーの動きはまだ画面に反映されていません。 次に、位置の同期に進みましょう。 ステップ3:他のプレイヤーの情報を取得して同期する 同期のために、プレイヤーが動いたりアクションを取ったりするたびに、彼らはサーバーに状態の変化を送信しなければなりません。 状態の変化を含むメッセージをサーバーに送信するのは、ルームメッセージ通信を通じて行われます。 サーバーがプレイヤーの状態変化に関するメッセージを受信すると、ルームの状態が更新されます。 以下のガイドを参照してください。 \[ multiplay room message docid\ p hrmrcyw91bz52ji7w67 ] 例えば、あなたのローカルプレイヤーがbという名前だとしましょう。 ネットワークプレイヤーaがルームに参加すると、彼らは座標x 0, y 0, z 0でインスタンス化されます。 プレイヤーaが位置x 10, y 0, z 0に移動すると、bはaが移動していることを知る方法が実際にはありません。 これは、両者が別々のデバイスでローカルに操作されているため、別々のクライアントであるからです。 したがって、aを操作している人は、ルームメッセージを介してサーバーに自分の動きを伝える必要があります。 サーバーがこの情報を受け取ると、ルーム内の全員にaのリアルタイムの位置を通知します。これにより、bはついにaが移動していることを認識します。 サーバーが他のプレイヤーに通知するためには、2つの方法があります: ルームメッセージのブロードキャストを使用する。 ルームの状態を更新し、その後クライアントがルームの状態を取得して適用する。 このガイドでは、ルームの状態を更新する2番目の方法を利用します。 シーンにロードされたzepetoプレイヤーがzepetoキャラクター属性を持っているため、特定の場所への移動やジャンプを指示するためにzepetoキャラクター機能を使用できます。 bのクライアントがaの移動をx 10, y 0, z 0に視覚化するための最も直感的なアプローチは、movetoposition()を使用することです。この関数は、サーバーから受信したaの最新の位置にプレイヤーを移動させます。 位置の変更だけでなく、ジェスチャー、スキルの使用、アイテムの収集、すべての状態変更は、同期のためにサーバーとクライアントの通信を必要とします。 ネットワーク全体で全てのアクションを調和させるために、同期を実装する必要があります。 👍 同期の概念の概要 ローカルプレイヤーにステータス変更があると、room messageを使用してサーバーに送信します。 サーバーは、ローカルプレイヤーを除くすべての他のプレイヤーにステータス変更を通知します。 ステータス変更メッセージを受信すると、クライアントコードはメッセージを送信したプレイヤーのステータスを更新します。 ステップ 3 1 完全な位置同期を持つサーバースクリプト 基本的なサーバースクリプトでは、ローカルプレイヤーのクライアントからのステータス変更に関するメッセージを受信するたびに、ルームの状態を更新するための追加の実装が必要です。 import {sandbox, sandboxoptions, sandboxplayer} from "zepeto multiplay"; import {player, transform, vector3} from "zepeto multiplay schema"; export default class extends sandbox { 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; } // セッションidを使用してプレイヤーオブジェクトを管理します。これはクライアントオブジェクトの一意のキー値です。 // クライアントは、プレイヤーオブジェクトに追加された情報をplayersオブジェクトにadd onaddイベントを追加することで確認できます。 this state players set(client sessionid, player); } async onleave(client sandboxplayer, consented? boolean) { // allowreconnectionを設定することで、回路の接続を維持できますが、基本的なガイドではすぐにクリーンアップします。 // クライアントは、削除されたプレイヤーオブジェクトに関する情報をplayersオブジェクトにadd onremoveイベントを追加することで確認できます。 this state players delete(client sessionid); } } ステップ 3 2 完全な位置同期を持つクライアントスクリプト 基本的なクライアントスクリプトにおける重要な実装は次のとおりです: サーバーのルームステートが変更されたときにonstatechangeを自動的に呼び出します。 を使用して、 sendmessageloop(0 04) 関数を使用して、ローカルプレイヤーの位置とキャラクターのジャンプ状態情報をサーバーに0 04秒ごとに送信します。 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 ); } } 👍 ヒント このガイドは位置の同期のみを実装しています。ジェスチャーの同期、オブジェクトの同期などは実装されていません。 原則はすべて同じですが、必要な瞬間にルームメッセージを送受信するプロセスが必要です。 同期をより便利に実装したい場合は、マルチプレイ同期モジュールの使用を検討してください。