CREATE YOUR WORLD
Players & Characters : Advance...

Bot 플레이어 생성 가이드

16min
봇 플레이어는 멀티플레이어 월드 를 시작할 충분한 사람이 없거나, 플레이어가 월드 중에 나갈 때 채우기 위해 사용됩니다 봇 플레이어의 행동 방식은 각 콘텐츠에 맞게 구현되어야 합니다 이 가이드는 봇 플레이어를 만드는 일반적인 방법을 설명합니다 📘 봇 플레이어 생성 가이드는 멀티플레이어 가이드를 기반으로 합니다 \[ 멀티플레이 제작하기 docid\ s6aoernolpac9cyqi onr ] 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 함수를 작성하여 봇 플레이어에 태그와 동기화 구성 요소를 추가하고 이를 제어하는 스크립트를 생성합니다 봇 플레이어 데이터를 관리하기 위해 map 형식으로 botmapdata를 설정하여 저장합니다 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단계 클라이언트에 봇 플레이어 버튼 생성 시작하기 위해 특정 수의 플레이어가 필요한 월드의 경우, 때때로 플레이어가 충분하지 않아 월드가 시작되기까지 오랜 시간을 기다려야 합니다 이 경우, bot 플레이어를 추가하여 월드 를 시작할 수 있습니다 3 1 index ts에서 서버가 클라이언트로부터 메시지를 받을 때 createbot()을 실행하도록 함수를 등록합니다 async oncreate() { &#x9; // 주어진 userid로 봇 플레이어를 생성하는 "createbot" 메시지를 처리합니다 this onmessage("createbot", (client, message) => { this createbot(message); }); } 3 2 클라이언트 스크립트 botplayermanager ts에서 서버에 "createbot" 메시지를 보내는 함수를 작성합니다 함수를 실행하는 방법은 버튼을 눌러 메시지를 보내는 것입니다 생성할 bot 플레이어의 사용자 id를 문자열로 메시지를 통해 보냅니다 botplayermanager ts public buttoncreatebot button; public botplayerid string; start() {&#x9; &#x9; // "create bot" 버튼에 클릭 리스너를 추가하여 봇 플레이어를 생성하는 메시지를 보냅니다 &#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("startworld!"); this broadcast("startworld", this playtime); } 서버에서 실제 플레이어가 방에 들어오면 onjoin이 실행됩니다 따라서 createbot을 통해 봇 플레이어가 생성되고 플레이어가 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(`start world ${playtime}`); } 4 3 실행 중에 봇 플레이어를 포함하여 4명 이상의 플레이어가 있을 경우, 서버 콘솔과 클라이언트 콘솔에 'world start'라는 로그가 표시됩니다 5단계 봇 플레이어 위치 동기화 아래는 추가된 봇 플레이어를 로컬 플레이어 위치로 이동시키고 이동 위치를 동기화하는 샘플 코드입니다 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 } // 새로운 메시지 데이터를 json 문자열로 모든 클라이언트에 "movebottoposition" 메시지를 브로드캐스트합니다 this broadcast("movebottoposition", json stringify(newmessage)); } 5 2 클라이언트 스크립트인 botplayermanager ts에서 buttoncallbot이 눌릴 때 로컬 플레이어 위치를 서버로 전송하는 sendbotposition()을 작성합니다 그런 다음 서버에서 movebottoposition 메시지를 수신할 때 모든 봇 플레이어를 메시지에 포함된 위치 정보로 이동시키는 코드를 작성하십시오 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" 버튼에 클릭 리스너를 추가하여 봇 플레이어 위치를 전송하는 메시지를 보냅니다 &#x9;this buttoncallbot onclick addlistener(() => { &#x9; this sendbotposition(); &#x9;}); } // 이 메서드는 로컬 플레이어의 캐릭터 위치를 서버에 전송하여 봇 이동 동기화를 수행합니다 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 전체 코드 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 { // public properties to reference necessary components and settings from the inspector public zepetoworldmultiplay zepetoworldmultiplay; public buttoncreatebot button; public buttoncallbot button; public botplayerid string; // private variables to store the current room and bot player data private room room; private botmapdata map\<string, zepetocharacter> = new map\<string, zepetocharacter>(); start() { // listen for the `roomjoined` event from the `zepetoworldmultiplay` component this zepetoworldmultiplay roomjoined += (room room) => { this room = room; // add a message handler for the "startworld" message type this room addmessagehandler("startworld", (playtime number) => { this startworld(playtime); }); } // listen for the `onaddedplayer` event from `zepetoplayers instance` to handle newly added players zepetoplayers instance onaddedplayer addlistener((userid string) => { // get the current player data from the room state using the `userid` const currentplayer = this room state players get item(userid); // check if the player is a bot, and if so, set them as a bot player if (currentplayer isbot) { this setbotplayer(currentplayer sessionid); } }); // add a click listener to the "create bot" button to send a message to create a bot player 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); }); } // add a click listener to the "call bot" button to send a message to send a bot player position this buttoncallbot onclick addlistener(() => { this sendbotposition(); }); } // the `setbotplayer()` method is used to set a player as a bot setbotplayer(userid string) { // get the zepeto character associated with the bot player using their `userid` const bot = zepetoplayers instance getplayer(userid) character; // set the name of the character to the `userid` for identification bot gameobject name = userid; // store the bot player's data in the ` botmapdata` map using their `userid` as the key this botmapdata set(userid, bot); } // the `startworld()` method is called when the world starts with the provided `playtime` startworld(playtime number) { // print the world start message along with the `playtime` console log(`start world ${playtime}`); } // this method sends the position of the local player's character to the server for bot movement synchronization sendbotposition() { // get the position of the local player's character const localplayerposition = zepetoplayers instance localplayer zepetoplayer character transform position; // create a position object containing the x, y, and z coordinates of the local player's character const position = { x localplayerposition x, y localplayerposition y, z localplayerposition z } // convert the position object to a json string and send it to the server with the "movebot" message type this room send("movebot", json stringify(position)); } // this method is called when the server receives the "movebot" message from a client and moves the bot characters to the specified position movebottoposition(message) { // parse the json message received from the client to extract the position information const jsonmessage = json parse(message); const position = new vector3(jsonmessage positionx, jsonmessage positiony, jsonmessage positionz); // move each bot character in the ` botmapdata` map to the specified position with a small random offset on the x and z axes this botmapdata foreach((character zepetocharacter) => { // the `movetoposition()` method is used to move the character to the specified position // here, a small random offset is added to the target position to create a natural looking movement for the bots 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(`player number, ${this state players size}`); // 방에 최소 4명의 플레이어가 있으면 월드를 시작합니다 if (this state players size >= 4) { this startworld(); } } // `startworld()` 메서드는 플레이 시간을 증가시키고 "startworld" 메시지를 모든 클라이언트에 방송합니다 startworld() { this playtime += 1; // 월드의 시작과 현재 플레이 시간을 나타내는 메시지를 출력합니다 console log("start world!"); 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를 사용하여 플레이어 객체를 관리합니다 이는 클라이언트 객체의 고유 키 값입니다 // 클라이언트는 플레이어 객체에 추가된 정보를 확인할 수 있습니다 this state players set(client sessionid, player); // 플레이어가 입장한 후 방의 플레이어 수를 확인합니다 this checkplayernumber(); } ontick(deltatime number) void { // 서버에서 설정된 시간마다 반복적으로 호출되며, 특정 간격 이벤트를 deltatime을 사용하여 관리할 수 있습니다 } async onleave(client sandboxplayer, consented? boolean) { // allowreconnection을 설정하면 회로를 위해 연결을 유지할 수 있지만, 기본 가이드에서는 즉시 정리합니다 // 클라이언트는 삭제된 플레이어 객체에 대한 정보를 확인할 수 있습니다 this state players delete(client sessionid); } }