Multiplayer platformer - ปกติแล้วการแก้ไขเซิร์ฟเวอร์จะต้องใช้กับไคลเอนต์เดียวบนเซิร์ฟเวอร์หรือไม่?


10

ขณะนี้ฉันกำลังทำงานบน platformer ที่มีผู้เล่นหลายคนค่อนข้างง่าย ฉันอ่านบทความเกี่ยวกับเทคนิคต่าง ๆ ที่ใช้ในการซ่อนเวลาแฝงอยู่ แต่ฉันก็ยังไม่เข้าใจแนวคิดบางอย่าง ฉันพบว่าหัวข้อน่าสนใจมากและต้องการลองใช้ความคิดด้วยตัวเอง แต่ฉันคิดว่าการขอแลกเปลี่ยน gamedev จะมีประสิทธิภาพมากขึ้นสำหรับคำถามของฉัน ฉันจะพยายามอย่างดีที่สุดเพื่ออธิบายสถานการณ์ปัจจุบันของฉันและคำถามใดที่เกิดขึ้นระหว่างทาง

ตอนนี้ฉันต้องการซิงค์ผู้เล่นคนเดียวกับเซิร์ฟเวอร์เท่านั้น ในทางทฤษฎีผมสันนิษฐานว่าผู้เล่นคนเดียวที่มีการคาดการณ์ฝั่งไคลเอ็นต์จะไม่ต้องการการแก้ไขเซิร์ฟเวอร์เนื่องจากไม่มีปัจจัยภายนอกที่มีอิทธิพลต่อการเคลื่อนไหวของเขา ดังนั้นต้นแบบของฉันในปัจจุบันมีผู้เล่นเพียงคนเดียวที่ซิงค์กับเซิร์ฟเวอร์โดยไม่ต้องทำการแก้ไขเซิร์ฟเวอร์

หากคุณคุ้นเคยกับระบบเครือข่ายเกมฉันคิดว่าคุณอาจข้ามส่วนบริบทแม้ว่าฉันอาจทำสิ่งผิดปกติไปพร้อมกัน

ลูปไคลเอ็นต์ (หนึ่งครั้งต่อเฟรมหนึ่งครั้งทุกๆ ~ 16.67ms)

ลูปไคลเอนต์แบบง่ายดูเหมือนว่า:

  1. ตรวจสอบอินพุตภายใน (WASD) และจัดทำแพ็กเกจเป็นการกระทำ (เช่นType=MoveLeft, Time=132.0902, ID=15) เราเก็บการกระทำที่บรรจุไว้เพื่อส่งในที่สุด นอกจากนี้เรายังนำการกระทำที่ต้องการไปใช้กับการจำลองทางฟิสิกส์ของเกมโดยตรง ตัวอย่างเช่นหากเรามีการMoveLeftดำเนินการเราจะใช้กำลังไปทางซ้ายกับความเร็วของผู้เล่น

  2. ตรวจสอบเพื่อส่งการกระทำ เพื่อป้องกันการใช้แบนด์วิดท์ของลูกค้าที่ไม่เหมาะสมให้ส่งการกระทำที่ทำแพคเกจในช่วงเวลาที่แน่นอนเท่านั้น (เช่น 30ms)

  3. ใช้การแก้ไขเซิร์ฟเวอร์ ณ จุดนี้จะจัดการกับเดลต้าและการแก้ไขที่ได้รับจากเซิร์ฟเวอร์และนำไปใช้กับการจำลองสถานการณ์ของเกม สำหรับคำถามนี้ไม่ได้ใช้

  4. อัปเดตฟิสิกส์ท้องถิ่น ใช้วงฟิสิกส์ของผู้เล่นหลัก โดยพื้นฐานแล้วสิ่งนี้จะทำการทำนายการเคลื่อนไหวของผู้เล่น สิ่งนี้เป็นการเพิ่มแรงโน้มถ่วงให้กับความเร็วของผู้เล่นใช้ความเร็วของผู้เล่นกับตำแหน่งของเขาแก้ไขการชนกันตลอดทาง ฯลฯ ฉันควรระบุว่าการจำลองทางฟิสิกส์มักจะดำเนินการด้วยเดลต้าวินาทีคงที่ (เรียกหลายครั้งขึ้นอยู่กับวินาทีเดลต้าจริง) .

ฉันข้ามรายละเอียดเฉพาะบางอย่างเกี่ยวกับฟิสิกส์และส่วนอื่น ๆ เพราะฉันรู้สึกว่าพวกเขาไม่จำเป็นสำหรับคำถามนี้ แต่อย่าลังเลที่จะแจ้งให้เราทราบหากพวกเขาจะเกี่ยวข้องกับคำถาม

เซิร์ฟเวอร์ลูป (ทุก ๆ 15ms)

เซิร์ฟเวอร์ลูปที่เรียบง่ายดูเหมือนว่า:

  1. จัดการกับการกระทำ ตรวจสอบแพ็คเกจการกระทำที่ได้รับจากลูกค้าและนำไปใช้กับการจำลองทางฟิสิกส์ของเซิร์ฟเวอร์ ตัวอย่างเช่นเราจะได้รับ 5 MoveLeftกระทำและเราจะนำมาใช้บังคับกับความเร็ว 5 ครั้ง เป็นสิ่งสำคัญที่จะต้องทราบแพคเกจการดำเนินการทั้งหมดจะถูกดำเนินการในหนึ่ง "เฟรม"ตรงกันข้ามกับลูกค้าที่จะถูกนำไปใช้ทันทีที่การกระทำเกิดขึ้น

  2. อัปเดตลอจิกเกม เราอัปเดตฟิสิกส์ของเกมผู้เล่นที่เคลื่อนไหวและแก้ไขการชน ฯลฯ นอกจากนี้เรายังจัดแพคเกจเหตุการณ์สำคัญใด ๆ ที่เกิดขึ้นกับผู้เล่น (เช่นสุขภาพของผู้เล่นลดลงผู้เล่นเสียชีวิตเป็นต้น) ในภายหลัง

  3. ส่งการแก้ไข เราเป็นประจำ (เช่นทุกๆ 35ms) ส่ง delta ไปยังผู้เล่นอื่น (เช่นตำแหน่งของผู้เล่นสุขภาพ ฯลฯ ) หากพวกเขาเปลี่ยนไปเมื่อเร็ว ๆ นี้ ส่วนนี้ไม่ได้ถูกนำไปใช้ในขณะที่ฉันต้องการให้ผู้เล่นคนเดียวทำการจำลองผลลัพธ์เดียวกันบนไคลเอนต์และเซิร์ฟเวอร์โดยไม่มีการแก้ไขเพื่อให้แน่ใจว่าการทำนายฝั่งไคลเอ็นต์ทำงานได้ดี

ปัญหา

ระบบปัจจุบันทำงานได้ดีภายใต้สถานการณ์ที่เรียบง่ายและฉันรู้สึกประหลาดใจอย่างยิ่งที่เห็นว่ามันให้ผลลัพธ์ที่คล้ายกันมากกับการเคลื่อนที่ในแนวนอนที่เรียบง่าย (ความไม่ถูกต้องเกิดจากข้อผิดพลาดแม่นยำของจุดลอยตัวฉันเชื่อว่า):

การซิงค์ทำงานได้ดีด้วยการชน / การเคลื่อนไหวที่ง่าย

โปรดละเว้นกราฟิกต้นแบบ สี่เหลี่ยมสีขาว = ผู้เล่น, สี่เหลี่ยมสีแดง = อุปสรรค, สีน้ำเงิน = พื้นหลัง

อย่างไรก็ตามฉันได้รับข้อผิดพลาดในการซิงค์หลังจากทำการเคลื่อนไหวที่ไวต่อเวลาเช่นการกระโดดและการขยับเข้าใกล้สิ่งกีดขวางที่แยกได้:

การซิงค์ไม่ทำงานเพราะฉันกระโดดข้ามสิ่งกีดขวางที่ระบุในช่วงเวลาที่อ่อนไหว

ในทางทฤษฎีแล้วฉันคาดหวังว่าทั้งคู่จะได้ผลลัพธ์ที่เหมือนกันเสมอเนื่องจากไม่มีปัจจัยภายนอกที่ทำให้ลูกค้ามีอิทธิพลต่อตำแหน่งของเขา ในทางปฏิบัติฉันคิดว่าฉันเข้าใจปัญหา

ตั้งแต่การกระโดดรอบอุปสรรคเหมือนที่มากขึ้นอยู่กับระยะเวลาของการเล่นรูปแบบเล็ก ๆ ของเมื่อความเร็วจะถูกนำไปใช้กับตำแหน่งที่จะมี repercutions ผล (เช่นลูกค้าสามารถย้ายออกไปเพียงในเวลาที่จะหลีกเลี่ยงการปะทะกันกับที่ สิ่งกีดขวางในขณะที่เซิร์ฟเวอร์จะทำตามที่ได้รับแพคเกจการดำเนินการทั้งหมดในภายหลังและติดอยู่กับสิ่งกีดขวางเป็นระยะเวลาสั้น ๆ เปลี่ยนผลสุดท้าย) ความแตกต่างระหว่างวิธีที่ไคลเอ็นต์และเซิร์ฟเวอร์จัดการเป็นส่วนใหญ่ว่าไคลเอนต์จะดำเนินการทั้งหมดตามที่เกิดขึ้นในขณะที่เซิร์ฟเวอร์ดำเนินการทั้งหมดเป็น bulks ตามที่ได้รับ

คำถาม

บริบทที่ยาวนี้ในที่สุดนำไปสู่คำถามของฉัน (ขอบคุณสำหรับการอ่านที่ไกลขนาดนี้): เป็นเรื่องปกติที่ต้องมีการแก้ไขเซิร์ฟเวอร์แม้ว่าจะมีผู้เล่นเพียงคนเดียวที่ซิงค์กับเซิร์ฟเวอร์หรือฉันควรใช้เทคนิคบางอย่างเพื่อหลีกเลี่ยงการซิงโครไนซ์ ?

ฉันคิดถึงวิธีแก้ปัญหาที่เป็นไปได้บางอย่างซึ่งฉันไม่ค่อยสบายใจกับ:

  1. ใช้การแก้ไขเซิร์ฟเวอร์ เพียงสมมติว่านี่เป็นพฤติกรรมปกติและแก้ไขข้อผิดพลาดเมื่อเกิดขึ้น ฉันต้องการใช้งานต่อไป แต่ฉันแค่ต้องการทำให้แน่ใจว่าสิ่งที่ฉันได้ทำไปนั้นเป็นที่ยอมรับ

  2. ใช้เวลาของไคลเอ็นต์ที่ระบุเพื่อใช้การกระทำที่ต้องการ ฉันเดาว่านี่จะคล้ายกับการชดเชยความล่าช้าโดยต้อง "ย้อนเวลากลับไป" และตรวจสอบความเคลื่อนไหว ชนิดของการใช้การแก้ไขเซิร์ฟเวอร์ย้อนเวลากลับไปและดำเนินการต่อไปหลังจากนั้น ฉันไม่ชอบความคิดจริงๆ มันดูซับซ้อนทรัพยากรแพงและต้องใช้เวลาที่ลูกค้าไว้วางใจ (แม้ว่าฉันจะวางแผนเพื่อตรวจสอบว่าเวลานั้นดูค่อนข้างถูกกฎหมาย)

  3. ถาม GameDevelopment StackExchange สำหรับไอเดียใหม่ยอดเยี่ยมที่จะแก้ไขปัญหาทั้งหมดของฉัน

ฉันเพิ่งเริ่มต้นในโลกของเครือข่ายเกมดังนั้นโปรดแก้ไข / วิจารณ์ / ดูถูกแนวคิดใด ๆ ข้างต้นหรือให้ความคิด / ทรัพยากรที่สามารถช่วยฉันได้ตลอดการเดินทางใน Wonderful World of Networking ให้อภัยฉันถ้าฉันสามารถหาคำตอบของฉันที่อื่นฉันล้มเหลวที่

ขอบคุณมากสำหรับช่วงเวลาอันมีค่าของคุณ


เซิร์ฟเวอร์และไคลเอนต์ของคุณรันเฟรมในอัตราที่ต่างกัน จะเกิดอะไรขึ้นถ้าไคลเอนต์ดำเนินการสองอย่างในเฟรมที่ต่อเนื่องกัน แต่เซิร์ฟเวอร์จะเห็นช่องว่างระหว่างเฟรมเดียว
253751

@immibis ตามตัวเองออกจากgafferongames.com/game-physics/fix-your-timestepเพื่อลดผลกระทบนี้
Jesse Emond

คำตอบ:


11

ในกรณีเช่นนี้คุณอาจจะดีกว่าปล่อยให้ลูกค้ามีสิทธิ์เล็กน้อย สำหรับการควบคุมที่แม่นยำเช่นนี้คุณไม่น่าจะมีพฤติกรรมที่ดีแม้จะมีการแก้ไขและการคาดการณ์ขั้นสูงจริงๆ

ลูกค้าต้องขยายจากเพียงแค่ส่งข้อความ "I jumped" เพื่อส่งข้อความ "I jumped จากข้อความ X, Y ณ เวลา T" เซิร์ฟเวอร์จะตรวจสอบว่าสถานที่ตั้งอยู่ใกล้กับสิ่งที่คิดว่าผู้เล่นอยู่ในเวลา T (ซึ่งคุณสามารถ จำกัด เวลาเล็กน้อยในอดีต) เพื่อป้องกันการโกงแล้วจำลองการกระโดดจากตำแหน่งที่ลูกค้า ที่ส่ง เซิร์ฟเวอร์จะแก้ไขลูกค้าเมื่ออยู่ไกลจากการตี (โดยปกติจะเกิดจากความล่าช้าหรือไม่ชอบ)

เทคนิคประเภทนี้ใช้ร่วมกับการแก้ไขและการแก้ไขเพื่อให้เกมตอบสนองกับลูกค้าในพื้นที่และดูราบรื่นสำหรับไคลเอนต์ระยะไกล


น่าสนใจมาก. ฉันจะพยายามใช้สิ่งนี้และดูว่ามันจะไปอย่างไร ขอบคุณมากที่สละเวลาตอบ โดยวิธีการที่คุณพูดถึง "ในระยะใกล้" และ "เวลาที่มีขนาดเล็กพอสมควรในอดีต" คุณจะตรวจสอบกับระยะทางและเวลาคงที่ตามลำดับหรือไม่? หรือคุณจะใช้เทคนิคที่ซับซ้อนมากขึ้นเช่นการเก็บประวัติตำแหน่งและการใช้เวลา Round-Trip เฉลี่ยของลูกค้า (หรืออย่างอื่น)
Jesse Emond

สิ่งที่ใช้ได้กับเกมของคุณ เริ่มง่ายซับซ้อนเพียงระดับที่คุณคิดว่าจำเป็น เซิร์ฟเวอร์บางแห่งเก็บประวัติสแนปชอตไว้เป็นจำนวนมากเพื่อ~max(RTT)ติ๊กเซิร์ฟเวอร์ในอดีต แต่ไม่ว่าคุณจะต้องการเกมของคุณโดยเฉพาะหรือไม่ มันจะมีประโยชน์มากยิ่งขึ้นสำหรับเกมสไตล์นักกีฬาที่คุณต้องการตรวจจับ Hit / shot ระดับหนึ่งบนไคลเอนต์และไม่เพียง แต่ต้องรู้ว่าผู้เล่นอยู่ที่ไหน 46ms ที่ผ่านมา แต่ยังที่เป้าหมายของเขาคือ 46ms ที่ผ่านมาและที่ใด ๆ กำลังเคลื่อนย้ายแพลตฟอร์มที่ปิดกั้นที่ซึ่ง
Sean Middleditch

สมบูรณ์ ฉันจะทดสอบและดูว่าอะไรดีที่สุด ทำเครื่องหมายว่าเป็นคำตอบที่ยอมรับขอบคุณอีกครั้ง!
Jesse Emond
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.