创造你的世界
玩家及角色:进阶

机器人玩家创建指南

15
机器人玩家用于在没有足够的人开始多人世界时填补空缺,或者当玩家在游戏中离开时。 机器人玩家的行为需要为每个内容实现。 本指南描述了创建机器人玩家的一般方法。 📘 机器人玩家创建指南基于多人游戏指南。 \[ 创建多人游戏 docid\ spk tardrdz3td3r6gnae ] 步骤 1:创建一个机器人玩家 1 1 添加一个名为 isbot 的布尔值到多人游戏架构中。 1 2 在服务器脚本 index ts 中定义以下函数以创建一个机器人玩家,并在所需位置调用它。 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。在服务器脚本中编写以下脚本后,从相关世界连接。 index ts onjoin(client sandboxplayer) { &#x9;console log(client userid); } 步骤 2:在客户端创建一个机器人玩家 2 1 如果服务器在某个时刻创建了一个机器人玩家,客户端将在 onjoinplayer() 中将其识别为新玩家。 创建项目 > 创建 > zepeto > typescript,并将其重命名为 botplayermanager。 在 onaddedplayer() 中添加逻辑以创建每个玩家,并添加逻辑以区分机器人玩家并创建他们的 zepeto 角色。 botplayermanager ts 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() { &#x9;&#x9; &#x9; // 监听来自 `zepetoworldmultiplay` 组件的 `roomjoined` 事件。 this zepetoworldmultiplay roomjoined += (room room) => { this room = room; } &#x9; // 监听来自 `zepetoplayers instance` 的 `onaddedplayer` 事件以处理新添加的玩家。 zepetoplayers instance onaddedplayer addlistener((userid string) => { &#x9; // 使用 `userid` 从房间状态获取当前玩家数据。 const currentplayer = this room state players get item(userid); &#x9; // 检查玩家是否为机器人,如果是,则将其设置为机器人玩家。 if (currentplayer isbot) { this setbotplayer(currentplayer sessionid); } }); } } 2 2 编写 setbotplayer 函数,以将标签和同步组件添加到机器人玩家,并创建控制它们的脚本。 将 botmapdata 设置为以 map 格式保存机器人玩家数据,以管理机器人玩家。 botplayermanager ts // `setbotplayer()` 方法用于将玩家设置为机器人。 setbotplayer(userid string) { // 使用他们的 `userid` 获取与机器人玩家关联的 zepeto 角色。 const bot = zepetoplayers instance getplayer(userid) character; // 将角色的名称设置为 `userid` 以便识别。 bot gameobject name = userid; // 使用他们的 `userid` 作为键,将机器人玩家的数据存储在 ` botmapdata` 映射中。 &#x9; this botmapdata set(userid, bot); } 👍 提示 您可以向 setbotplayer() 添加额外的脚本或设置,以控制机器人玩家的行为。 步骤 3:在客户端创建一个机器人玩家按钮 对于需要一定数量玩家才能开始的世界,有时玩家数量不足,您必须等待很长时间才能开始游戏。 在这种情况下,您可以通过添加一个机器人玩家来启动世界。 3 1 在 index ts 中注册一个函数,当服务器接收到来自客户端的消息时执行 createbot()。 async oncreate() { &#x9; // 处理 "createbot" 消息,该消息使用给定的 userid 创建一个机器人玩家。 this onmessage("createbot", (client, message) => { this createbot(message); }); } 3 2 在客户端脚本 botplayermanager ts 中,编写一个函数将 "createbot" 消息发送到服务器。 执行函数的方法是通过按下按钮发送消息。 通过消息将要创建的机器人玩家的用户 id 作为字符串发送。 botplayermanager ts public buttoncreatebot button; public botplayerid string; start() {&#x9; &#x9; // 为 "创建机器人" 按钮添加点击监听器,以发送消息以创建一个机器人玩家。 &#x9; this buttoncreatebot onclick addlistener(() => { this room send("createbot", this botplayerid); }); } 3 3 现在,当你运行服务器和运行时,你可以看到当你按下按钮时会创建机器人玩家。 步骤 4:通过添加机器人玩家来启动世界 当没有足够的玩家来启动世界时,你可以添加机器人玩家来启动世界。 4 1 在服务器脚本中,在 onjoin 期间添加以下代码,以检查玩家数量,并在至少有四名玩家时启动世界。 在 createbot() 中添加一个函数以检查玩家数量。 在 startworld() 函数中添加一个计数器以记录游戏次数。 index ts async onjoin(client sandboxplayer) { // 在玩家加入后检查房间中的玩家数量。 &#x9;this checkplayernumber(); } // `checkplayernumber()` 方法检查房间中的玩家数量,并在至少有四名玩家时启动世界。 checkplayernumber() { &#x9;// 将当前房间中的玩家数量打印到控制台。 &#x9;console log(`玩家数量, ${this state players size}`); &#x9;// 如果房间中至少有四名玩家,则启动世界。 &#x9;if (this state players size >= 4) { this startworld(); &#x9;} } // `createbot()` 方法用于创建具有给定 `userid` 的机器人玩家。 createbot(userid string) { &#x9;// 使用提供的 `userid` 为机器人玩家生成会话 id。 &#x9;const sessionid = "bot " + userid; &#x9;// 检查是否已经存在具有相同会话 id 的机器人玩家。如果存在,则返回而不创建重复项。 if (this state players has(sessionid)) { &#x9;return; &#x9;} &#x9;// 为机器人玩家创建一个新的 `player` 对象。 &#x9;const player player = new player(); player sessionid = sessionid; &#x9;if (userid) { &#x9; player zepetouserid = userid; &#x9;} &#x9;player isbot = true; &#x9;// 使用会话 id 作为键将机器人玩家添加到状态的玩家映射中。 &#x9;this state players set(player sessionid, player); &#x9;this botmap set(sessionid, player); &#x9;// 在添加机器人玩家后检查房间中的玩家数量。 &#x9;this checkplayernumber(); } private playtime number = 0; // `startworld()` 方法增加游戏时间并向所有客户端广播 "startworld" 消息。 startworld() { &#x9; this playtime += 1; // 打印一条消息,指示世界的开始和当前的游戏时间。 console log("开始世界!"); this broadcast("startworld", this playtime); } 在服务器上,当真实玩家加入房间时,会执行 onjoin。因此,当通过 createbot 创建一个 bot 玩家时,以及当玩家通过 onjoin 进入时,checkplayernumber() 会增加人数。 4 2 在客户端脚本 botplayermanager ts 中,编写 startworld(),当从服务器接收到 startworld 消息时执行。 botplayermanager ts start() { &#x9;// 监听来自 `zepetoworldmultiplay` 组件的 `roomjoined` 事件。 &#x9;this zepetoworldmultiplay roomjoined += (room room) => { // 为 "startworld" 消息类型添加消息处理程序。 &#x9;this room addmessagehandler("startworld", (playtime number) => { this startworld(playtime); }); } // 当世界以提供的 `playtime` 启动时调用 `startworld()` 方法。 startworld(playtime number) { &#x9;// 打印世界启动消息以及 `playtime`。 console log(`开始世界 ${playtime}`); } 4 3 在运行时,当玩家数量超过 4 个(包括 bot 玩家)时,您可以在服务器控制台和客户端控制台上看到名为 world start 的日志。 步骤 5:同步 bot 玩家位置 以下是示例代码,它将添加的机器人玩家移动到本地玩家的位置并同步移动位置。 5 1 首先,编写代码以在服务器的 index ts 中接收到来自客户端的消息时移动机器人玩家, movebot 。 async oncreate() { // 处理 "movebot" 消息,该消息将机器人玩家移动到指定位置。 &#x9;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 } // 将 "movebottoposition" 消息广播给所有客户端,消息数据作为 json 字符串。 this broadcast("movebottoposition", json stringify(newmessage)); } 5 2 在客户端脚本 botplayermanager ts 中,编写 sendbotposition(),当按下 buttoncallbot 时将本地玩家位置发送到服务器。 然后编写代码,当从服务器接收到消息 movebottoposition 时,将所有 bot 玩家移动到消息中包含的位置。 botplayermanager ts public buttoncallbot button; start(){ &#x9;// 监听来自 `zepetoworldmultiplay` 组件的 `roomjoined` 事件。 this zepetoworldmultiplay roomjoined += (room room) => { this room = room; // 为 "startworld" 消息类型添加消息处理程序。 this room addmessagehandler("startworld", (playtime number) => { this startworld(playtime); }); &#x9;// 为 "call bot" 按钮添加点击监听器,以发送消息以发送 bot 玩家位置。 &#x9;this buttoncallbot onclick addlistener(() => { &#x9; this sendbotposition(); &#x9;}); } // 此方法将本地玩家角色的位置发送到服务器以进行 bot 移动同步。 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` 映射中的每个 bot 角色移动到指定位置,并在 x 和 z 轴上添加一个小的随机偏移。 this botmapdata foreach((character zepetocharacter) => { // 使用 `movetoposition()` 方法将角色移动到指定位置。 // 在这里,向目标位置添加一个小的随机偏移,以为 bot 创建自然的移动效果。 character movetoposition(position + new vector3(random range(0 5, 1), 0, random range(0 5, 1))); }); } 5 3 现在,如果您在运行时创建一个 bot 玩家并按下 buttoncallbot 按钮,您应该会看到创建的 bot 玩家移动到本地玩家角色的位置。 botplayermanager ts 完整代码 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) { // 使用其 `userid` 获取与机器人玩家相关联的 zepeto 角色。 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` 映射中的每个机器人角色移动到指定位置,并在 x 和 z 轴上添加一个小的随机偏移。 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) { // 当房间对象被创建时调用。 // 处理房间对象的状态或数据初始化。 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; } }); // 处理 "createbot" 消息,该消息使用给定的 userid 创建一个机器人玩家。 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()` 方法检查房间中的玩家数量,并在至少有四名玩家时启动世界。 checkplayernumber() { // 将房间中当前的玩家数量打印到控制台。 console log(`玩家数量, ${this state players size}`); // 如果房间中至少有四名玩家,则启动世界。 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 } // 将 "movebottoposition" 消息广播给所有客户端,消息数据作为 json 字符串。 this broadcast("movebottoposition", json stringify(newmessage)); } async onjoin(client sandboxplayer) { // 创建在 schemas json 中定义的玩家对象并设置初始值。 console log(`\[onjoin] 会话 id ${client sessionid}, 哈希码 ${client hashcode}, 用户 id ${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] 加载进入玩家的数据存储 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 管理玩家对象,这是客户端对象的唯一键值。 // 客户端可以通过将 add onadd 事件添加到玩家对象来检查通过 set 添加的玩家对象的信息。 this state players set(client sessionid, player); // 在玩家加入后检查房间中的玩家数量。 this checkplayernumber(); } ontick(deltatime number) void { // 它在服务器中每次设置时间时重复调用,并且可以使用 deltatime 管理某个间隔事件。 } async onleave(client sandboxplayer, consented? boolean) { // 通过设置 allowreconnection,可以保持电路的连接,但在基本指南中立即清理。 // 客户端可以通过将 add onremove 事件添加到玩家对象来检查已删除的玩家对象的信息。 this state players delete(client sessionid); } }