ボットプレイヤー作成ガイド
ボットプレイヤーは、マルチプレイヤーの世界を開始するのに十分な人数がいないときや、プレイヤーが世界から離脱したときに補填するために使用されます。
ボットプレイヤーの動作は、各コンテンツに対して実装する必要があります。
このガイドは、ボットプレイヤーを作成する一般的な方法を説明しています。
📘 ボットプレイヤー作成ガイドは、マルチプレイヤーガイドに基づいています。[マルチプレイを作る]
ステップ 1: ボットプレイヤーを作成する
1-1. マルチプレイヤースキーマに「IsBot」というブール値を追加します。

1-2. サーバースクリプト index.ts にボットプレイヤーを作成するための以下の関数を定義し、必要なポイントで呼び出します。
// `CreateBot()` メソッドは、指定された `userId` を持つボットプレイヤーを作成するために使用されます。
CreateBot(userId: string) {
// 提供された `userId` を使用してボットプレイヤーのセッションIDを生成します。
const sessionId = "Bot_" + userId;
// 同じセッションIDを持つボットプレイヤーがすでに存在するか確認します。存在する場合は、重複を作成せずに戻ります。
if (this.state.players.has(sessionId)) {
return;
}
// ボットプレイヤーのための新しい `Player` オブジェクトを作成します。
const player: Player = new Player();
player.sessionId = sessionId;
if (userId) {
player.zepetoUserId = userId;
}
player.isBot = true;
// セッションIDをキーとして使用して、ボットプレイヤーを状態のプレイヤーマップに追加します。
this.state.players.set(player.sessionId, player);
this._botMap.set(sessionId, player);
}
👍 ヒント
- 特定のユーザーの userId は、ボットプレイヤーキャラクターを作成するために事前に保存されています。
- サーバーの OnJoin に接続しているクライアントの userId を確認することで、特定のユーザーの UserId を確認できます。以下のスクリプトをサーバースクリプトに書いた後、関連するワールドから接続してください。
onJoin(client: SandboxPlayer) {
console.log(client.userId);
}
ステップ 2: クライアントでボットプレイヤーを作成する
2-1. サーバーがボットプレイヤーを作成する場合、クライアントはそれを OnJoinPlayer() で新しいプレイヤーとして認識します。
- プロジェクトを作成 > 作成 > ZEPETO > TypeScript として作成し、BotPlayerManager に名前を変更します。
- OnAddedPlayer() にロジックを追加して各プレイヤーを作成し、ボットプレイヤーを区別してその ZEPETO キャラクターを作成するロジックを追加します。
import { ZepetoCharacter, ZepetoPlayers } from 'ZEPETO.Character.Controller';
import { Room } from 'ZEPETO.Multiplay';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import { ZepetoWorldMultiplay } from 'ZEPETO.World';
export default class BotPlayerManager extends ZepetoScriptBehaviour {
public zepetoWorldMultiplay: ZepetoWorldMultiplay;
// 現在の部屋とボットプレイヤーデータを保存するためのプライベート変数。
private _room: Room;
private _botMapData: Map<string, ZepetoCharacter> = new Map<string, ZepetoCharacter>();
Start() {
// `ZepetoWorldMultiplay` コンポーネントからの `RoomJoined` イベントをリッスンします。
this.zepetoWorldMultiplay.RoomJoined += (room: Room) => {
this._room = room;
}
// `ZepetoPlayers.instance` からの `OnAddedPlayer` イベントをリッスンして、新しく追加されたプレイヤーを処理します。
ZepetoPlayers.instance.OnAddedPlayer.AddListener((userId: string) => {
// `userId` を使用して部屋の状態から現在のプレイヤーデータを取得します。
const currentPlayer = this._room.State.players.get_Item(userId);
// プレイヤーがボットかどうかを確認し、ボットプレイヤーとして設定します。
if (currentPlayer.isBot) {
this.SetBotPlayer(currentPlayer.sessionId);
}
});
}
}
2-2. SetBotPlayer関数を作成して、ボットプレイヤーにタグと同期コンポーネントを追加し、それらを制御するスクリプトを作成します。
- ボットプレイヤーのデータを管理するために、Map形式で_botMapDataに保存します。
// `SetBotPlayer()`メソッドは、プレイヤーをボットとして設定するために使用されます。
SetBotPlayer(userId: string) {
// `userId`を使用してボットプレイヤーに関連付けられたZEPETOキャラクターを取得します。
const bot = ZepetoPlayers.instance.GetPlayer(userId).character;
// 識別のためにキャラクターの名前を`userId`に設定します。
bot.gameObject.name = userId;
// ボットプレイヤーのデータを`userId`をキーとして_botMapDataマップに保存します。
this._botMapData.set(userId, bot);
}
👍 ヒント ボットプレイヤーの動作を制御するために、SetBotPlayer()に追加のスクリプトや設定を追加できます。
ステップ 3: クライアントにボットプレイヤーボタンを作成する
開始するために特定の数のプレイヤーが必要なワールドでは、時々プレイヤーが不足していて、ワールドが開始するまで長い間待たなければなりません。
この場合、Botプレイヤーを追加することで世界を開始できます。
3-1. index.tsでサーバーがクライアントからメッセージを受信したときにCreateBot()を実行する関数を登録します。
async OnCreate() {
// 与えられたuserIdでボットプレイヤーを作成する"CreateBot"メッセージを処理します。
this.onMessage("CreateBot", (client, message) => {
this.CreateBot(message);
});
}
3-2. クライアントスクリプトBotPlayerManager.tsで、サーバーに"CreateBot"メッセージを送信する関数を書きます。
- 関数を実行する方法は、ボタンを押してメッセージを送信することです。
- 作成されるBotプレイヤーのユーザーIDを文字列としてメッセージを通じて送信します。
public buttonCreateBot: Button;
public botPlayerId: string;
Start() {
// ボットプレイヤーを作成するメッセージを送信するために"Create Bot"ボタンにクリックリスナーを追加します。
this.buttonCreateBot.onClick.AddListener(() => {
this._room.Send("CreateBot", this.botPlayerId);
});
}
3-3. さて、サーバーとランタイムを実行すると、ボットプレイヤーがボタンを押すと作成されるのがわかります。

ステップ 4: ボットプレイヤーを追加して世界を開始する
プレイヤーが不足している場合、ボットプレイヤーを追加して世界を開始できます。
4-1. サーバースクリプトで、OnJoin中に以下のコードを追加して、プレイヤーの数を確認し、4人以上のプレイヤーがいるときにワールドを開始します。
- CreateBot()内でプレイヤーの数を確認する関数を追加します。
- StartWorld()関数内でプレイの数をカウントする機能を追加します。
async onJoin(client: SandboxPlayer) {
// プレイヤーが参加した後、部屋のプレイヤー数を確認します。
this.CheckPlayerNumber();
}
// `CheckPlayerNumber()`メソッドは、部屋のプレイヤー数を確認し、4人以上のプレイヤーがいる場合にワールドを開始します。
CheckPlayerNumber() {
// 現在のプレイヤー数をコンソールに出力します。
console.log(`プレイヤー数, ${this.state.players.size}`);
// 部屋に4人以上のプレイヤーがいる場合、ワールドを開始します。
if (this.state.players.size >= 4) {
this.StartWorld();
}
}
// `CreateBot()`メソッドは、指定された`userId`を持つボットプレイヤーを作成するために使用されます。
CreateBot(userId: string) {
// 提供された`userId`を使用してボットプレイヤーのセッションIDを生成します。
const sessionId = "Bot_" + userId;
// 同じセッションIDを持つボットプレイヤーがすでに存在するか確認します。存在する場合は、重複を作成せずに戻ります。
if (this.state.players.has(sessionId)) {
return;
}
// ボットプレイヤーのために新しい`Player`オブジェクトを作成します。
const player: Player = new Player();
player.sessionId = sessionId;
if (userId) {
player.zepetoUserId = userId;
}
player.isBot = true;
// セッションIDをキーとして、状態のプレイヤーマップにボットプレイヤーを追加します。
this.state.players.set(player.sessionId, player);
this._botMap.set(sessionId, player);
// ボットプレイヤーを追加した後、部屋のプレイヤー数を確認します。
this.CheckPlayerNumber();
}
private playTime: number = 0;
// `StartWorld()`メソッドは、プレイ時間を増加させ、すべてのクライアントに"StartWorld"メッセージをブロードキャストします。
StartWorld() {
this.playTime += 1;
// ワールドの開始と現在のプレイ時間を示すメッセージを出力します。
console.log("ワールドを開始します!");
this.broadcast("StartWorld", this.playTime);
}
- サーバーでは、実際のプレイヤーが部屋に参加するときに OnJoin が実行されます。したがって、CreateBot を介して Bot プレイヤーが作成され、OnJoin を介してプレイヤーが入るとき、checkPlayerNumber() は人数を追加します。
4-2. クライアントスクリプト BotPlayerManager.ts で、サーバーから StartWorld メッセージを受信したときに実行される StartWorld() を記述します。
Start() {
// `ZepetoWorldMultiplay` コンポーネントからの `RoomJoined` イベントをリッスンします。
this.zepetoWorldMultiplay.RoomJoined += (room: Room) => {
// "StartWorld" メッセージタイプのメッセージハンドラーを追加します。
this._room.AddMessageHandler("StartWorld", (playTime: number) => {
this.StartWorld(playTime);
});
}
// `StartWorld()` メソッドは、提供された `playTime` でワールドが開始されるときに呼び出されます。
StartWorld(playTime: number) {
// `playTime` とともにワールド開始メッセージを表示します。
console.log(`Start World : ${playTime}`);
}
4-3. 実行時に、Bot プレイヤーを含む 4 人以上のプレイヤーがいる場合、サーバーコンソールとクライアントのコンソールに「World Start」というログが表示されます。

ステップ 5: Bot プレイヤーの位置を同期する
以下は、追加されたボットプレイヤーをローカルプレイヤーの位置に移動させ、移動位置を同期させるサンプルコードです。
5-1. まず、サーバーのindex.tsでクライアントから受信したメッセージのときにボットプレイヤーを移動させるコードを書きます。MoveBotが受信されると、
async OnCreate() {
// "MoveBot"メッセージを処理し、ボットプレイヤーを指定された位置に移動させます。
this.onMessage("MoveBot", (client, message) => {
this.MoveBot(client, message);
});
}
// `MoveBot()`メソッドは、受信したメッセージに基づいてボットプレイヤーを指定された位置に移動させます。
MoveBot(client: SandboxPlayer, message: string) {
// クライアントから受信したJSONメッセージを解析して位置情報を抽出します。
const position = JSON.parse(message);
// ユーザーのセッションIDと解析された位置データを持つ新しいメッセージオブジェクトを作成します。
const newMessage = {
user: client.sessionId,
positionX: position.x,
positionY: position.y,
positionZ: position.z
}
// 新しいメッセージデータをJSON文字列としてすべてのクライアントに"MoveBotToPosition"メッセージをブロードキャストします。
this.broadcast("MoveBotToPosition", JSON.stringify(newMessage));
}
5-2. クライアントスクリプトのBotPlayerManager.tsで、buttonCallBotが押されたときにローカルプレイヤーの位置をサーバーに送信するSendBotPosition()を書きます。
- メッセージ MoveBotToPosition がサーバーから受信されたときに、すべてのボットプレイヤーをメッセージに含まれる位置情報に移動させるコードを書いてください。
public buttonCallBot: Button;
Start(){
// `ZepetoWorldMultiplay` コンポーネントからの `RoomJoined` イベントをリッスンします。
this.zepetoWorldMultiplay.RoomJoined += (room: Room) => {
this._room = room;
// "StartWorld" メッセージタイプのメッセージハンドラーを追加します。
this._room.AddMessageHandler("StartWorld", (playTime: number) => {
this.StartWorld(playTime);
});
// ボタン "Call Bot" にクリックリスナーを追加して、ボットプレイヤーの位置を送信するメッセージを送ります。
this.buttonCallBot.onClick.AddListener(() => {
this.SendBotPosition();
});
}
// このメソッドは、ボットの動きの同期のためにローカルプレイヤーのキャラクターの位置をサーバーに送信します。
SendBotPosition() {
// ローカルプレイヤーのキャラクターの位置を取得します。
const localPlayerPosition = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character.transform.position;
// ローカルプレイヤーのキャラクターの x, y, z 座標を含む位置オブジェクトを作成します。
const position = {
x: localPlayerPosition.x,
y: localPlayerPosition.y,
z: localPlayerPosition.z
}
// 位置オブジェクトを JSON 文字列に変換し、"MoveBot" メッセージタイプでサーバーに送信します。
this._room.Send("MoveBot", JSON.stringify(position));
}
MoveBotToPosition(message) {
// クライアントから受信した JSON メッセージを解析して位置情報を抽出します。
const jsonMessage = JSON.parse(message);
const position = new Vector3(jsonMessage.positionX, jsonMessage.positionY, jsonMessage.positionZ);
// `_botMapData` マップ内の各ボットキャラクターを指定された位置に移動させ、x および z 軸に小さなランダムオフセットを加えます。
this._botMapData.forEach((character: ZepetoCharacter) => {
// `MoveToPosition()` メソッドを使用してキャラクターを指定された位置に移動させます。
// ここでは、ボットの自然な動きを作成するために、ターゲット位置に小さなランダムオフセットを追加します。
character.MoveToPosition(position + new Vector3(Random.Range(0.5, 1), 0, Random.Range(0.5, 1)));
});
}
5-3. さて、ランタイムでボットプレイヤーを作成し、buttonCallBot ボタンを押すと、作成されたボットプレイヤーがローカルプレイヤーのキャラクターの位置に移動するのが見えるはずです。

BotPlayerManager.tsの全コード
import { Random, Vector3 } from 'UnityEngine';
import { Button } from 'UnityEngine.UI';
import { ZepetoCharacter, ZepetoPlayers } from 'ZEPETO.Character.Controller';
import { Room } from 'ZEPETO.Multiplay';
import { ZepetoScriptBehaviour } from 'ZEPETO.Script'
import { ZepetoWorldMultiplay } from 'ZEPETO.World';
export default class BotPlayerManager extends ZepetoScriptBehaviour {
// Inspectorから必要なコンポーネントと設定を参照するための公開プロパティ。
public zepetoWorldMultiplay: ZepetoWorldMultiplay;
public buttonCreateBot: Button;
public buttonCallBot: Button;
public botPlayerId: string;
// 現在の部屋とボットプレイヤーデータを保存するためのプライベート変数。
private _room: Room;
private _botMapData: Map<string, ZepetoCharacter> = new Map<string, ZepetoCharacter>();
Start() {
// `ZepetoWorldMultiplay`コンポーネントからの`RoomJoined`イベントをリッスンします。
this.zepetoWorldMultiplay.RoomJoined += (room: Room) => {
this._room = room;
// "StartWorld"メッセージタイプのメッセージハンドラーを追加します。
this._room.AddMessageHandler("StartWorld", (playTime: number) => {
this.StartWorld(playTime);
});
}
// 新しく追加されたプレイヤーを処理するために`ZepetoPlayers.instance`からの`OnAddedPlayer`イベントをリッスンします。
ZepetoPlayers.instance.OnAddedPlayer.AddListener((userId: string) => {
// `userId`を使用して部屋の状態から現在のプレイヤーデータを取得します。
const currentPlayer = this._room.State.players.get_Item(userId);
// プレイヤーがボットかどうかを確認し、そうであればボットプレイヤーとして設定します。
if (currentPlayer.isBot) {
this.SetBotPlayer(currentPlayer.sessionId);
}
});
// ボットプレイヤーを作成するためのメッセージを送信するために"Create Bot"ボタンにクリックリスナーを追加します。
this.buttonCreateBot.onClick.AddListener(() => {
this._room.Send("CreateBot", this.botPlayerId);
});
this.zepetoWorldMultiplay.RoomJoined += (room: Room) => {
this._room = room;
this._room.AddMessageHandler("MoveBotToPosition", (message: string) => {
this.MoveBotToPosition(message);
});
}
// ボットプレイヤーの位置を送信するためのメッセージを送信するために"Call Bot"ボタンにクリックリスナーを追加します。
this.buttonCallBot.onClick.AddListener(() => {
this.SendBotPosition();
});
}
// `SetBotPlayer()`メソッドはプレイヤーをボットとして設定するために使用されます。
SetBotPlayer(userId: string) {
// ボットプレイヤーに関連付けられたZEPETOキャラクターを`userId`を使用して取得します。
const bot = ZepetoPlayers.instance.GetPlayer(userId).character;
// 識別のためにキャラクターの名前を`userId`に設定します。
bot.gameObject.name = userId;
// ボットプレイヤーのデータを`userId`をキーとして`_botMapData`マップに保存します。
this._botMapData.set(userId, bot);
}
// `StartWorld()`メソッドは、指定された`playTime`で世界が開始されると呼び出されます。
StartWorld(playTime: number) {
// `playTime`と共に世界開始メッセージを出力します。
console.log(`Start World : ${playTime}`);
}
// このメソッドは、ボットの動きの同期のためにローカルプレイヤーのキャラクターの位置をサーバーに送信します。
SendBotPosition() {
// ローカルプレイヤーのキャラクターの位置を取得します。
const localPlayerPosition = ZepetoPlayers.instance.LocalPlayer.zepetoPlayer.character.transform.position;
// ローカルプレイヤーのキャラクターのx、y、z座標を含む位置オブジェクトを作成します。
const position = {
x: localPlayerPosition.x,
y: localPlayerPosition.y,
z: localPlayerPosition.z
}
// 位置オブジェクトをJSON文字列に変換し、"MoveBot"メッセージタイプでサーバーに送信します。
this._room.Send("MoveBot", JSON.stringify(position));
}
// このメソッドは、サーバーがクライアントから"MoveBot"メッセージを受信したときに呼び出され、ボットキャラクターを指定された位置に移動させます。
MoveBotToPosition(message) {
// クライアントから受信したJSONメッセージを解析して位置情報を抽出します。
const jsonMessage = JSON.parse(message);
const position = new Vector3(jsonMessage.positionX, jsonMessage.positionY, jsonMessage.positionZ);
// `_botMapData`マップ内の各ボットキャラクターを指定された位置に小さなランダムオフセットを加えて移動させます。
this._botMapData.forEach((character: ZepetoCharacter) => {
// `MoveToPosition()`メソッドは、キャラクターを指定された位置に移動させるために使用されます。
// ここでは、ボットの自然な動きを作成するために、ターゲット位置に小さなランダムオフセットが追加されます。
character.MoveToPosition(position + new Vector3(Random.Range(0.5, 1), 0, Random.Range(0.5, 1)));
});
}
}
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>();
// ボットプレイヤーのマップデータを _botMap として保存
private _botMap: Map<string, Player> = new Map<string, Player>();
private playTime: number = 0;
constructor() {
super();
}
onCreate(options: SandboxOptions) {
// Room オブジェクトが作成されたときに呼び出されます。
// Room オブジェクトの状態またはデータの初期化を処理します。
this.onMessage("onChangedTransform", (client, message) => {
this.state.players.get(client.sessionId);
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;
}
});
// 指定された userId でボットプレイヤーを作成する "CreateBot" メッセージを処理します。
this.onMessage("CreateBot", (client, message) => {
this.CreateBot(message);
});
// 指定された位置にボットプレイヤーを移動する "MoveBot" メッセージを処理します。
this.onMessage("MoveBot", (client, message) => {
this.MoveBot(client, message);
});
}
// `CreateBot()` メソッドは、指定された `userId` でボットプレイヤーを作成するために使用されます。
CreateBot(userId: string) {
// 提供された `userId` を使用してボットプレイヤーのセッション ID を生成します。
const sessionId = "Bot_" + userId;
// 同じセッション ID のボットプレイヤーがすでに存在するか確認します。存在する場合は、重複を作成せずに戻ります。
if (this.state.players.has(sessionId)) {
return;
}
// ボットプレイヤーのための新しい `Player` オブジェクトを作成します。
const player: Player = new Player();
player.sessionId = sessionId;
if (userId) {
player.zepetoUserId = userId;
}
player.isBot = true;
// セッション ID をキーとして、状態のプレイヤーマップにボットプレイヤーを追加します。
this.state.players.set(player.sessionId, player);
this._botMap.set(sessionId, player);
// ボットプレイヤーを追加した後、部屋のプレイヤー数を確認します。
this.CheckPlayerNumber();
}
// `CheckPlayerNumber()` メソッドは、部屋のプレイヤー数を確認し、4人以上のプレイヤーがいる場合にワールドを開始します。
CheckPlayerNumber() {
// 現在の部屋のプレイヤー数をコンソールに出力します。
console.log(`プレイヤー数, ${this.state.players.size}`);
// 部屋に4人以上のプレイヤーがいる場合、ワールドを開始します。
if (this.state.players.size >= 4) {
this.StartWorld();
}
}
// `StartWorld()` メソッドは、プレイ時間を増加させ、すべてのクライアントに "StartWorld" メッセージをブロードキャストします。
StartWorld() {
this.playTime += 1;
// ワールドの開始と現在のプレイ時間を示すメッセージを出力します。
console.log("ワールドを開始!");
this.broadcast("StartWorld", this.playTime);
}
// `MoveBot()` メソッドは、受信したメッセージに基づいてボットプレイヤーを指定された位置に移動します。
MoveBot(client: SandboxPlayer, message: string) {
// クライアントから受信した JSON メッセージを解析して位置情報を抽出します。
const position = JSON.parse(message);
// ユーザーのセッション ID と解析された位置データを持つ新しいメッセージオブジェクトを作成します。
const newMessage = {
user: client.sessionId,
positionX: position.x,
positionY: position.y,
positionZ: position.z
}
// 新しいメッセージデータを JSON 文字列としてすべてのクライアントに "MoveBotToPosition" メッセージをブロードキャストします。
this.broadcast("MoveBotToPosition", JSON.stringify(newMessage));
}
async onJoin(client: SandboxPlayer) {
// schemas.json で定義されたプレイヤーオブジェクトを作成し、初期値を設定します。
console.log(`[OnJoin] sessionId : ${client.sessionId}, HashCode : ${client.hashCode}, userId : ${client.userId}`)
const player = new Player();
player.sessionId = client.sessionId;
if (client.hashCode) {
player.zepetoHash = client.hashCode;
}
if (client.userId) {
player.zepetoUserId = client.userId;
}
// [DataStorage] 入力されたプレイヤーの 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 を使用してプレイヤーオブジェクトを管理します。これはクライアントオブジェクトの一意のキー値です。
// クライアントは、プレイヤーオブジェクトに追加された情報を players オブジェクトに add_OnAdd イベントを追加することで確認できます。
this.state.players.set(client.sessionId, player);
// プレイヤーが参加した後、部屋のプレイヤー数を確認します。
this.CheckPlayerNumber();
}
onTick(deltaTime: number): void {
// サーバーで設定された各時間に繰り返し呼び出され、特定の間隔イベントを deltaTime を使用して管理できます。
}
async onLeave(client: SandboxPlayer, consented?: boolean) {
// allowReconnection を設定することで、回路の接続を維持できますが、基本的なガイドではすぐにクリーンアップします。
// クライアントは、プレイヤーオブジェクトが削除された情報を players オブジェクトに add_OnRemove イベントを追加することで確認できます。
this.state.players.delete(client.sessionId);
}
}