ฉันกำลังทำงานในเกม 2D แบบสามมิติที่มีผู้เล่นหลายคนในระดับปานกลางโดยมีผู้เล่นประมาณ 20-30 คนเชื่อมต่อพร้อมกันกับเซิร์ฟเวอร์ถาวร ฉันมีปัญหาในการใช้การทำนายการเคลื่อนไหวที่ดี
ฟิสิกส์ / การเคลื่อนไหว
เกมดังกล่าวไม่มีการนำฟิสิกส์มาใช้อย่างแท้จริง แต่ใช้หลักการพื้นฐานเพื่อทำให้เกิดการเคลื่อนไหว แทนที่การป้อนข้อมูลแบบโพลอย่างต่อเนื่องการเปลี่ยนแปลงสถานะ (เช่น / เมาส์ลง / ขึ้น / ย้ายเหตุการณ์) ใช้เพื่อเปลี่ยนสถานะของเอนทิตีอักขระที่ผู้เล่นควบคุม ทิศทางของผู้เล่น (เช่น / ตะวันออกเฉียงเหนือ) รวมกับความเร็วคงที่และเปลี่ยนเป็นเวกเตอร์ 3 มิติที่แท้จริง - ความเร็วของเอนทิตี
ในลูปเกมหลัก "อัปเดต" จะถูกเรียกก่อน "วาด" ตรรกะการอัปเดตจะทำให้เกิด "ภารกิจการอัปเดตทางฟิสิกส์" ที่ติดตามเอนทิตีทั้งหมดด้วยความเร็วที่ไม่เป็นศูนย์ใช้การรวมขั้นพื้นฐานมากเพื่อเปลี่ยนตำแหน่งเอนทิตี ตัวอย่างเช่น: entity.Position + = entity.Velocity.Scale (ElapsedTime.Seconds) (โดยที่ "วินาที" เป็นค่าจุดลอยตัว แต่วิธีการเดียวกันจะใช้งานได้กับค่าจำนวนเต็มมิลลิวินาที)
จุดสำคัญคือไม่ต้องใช้การแก้ไขสำหรับการเคลื่อนไหว - เครื่องยนต์ฟิสิกส์พื้นฐานไม่มีแนวคิดของ "สถานะก่อนหน้า" หรือ "สถานะปัจจุบัน" เพียงตำแหน่งและความเร็ว
การเปลี่ยนสถานะและแพ็คเก็ตอัพเดท
เมื่อความเร็วของเอนทิตีของตัวละครที่ผู้เล่นกำลังควบคุมการเปลี่ยนแปลงแพ็คเก็ต "ย้ายอวตาร" จะถูกส่งไปยังเซิร์ฟเวอร์ที่มีประเภทการกระทำของเอนทิตี (ยืนเดินวิ่ง) ทิศทาง (ตะวันออกเฉียงเหนือ) และตำแหน่งปัจจุบัน สิ่งนี้แตกต่างจากการทำงานของเกมสามคนแรก ในเกม 3D ความเร็ว (ทิศทาง) สามารถเปลี่ยนเฟรมเป็นเฟรมขณะที่ผู้เล่นเคลื่อนที่ การส่งทุกการเปลี่ยนแปลงสถานะจะส่งแพ็กเก็ตต่อเฟรมได้อย่างมีประสิทธิภาพซึ่งจะแพงเกินไป แต่เกม 3D ดูเหมือนจะเพิกเฉยต่อการเปลี่ยนแปลงสถานะและส่งแพ็กเก็ต "state update" ในช่วงเวลาคงที่ - พูดทุก ๆ 80-150ms
เนื่องจากการอัปเดตความเร็วและทิศทางเกิดขึ้นบ่อยครั้งมากในเกมของฉันฉันจึงสามารถออกไปพร้อมกับส่งการเปลี่ยนแปลงทุกสถานะได้ แม้ว่าการจำลองทางฟิสิกส์ทั้งหมดจะเกิดขึ้นที่ความเร็วเดียวกันและแน่นอน แต่ความหน่วงแฝงยังคงเป็นปัญหาอยู่ ด้วยเหตุนี้ฉันส่งแพ็คเก็ตอัปเดตตำแหน่งประจำ (คล้ายกับเกม 3 มิติ) แต่บ่อยครั้งมากน้อยลง - ตอนนี้ทุก ๆ 250ms แต่ฉันสงสัยว่าด้วยการทำนายที่ดี ปัญหาที่ใหญ่ที่สุดคือตอนนี้ฉันเบี่ยงเบนจากบรรทัดฐาน - เอกสารอื่น ๆ คู่มือและตัวอย่างออนไลน์ทั้งหมดส่งอัปเดตตามปกติและแก้ไขระหว่างรัฐทั้งสอง ดูเหมือนว่าเข้ากันไม่ได้กับสถาปัตยกรรมของฉันและฉันต้องการอัลกอริธึมการทำนายการเคลื่อนไหวที่ดีกว่าซึ่งใกล้เคียงกับสถาปัตยกรรม "ฟิสิกส์เครือข่าย" (ใกล้มาก)
จากนั้นเซิร์ฟเวอร์จะได้รับแพ็คเก็ตและกำหนดความเร็วของผู้เล่นจากประเภทการเคลื่อนไหวตามสคริปต์ (ผู้เล่นสามารถทำงานได้หรือไม่รับความเร็วในการเล่นของผู้เล่น) เมื่อมีความเร็วมันจะรวมเข้ากับทิศทางเพื่อให้ได้เวกเตอร์ - ความเร็วของเอนทิตี การตรวจจับการโกงบางอย่างและการตรวจสอบเบื้องต้นเกิดขึ้นและเอนทิตีทางฝั่งเซิร์ฟเวอร์จะได้รับการอัปเดตด้วยความเร็วทิศทางและตำแหน่งปัจจุบัน การควบคุมปริมาณพื้นฐานจะดำเนินการเพื่อป้องกันผู้เล่นไม่ให้เซิร์ฟเวอร์ร้องขอการเคลื่อนไหว
หลังจากอัพเดตเอนทิตีของตัวเองแล้วเซิร์ฟเวอร์จะออกอากาศแพ็กเก็ต "การอัพเดตตำแหน่งอวาตาร์" ให้กับผู้เล่นคนอื่น ๆ ที่อยู่ในระยะ แพ็กเก็ตการอัพเดทตำแหน่งจะใช้ในการอัพเดทการจำลองทางฟิสิกส์ฝั่งไคลเอ็นต์ (สถานะโลก) ของไคลเอนต์ระยะไกลและทำการคาดการณ์และชดเชยความล่าช้า
การทำนายและการชดเชยความล่าช้า
ดังกล่าวข้างต้นลูกค้ามีอำนาจสำหรับตำแหน่งของตนเอง ยกเว้นในกรณีของการโกงหรือความผิดปกติ Avatar ของลูกค้าจะไม่ถูกปรับตำแหน่งโดยเซิร์ฟเวอร์ ไม่มีการคาดการณ์ ( "ย้ายในขณะนี้และต่อมาถูกต้อง") เป็นสิ่งจำเป็นสำหรับ avatar ของลูกค้า - สิ่งที่ผู้เล่นเห็นเป็นที่ถูกต้อง อย่างไรก็ตามจำเป็นต้องมีการคาดการณ์หรือการแก้ไขบางอย่างสำหรับเอนทิตีระยะไกลทั้งหมดที่กำลังเคลื่อนที่ การคาดการณ์และ / หรือการชดเชยความล่าช้าบางอย่างจำเป็นต้องมีอย่างชัดเจนภายในเครื่องจำลอง / ฟิสิกส์ของลูกค้า
ปัญหาที่เกิดขึ้น
ฉันดิ้นรนกับอัลกอริทึมที่หลากหลายและมีคำถามและปัญหามากมาย:
ฉันควรคาดการณ์สอดแทรกหรือทั้งสองอย่าง "ความรู้สึกลำไส้" ของฉันคือฉันควรใช้การคาดการณ์ที่บริสุทธิ์โดยยึดตามความเร็ว ลูกค้าจะได้รับการเปลี่ยนแปลงสถานะลูกค้าจะคำนวณความเร็ว "คาดการณ์" ที่ชดเชยความล่าช้าและระบบฟิสิกส์ปกติจะจัดการส่วนที่เหลือ อย่างไรก็ตามมันรู้สึกขัดแย้งกับโค้ดตัวอย่างและบทความอื่น ๆ ทั้งหมด - พวกเขาดูเหมือนจะเก็บหมายเลขของรัฐและทำการแก้ไขโดยไม่ต้องใช้กลไกทางฟิสิกส์
เมื่อแพ็กเก็ตมาถึงฉันได้ลอง interpolating ตำแหน่งของแพ็คเก็ตด้วยความเร็วของแพ็คเก็ตในช่วงเวลาที่แน่นอน (พูด 200ms) จากนั้นฉันก็นำความแตกต่างระหว่างตำแหน่งที่สอดแทรกและตำแหน่ง "ข้อผิดพลาด" ปัจจุบันเพื่อคำนวณเวกเตอร์ใหม่และวางบนเอนทิตีแทนที่จะเป็นความเร็วที่ถูกส่ง อย่างไรก็ตามข้อสันนิษฐานคือแพ็กเก็ตอื่นจะมาถึงในช่วงเวลานั้นและเป็นเรื่องยากที่จะ "คาดเดา" เมื่อแพ็กเก็ตถัดไปจะมาถึง แนวคิดมีข้อบกพร่องพื้นฐานหรือไม่ถูกต้อง แต่ต้องการการแก้ไข / ปรับเปลี่ยนบางอย่าง?
จะเกิดอะไรขึ้นเมื่อผู้เล่นระยะไกลหยุด ฉันสามารถหยุดเอนทิตีได้ทันที แต่จะถูกวางในตำแหน่ง "ผิด" จนกว่ามันจะเคลื่อนไหวอีกครั้ง ถ้าฉันประมาณเวกเตอร์หรือพยายามที่จะแก้ไขฉันมีปัญหาเพราะฉันไม่ได้เก็บสถานะก่อนหน้า - เครื่องมือฟิสิกส์ไม่มีทางที่จะพูดว่า "คุณต้องหยุดหลังจากที่คุณไปถึงตำแหน่ง X" มันเข้าใจความเร็วเพียงแค่ไม่มีอะไรซับซ้อนมากขึ้น ฉันลังเลที่จะเพิ่มข้อมูล "สถานะการเคลื่อนไหวของแพ็คเก็ต" ลงในเอนทิตีหรือเอนจินฟิสิกส์เนื่องจากมันละเมิดหลักการออกแบบขั้นพื้นฐานและทำให้รหัสเครือข่ายตกไปในเอ็นจิ้นเกมอื่น
จะเกิดอะไรขึ้นเมื่อเอนทิตีชน มีสามสถานการณ์ - ผู้เล่นที่ควบคุมชนกันภายใน, สองเอนทิตีชนกันบนเซิร์ฟเวอร์ในระหว่างการอัพเดทตำแหน่งหรือการอัพเดทเอนทิตีระยะไกลชนกันบนไคลเอนต์โลคอล ในทุกกรณีฉันไม่แน่ใจว่าจะจัดการการปะทะกันอย่างไรนอกเหนือจากการโกงทั้งสองสถานะนั้น "ถูกต้อง" แต่ในช่วงเวลาที่ต่างกัน ในกรณีของเอนทิตีระยะไกลมันไม่สมเหตุสมผลที่จะวาดมันเดินผ่านกำแพงดังนั้นฉันจึงทำการตรวจจับการชนกันของไคลเอนต์ในพื้นที่และทำให้ "หยุด" จากจุดที่ # 2 ข้างต้นฉันอาจคำนวณ "เวกเตอร์ที่ถูกแก้ไข" ที่พยายามย้ายเอนทิตี "ผ่านกำแพง" อย่างต่อเนื่องซึ่งจะไม่ประสบความสำเร็จ - รีโมตอวาตาร์ติดอยู่ที่นั่นจนกว่าข้อผิดพลาดจะสูงเกินไป ตำแหน่ง. เกมทำงานด้วยวิธีนี้ได้อย่างไร