สร้างโลกของคุณเอง
ผู้เล่นและตัวละคร: ข้อมูลเบื้อ...
ผู้เล่น ZEPETO
18min
zepetoplayers เป็นคลาสผู้จัดการ (singleton) ที่ออกแบบมาเพื่อควบคุมทั้ง zepeto player และ zepeto character โดยการเพิ่ม zepetoplayers ลงในฉาก คุณสามารถสร้าง ลบ และจัดการ zepeto player ได้ zepetoplayer แทนตัวอย่างเฉพาะของตัวละคร zepeto ที่ใช้ในการจัดการทั้งผู้เล่นที่คุณควบคุมโดยตรงในโลกหลายผู้เล่นและผู้เล่นคนอื่น ๆ ทุก zepetoplayer ที่สร้างขึ้นในโลกหลายผู้เล่นจะถูกกำหนดให้มี id เซสชันที่ไม่ซ้ำกันและจะถูกจัดการโดยใช้ id เซสชันนี้ เนื่องจาก zepetoplayer มีคุณสมบัติของ zepetocharacter มันจึงสามารถควบคุมได้โดยใช้ฟังก์ชันที่เกี่ยวข้องกับ zepetocharacter มี zepetoplayer สามประเภทคือ zepetoplayer คำอธิบาย ผู้เล่นท้องถิ่น แสดงถึงอินสแตนซ์ตัวละคร zepeto ที่ถูกควบคุมโดยผู้ใช้ท้องถิ่นโดยตรง。 \ ติดตั้งด้วย character controller/zepeto camera components ผู้เล่นเครือข่าย (ผู้เล่นระยะไกล) อินสแตนซ์ตัวละคร zepeto ที่สามารถโหลดและใช้งานในเนื้อหาหลายผู้เล่น。 \ ไม่มีการติดตั้ง character controller/zepeto camera components ผู้เล่นบอท อินสแตนซ์ตัวละคร zepeto สำหรับเนื้อหาหลายผู้เล่น แต่ถูกควบคุมโดยบอทแทนที่จะเป็นผู้ใช้จริง。 ใช้เป็นตัวแทนเมื่อเริ่มโลกหลายผู้เล่นที่มีผู้เล่นไม่เพียงพอหรือหากผู้เล่นออกจากเกมระหว่างเล่น。 \ ไม่มีการติดตั้ง character controller/zepeto camera components โปรดดูคู่มือต่อไปนี้ \[ ผู้เล่น zepeto docid\ xjzgmlq9 z2w8msgxqdl0 ] \[ ไกด์การสร้างผู้เล่นบอท docid\ rheab2yzn4tlfo r7rdpt ] zepetocharacter เป็นหน่วยตัวอย่างพื้นฐานของตัวละคร zepeto ที่สามารถโหลดและควบคุมใน world scene ได้ zepetocharacter มีลักษณะเป็นอวตารที่สร้างขึ้นผ่านแอป zepeto กรุณาอ้างอิงไปยังคู่มือต่อไปนี้ \[ ตัวละคร zepeto docid\ msdx6mzfjeub38vbdzbjn ] การเพิ่ม zepetoplayers ในหน้าต่าง hierarchy ให้เลือก zepeto → แท็บ zepetoplayers คุณสามารถเพิ่มมันลงในฉากของคุณได้แล้ว โปรดทราบว่าการเพิ่ม zepetoplayers เพียงอย่างเดียวจะไม่ทำให้ zepeto player เข้ามาในฉาก คุณต้องดำเนินการเขียนสคริปต์โดยใช้ api การสร้างตัวละครของ zepetoplayers หากคุณต้องการสร้างและลองใช้ local player ในฉากอย่างรวดเร็ว โปรดดูคู่มือ โปรดดูคู่มือดังต่อไปนี้ \[ การสร้างตัวละคร zepeto docid\ mrnk9t5xnrdqcvmsgfgpk ] สำหรับตัวอย่างวิธีการแสดงชื่อและรูปโปรไฟล์ของผู้เล่น zepeto โปรดดูที่คู่มือ โปรดดูที่คู่มือต่อไปนี้ \[ ข้อมูลผู้ใช้ docid\ cl33fgnsabafjr7nprfyi ] api ผู้เล่น zepeto หากคุณสนใจใน api ของ zepetoplayers โปรดดูที่เอกสาร โปรดดูที่คู่มือต่อไปนี้ \[ api zepeto character controller ] คู่มือนี้ครอบคลุมตัวอย่างการใช้ผู้เล่น zepeto ในสถานการณ์หลายผู้เล่นเป็นหลัก การซิงโครไนซ์ตำแหน่งหลายผู้เล่นโดยใช้ผู้เล่น zepeto ในโลกผู้เล่นคนเดียว จะต้องสร้างผู้เล่นท้องถิ่นเพียงคนเดียว และเนื่องจากมีเพียงผู้เล่นท้องถิ่นที่ปรากฏบนหน้าจอ การซิงโครไนซ์จึงไม่จำเป็น อย่างไรก็ตาม ในโลกหลายผู้เล่น ไม่เพียงแต่ผู้เล่นท้องถิ่นที่คุณควบคุมโดยตรง แต่ยังมีผู้เล่นอื่น ๆ ที่เรียกว่าผู้เล่นเครือข่าย ต้องแสดงบนหน้าจอด้วย ทุกการกระทำของผู้เล่นเครือข่าย การเคลื่อนที่, การกระโดด, และการทำท่าทางเฉพาะ ควรปรากฏเหมือนกันบนหน้าจอของคุณ กระบวนการนี้เรียกว่าการซิงโครไนซ์ หากไม่ดำเนินการตามสคริปต์การซิงโครไนซ์ คุณจะไม่สามารถเห็นการปรากฏตัวหรือการเคลื่อนไหวของผู้เล่นในเครือข่ายได้ ในโลกที่มีการเล่นหลายคนโดยไม่มีการซิงโครไนซ์ วิธีเดียวที่จะรู้ว่าลูกค้าอื่นได้เข้ามาในห้องคือผ่านปุ่มโฮม ขั้นตอนที่ 1 การตั้งค่าสภาพแวดล้อมการเล่นหลายคน แนะนำให้เริ่มต้นด้วยการเข้าใจการตั้งค่าและแนวคิดพื้นฐานของการเล่นหลายคนผ่านวิดีโอสอนการเล่นหลายคน กรุณาอ้างอิงไปยังคู่มือต่อไปนี้ \[ บทเรียนการเล่นหลายคน docid\ bfzi crxt7x2ujbo1znog ] \[ มัลติเพลย์ docid kqgjbfq 6znt awbmnac ] ขั้นตอนที่ 2 แสดงผู้เล่นคนอื่นบนหน้าจอของคุณ ผู้เล่นท้องถิ่นของคุณจะถูกมองว่าเป็นผู้เล่นในเครือข่ายบนอุปกรณ์ของคนอื่น หมายความว่าผู้เล่นท้องถิ่นของคุณต้องส่งข้อมูลไปยังเซิร์ฟเวอร์เพื่อการซิงโครไนซ์ ผู้เล่นทั้งหมดที่เชื่อมต่อกับโลกการเล่นหลายคนควรแชร์ข้อมูลของพวกเขา ลูกค้าทั้งหมดที่เชื่อมต่อกับห้องเล่นหลายคนจะแชร์ข้อมูลสถานะของห้องเล่นหลายคน ข้อมูลสถานะของห้องนี้จะปฏิบัติตามสคีมาที่กำหนดใน schemas json (โปรดพิจารณา schema เป็นโครงสร้างข้อมูล) ในคู่มือนี้ คุณจะซิงโครไนซ์ตำแหน่งของผู้เล่นผ่านข้อมูลสถานะห้อง ดังนั้น ให้กำหนด schema ใน 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\ blqqvdrfy2e3przado dp ] 👍 เคล็ดลับ เก็บข้อมูลทั้งหมดที่เซิร์ฟเวอร์และลูกค้าทั้งหมดควรแชร์ใน สถานะห้องหลายผู้เล่น บันทึกข้อมูลเฉพาะสำหรับผู้เล่นแต่ละคน เช่น ระดับ, คะแนนประสบการณ์, คะแนน, ฯลฯ โดยใช้ datastorage สคริปต์เซิร์ฟเวอร์สามารถรับรู้เมื่อผู้เล่นคนอื่นเข้ามาในห้องและสามารถส่งข้อมูลนั้นไปยังไคลเอนต์เพื่อโหลดผู้เล่นคนนั้นเข้าสู่ฉากได้ ขั้นตอน 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; } // จัดการวัตถุผู้เล่นโดยใช้ sessionid ซึ่งเป็นค่าคีย์ที่ไม่ซ้ำกันของวัตถุไคลเอนต์ // ไคลเอนต์สามารถตรวจสอบข้อมูลเกี่ยวกับวัตถุผู้เล่นที่เพิ่มโดยการตั้งค่าโดยการเพิ่มเหตุการณ์ 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] ผู้เล่น 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] ผู้เล่น sessionid ${sessionid}`); this currentplayers delete(sessionid); zepetoplayers instance removeplayer(sessionid); } } ตอนนี้ เมื่อผู้เล่นเข้ามา คุณสามารถยืนยันได้ว่าตัวละคร zepeto ถูกสร้างขึ้นบนหน้าจอของคุณแล้ว แต่การเคลื่อนไหวของผู้เล่นยังไม่สะท้อนบนหน้าจอ เรามาดำเนินการซิงโครไนซ์ตำแหน่งกันต่อไป ขั้นตอนที่ 3 การซิงโครไนซ์โดยการดึงข้อมูลของผู้เล่นคนอื่น สำหรับการซิงโครไนซ์ ทุกครั้งที่ผู้เล่นเคลื่อนไหวหรือทำการใดๆ พวกเขาต้องส่งการเปลี่ยนแปลงสถานะของพวกเขาไปยังเซิร์ฟเวอร์ การส่งข้อความที่มีการเปลี่ยนแปลงสถานะของพวกเขาไปยังเซิร์ฟเวอร์จะทำผ่านการสื่อสารข้อความในห้อง เมื่อเซิร์ฟเวอร์ได้รับข้อความเกี่ยวกับการเปลี่ยนแปลงสถานะของผู้เล่น มันจะอัปเดตสถานะห้อง กรุณาอ้างอิงไปยังคู่มือต่อไปนี้ \[ ข้อความในห้องเล่นหลายคน docid 80px4fctallbpg9v1pncx ] ตัวอย่างเช่น สมมติว่าผู้เล่นท้องถิ่นของคุณชื่อ b เมื่อผู้เล่นเครือข่าย a เข้าร่วมห้อง พวกเขาจะถูกสร้างขึ้นที่พิกัด x 0, y 0, z 0 หากผู้เล่น a ย้ายไปที่ตำแหน่ง x 10, y 0, z 0, b ไม่มีวิธีการที่แท้จริงในการรู้ว่า a กำลังเคลื่อนที่อยู่ นี่เป็นเพราะทั้งสองคนกำลังทำงานในท้องถิ่นบนอุปกรณ์ที่แยกจากกัน ดังนั้นจึงอยู่บนลูกค้าที่แยกจากกัน ดังนั้น บุคคลที่ควบคุม a จำเป็นต้องสื่อสารการเคลื่อนไหวของพวกเขาไปยังเซิร์ฟเวอร์ผ่านข้อความในห้อง เมื่อเซิร์ฟเวอร์ได้รับข้อมูลนี้ มันจะแจ้งให้ทุกคนที่อยู่ในห้องทราบเกี่ยวกับตำแหน่งจริงของ a แบบเรียลไทม์ ด้วยวิธีนี้ b จะทราบว่า a กำลังเคลื่อนที่ เพื่อให้เซิร์ฟเวอร์แจ้งผู้เล่นคนอื่น มีสองวิธีคือ ใช้การกระจายข้อความในห้อง อัปเดตสถานะห้อง จากนั้นให้ลูกค้าดึงและใช้สถานะห้อง คู่มือนี้ใช้วิธีที่สองในการอัปเดตสถานะห้อง เนื่องจากผู้เล่น 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; } // จัดการวัตถุผู้เล่นโดยใช้ sessionid ซึ่งเป็นค่าคีย์ที่ไม่ซ้ำกันของวัตถุไคลเอนต์ // ไคลเอนต์สามารถตรวจสอบข้อมูลเกี่ยวกับวัตถุผู้เล่นที่เพิ่มโดยการตั้งค่า add onadd event ไปยังวัตถุผู้เล่น this state players set(client sessionid, player); } async onleave(client sandboxplayer, consented? boolean) { // โดยการตั้งค่า allowreconnection จะสามารถรักษาการเชื่อมต่อสำหรับวงจรได้ แต่จะทำความสะอาดทันทีในคู่มือพื้นฐาน // ไคลเอนต์สามารถตรวจสอบข้อมูลเกี่ยวกับวัตถุผู้เล่นที่ถูกลบโดยการตั้งค่า add onremove event ไปยังวัตถุผู้เล่น 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)); } // ส่งการแปลงตัวละครท้องถิ่นไปยังเซิร์ฟเวอร์ที่เวลาที่กำหนด 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) { // เมื่อได้รับเหตุการณ์ 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); // \[roomstate] ถูกเรียกเมื่อใดก็ตามที่สถานะของอินสแตนซ์ผู้เล่นถูกอัปเดต 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] สร้างอินสแตนซ์ผู้เล่นสำหรับผู้เล่นที่เข้าห้อง 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 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 ); } } 👍 เคล็ดลับ คู่มือนี้ใช้การซิงโครไนซ์ตำแหน่งเท่านั้น การซิงโครไนซ์ท่าทาง การซิงโครไนซ์วัตถุ ฯลฯ ยังไม่ได้ดำเนินการ หลักการเหมือนกันสำหรับทุกอย่าง แต่กระบวนการส่งและรับข้อความในห้องในทุกช่วงเวลาที่จำเป็นนั้นเป็นสิ่งจำเป็น หากคุณต้องการดำเนินการซิงโครไนซ์ได้สะดวกยิ่งขึ้น ให้พิจารณาใช้โมดูลซิงโครไนซ์แบบหลายผู้เล่น