วิธีที่เราปรับปรุงกลไกการคำนวณขีปนาวุธสำหรับเกมยิงมือถือด้วยอัลกอริธึมการชดเชยเวลาแฝงของเครือข่าย

วิธีที่เราปรับปรุงกลไกการคำนวณขีปนาวุธสำหรับเกมยิงมือถือด้วยอัลกอริธึมการชดเชยเวลาแฝงของเครือข่าย

สวัสดี ฉันชื่อ Nikita Brizhak ผู้พัฒนาเซิร์ฟเวอร์จาก Pixonic วันนี้ฉันอยากจะพูดเกี่ยวกับการชดเชยความล่าช้าในผู้เล่นหลายคนบนมือถือ

มีการเขียนบทความมากมายเกี่ยวกับการชดเชยความล่าช้าของเซิร์ฟเวอร์ รวมถึงภาษารัสเซียด้วย ไม่น่าแปลกใจเนื่องจากเทคโนโลยีนี้ถูกนำมาใช้อย่างแข็งขันในการสร้าง FPS แบบผู้เล่นหลายคนตั้งแต่ปลายยุค 90 ตัวอย่างเช่น คุณสามารถจำม็อด QuakeWorld ซึ่งเป็นหนึ่งในม็อดแรกๆ ที่ใช้มัน

เรายังใช้มันในเกมยิงผู้เล่นหลายคนบนมือถือ Dino Squad ของเราอีกด้วย

ในบทความนี้ เป้าหมายของฉันไม่ใช่การทำซ้ำสิ่งที่เขียนไว้นับพันครั้ง แต่เพื่อบอกว่าเราใช้การชดเชยความล่าช้าในเกมของเราอย่างไร โดยคำนึงถึงกลุ่มเทคโนโลยีและคุณสมบัติการเล่นเกมหลักของเรา

คำไม่กี่คำเกี่ยวกับเยื่อหุ้มสมองและเทคโนโลยีของเรา

Dino Squad เป็นเกมยิง PvP บนมือถือบนเครือข่าย ผู้เล่นควบคุมไดโนเสาร์ที่ติดตั้งอาวุธหลากหลายและต่อสู้กันในทีม 6 ต่อ 6

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

หากนี่เป็นครั้งแรกที่คุณได้ยินเกี่ยวกับการชดเชยความล่าช้า ต่อไปนี้เป็นการเจาะลึกประเด็นนี้โดยสรุป

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

ในขณะที่อยู่บนเครือข่ายท้องถิ่น ความล่าช้านี้ (โดยทั่วไปเรียกว่าความล่าช้าในการป้อนข้อมูล) อาจมองไม่เห็น แต่เมื่อเล่นผ่านอินเทอร์เน็ต จะสร้างความรู้สึก "เลื่อนบนน้ำแข็ง" เมื่อควบคุมตัวละคร ปัญหานี้มีความเกี่ยวข้องเป็นสองเท่าสำหรับเครือข่ายมือถือ โดยที่กรณีที่ Ping ของผู้เล่นอยู่ที่ 200 ms ยังถือว่าเป็นการเชื่อมต่อที่ยอดเยี่ยม บ่อยครั้งที่ Ping อาจเป็น 350, 500 หรือ 1000 ms จากนั้นแทบจะเป็นไปไม่ได้เลยที่จะเล่นเกมยิงเร็วที่มีอินพุตแล็ก

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

สิ่งสำคัญคือต้องเข้าใจความแตกต่างเล็กน้อยที่นี่: ไคลเอนต์จะดึงตัวเองตามอินพุตสุดท้ายเสมอและศัตรู - โดยมีความล่าช้าของเครือข่ายตามสถานะก่อนหน้าจากข้อมูลจากเซิร์ฟเวอร์ นั่นคือเมื่อยิงใส่ศัตรูผู้เล่นจะเห็นเขาในอดีตสัมพันธ์กับตัวเขาเอง ข้อมูลเพิ่มเติมเกี่ยวกับการทำนายลูกค้า เราเขียนไว้ก่อนหน้านี้.

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

ด้วยความรู้นี้ เราจึงเริ่มดำเนินการชดเชยความล่าช้าของเซิร์ฟเวอร์ใน Dino Squad ก่อนอื่น เราต้องเข้าใจวิธีการคืนค่าบนเซิร์ฟเวอร์ตามที่ลูกค้าเห็น? และจำเป็นต้องคืนค่าอะไรกันแน่? ในเกมของเรา การโจมตีจากอาวุธและความสามารถจะถูกคำนวณผ่านเรย์แคสต์และโอเวอร์เลย์ - นั่นคือผ่านการโต้ตอบกับผู้ชนทางกายภาพของศัตรู ดังนั้นเราจึงจำเป็นต้องจำลองตำแหน่งของชนเหล่านี้ซึ่งผู้เล่น "เห็น" ในเครื่องบนเซิร์ฟเวอร์ ตอนนั้นเราใช้ Unity เวอร์ชัน 2018.x API ฟิสิกส์เป็นแบบคงที่ โลกทางกายภาพมีอยู่ในสำเนาเดียว ไม่มีวิธีใดที่จะบันทึกสถานะแล้วกู้คืนจากกล่องได้ แล้วต้องทำอย่างไร?

วิธีแก้ปัญหาอยู่บนพื้นผิว เราใช้องค์ประกอบทั้งหมดแล้วเพื่อแก้ไขปัญหาอื่น ๆ :

  1. สำหรับลูกค้าแต่ละราย เราจำเป็นต้องรู้ว่าเขาเห็นคู่ต่อสู้ในเวลาใดเมื่อเขากดปุ่ม เราได้เขียนข้อมูลนี้ลงในแพ็คเกจอินพุตแล้วและใช้เพื่อปรับการคาดการณ์ของลูกค้า
  2. เราจำเป็นต้องสามารถจัดเก็บประวัติสถานะของเกมได้ มันอยู่ในนั้นที่เราจะรักษาตำแหน่งของคู่ต่อสู้ของเรา (และด้วยเหตุนี้ผู้ชนของพวกเขา) เรามีประวัติสถานะบนเซิร์ฟเวอร์อยู่แล้ว เราใช้มันเพื่อสร้าง สันดอน. เมื่อรู้เวลาที่เหมาะสม เราก็สามารถค้นหาสถานะที่ถูกต้องในประวัติศาสตร์ได้อย่างง่ายดาย
  3. ตอนนี้เรามีสถานะเกมจากประวัติศาสตร์อยู่ในมือแล้ว เราจำเป็นต้องสามารถซิงโครไนซ์ข้อมูลผู้เล่นกับสถานะของโลกทางกายภาพได้ ชนที่มีอยู่ - ย้าย, อันที่หายไป - สร้าง, อันที่ไม่จำเป็น - ทำลาย ตรรกะนี้เขียนไว้แล้วและประกอบด้วยระบบ ECS หลายระบบ เราใช้มันเพื่อเก็บห้องเกมหลายห้องในกระบวนการ Unity เดียว และเนื่องจากโลกทางกายภาพเป็นโลกเดียวต่อกระบวนการ จึงต้องนำกลับมาใช้ใหม่ระหว่างห้องต่างๆ ก่อนการจำลองแต่ละครั้ง เราจะ "รีเซ็ต" สถานะของโลกทางกายภาพและเริ่มต้นใหม่ด้วยข้อมูลสำหรับห้องปัจจุบัน โดยพยายามนำวัตถุเกม Unity กลับมาใช้ใหม่ให้มากที่สุดเท่าที่จะเป็นไปได้ผ่านระบบรวมกลุ่มที่ชาญฉลาด สิ่งที่เหลืออยู่ก็คือการเรียกใช้ตรรกะเดียวกันสำหรับสถานะของเกมจากอดีต

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

public class TimeMachine : ITimeMachine
{
     //История игровых состояний
     private readonly IGameStateHistory _history;

     //Текущее игровое состояние на сервере
     private readonly ExecutableSystem[] _systems;

     //Набор систем, расставляющих коллайдеры в физическом мире 
     //по данным из игрового состояния
     private readonly GameState _presentState;

     public TimeMachine(IGameStateHistory history, GameState presentState, ExecutableSystem[] timeInitSystems)
     {
         _history = history; 
         _presentState = presentState;
         _systems = timeInitSystems;  
     }

     public GameState TravelToTime(int tick)
     {
         var pastState = tick == _presentState.Time ? _presentState : _history.Get(tick);
         foreach (var system in _systems)
         {
             system.Execute(pastState);
         }
         return pastState;
     }
}

สิ่งที่เหลืออยู่คือการหาวิธีใช้เครื่องนี้เพื่อชดเชยช็อตและความสามารถได้อย่างง่ายดาย

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

แต่มีกลไกดังกล่าวน้อยมากใน Dino Squad! อาวุธส่วนใหญ่ในเกมสร้างขีปนาวุธ - กระสุนอายุยืนที่บินเพื่อจำลองเห็บหลายตัว (ในบางกรณีอาจมีเห็บหลายสิบตัว) จะทำอย่างไรกับพวกเขาพวกเขาควรบินกี่โมง?

В บทความโบราณ เกี่ยวกับสแต็กเครือข่าย Half-Life พวกจาก Valve ถามคำถามเดียวกันและคำตอบของพวกเขาคือ: การชดเชยความล่าช้าของกระสุนปืนเป็นปัญหาและควรหลีกเลี่ยงจะดีกว่า

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

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

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

2. เราทำทุกอย่างเหมือนกับในตัวเลือกแรก แต่เมื่อนับหนึ่งขีดของการจำลองกระสุนแล้ว เราก็ไม่หยุด แต่ยังคงจำลองการบินภายในขีดเซิร์ฟเวอร์เดียวกัน แต่ละครั้งจะนำเวลาเข้าใกล้เซิร์ฟเวอร์มากขึ้น ทีละขีดและอัปเดตตำแหน่งคอลไลเดอร์ เราทำสิ่งนี้จนกว่าหนึ่งในสองสิ่งจะเกิดขึ้น:

  • กระสุนหมดอายุแล้ว หมายความว่าเมื่อคำนวณเสร็จแล้ว เราสามารถนับพลาดหรือตีได้ และนี่ก็เป็นจังหวะเดียวกับที่กระสุนถูกยิง! สำหรับเรานี่เป็นทั้งข้อดีและข้อเสีย ข้อดี - เพราะสำหรับผู้เล่นยิงปืนสิ่งนี้จะช่วยลดความล่าช้าระหว่างการโจมตีและสุขภาพของศัตรูที่ลดลงอย่างมาก ข้อเสียคือสังเกตเอฟเฟกต์เดียวกันนี้เมื่อฝ่ายตรงข้ามยิงใส่ผู้เล่น: ดูเหมือนว่าศัตรูจะยิงจรวดช้าๆ เท่านั้นและนับความเสียหายแล้ว
  • สัญลักษณ์แสดงหัวข้อย่อยถึงเวลาเซิร์ฟเวอร์แล้ว ในกรณีนี้ การจำลองจะดำเนินต่อไปในเซิร์ฟเวอร์ถัดไป โดยไม่มีการชดเชยความล่าช้าใดๆ สำหรับกระสุนที่เคลื่อนที่ช้า สิ่งนี้สามารถลดจำนวนการย้อนกลับทางฟิสิกส์ในทางทฤษฎีได้เมื่อเทียบกับตัวเลือกแรก ในเวลาเดียวกัน การโหลดที่ไม่สม่ำเสมอในการจำลองก็เพิ่มขึ้น: เซิร์ฟเวอร์ไม่ได้ใช้งาน หรือในเซิร์ฟเวอร์หนึ่งติ๊ก กำลังคำนวณการจำลองหนึ่งโหลสำหรับสัญลักษณ์แสดงหัวข้อย่อยหลายรายการ

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

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

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

ในขั้นตอนนี้เรามีระบบการทำงานโดยทั่วไป รหัสของมันในรูปแบบที่ค่อนข้างง่าย:

public sealed class LagCompensationSystemGroup : ExecutableSystem
{
     //Машина времени
     private readonly ITimeMachine _timeMachine;

     //Набор систем лагкомпенсации
     private readonly LagCompensationSystem[] _systems;
     
     //Наша реализация кластеризатора
     private readonly TimeTravelMap _travelMap = new TimeTravelMap();

    public LagCompensationSystemGroup(ITimeMachine timeMachine, 
        LagCompensationSystem[] lagCompensationSystems)
     {
         _timeMachine = timeMachine;
         _systems = lagCompensationSystems;
     }

     public override void Execute(GameState gs)
     {
         //На вход кластеризатор принимает текущее игровое состояние,
         //а на выход выдает набор «корзин». В каждой корзине лежат энтити,
         //которым для лагкомпенсации нужно одно и то же время из истории.
         var buckets = _travelMap.RefillBuckets(gs);

         for (int bucketIndex = 0; bucketIndex < buckets.Count; bucketIndex++)
         {
             ProcessBucket(gs, buckets[bucketIndex]);
         }

         //В конце лагкомпенсации мы восстанавливаем физический мир 
         //в исходное состояние
         _timeMachine.TravelToTime(gs.Time);
     }

     private void ProcessBucket(GameState presentState, TimeTravelMap.Bucket bucket)
     {
         //Откатываем время один раз для каждой корзины
         var pastState = _timeMachine.TravelToTime(bucket.Time);

         foreach (var system in _systems)
         {
               system.PastState = pastState;
               system.PresentState = presentState;

               foreach (var entity in bucket)
               {
                   system.Execute(entity);
               }
          }
     }
}

สิ่งที่เหลืออยู่คือการกำหนดค่ารายละเอียด:

1. ทำความเข้าใจว่าต้องจำกัดระยะทางสูงสุดในการเคลื่อนที่ให้ทันเวลามากน้อยเพียงใด

เป็นสิ่งสำคัญสำหรับเราที่จะต้องทำให้เกมเข้าถึงได้มากที่สุดเท่าที่จะเป็นไปได้ในสภาพที่เครือข่ายมือถือไม่ดี ดังนั้นเราจึงจำกัดเรื่องราวไว้ที่ 30 ติ๊ก (ด้วยอัตราติ๊กที่ 20 Hz) สิ่งนี้ทำให้ผู้เล่นสามารถโจมตีคู่ต่อสู้ได้แม้จะมีค่าปิงที่สูงมากก็ตาม

2. พิจารณาว่าวัตถุใดสามารถเคลื่อนย้ายได้ทันเวลาและไม่สามารถเคลื่อนย้ายได้

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

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

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

ประการแรก ปรับปรุงการจัดกลุ่ม: เราสามารถใช้สถานะทางกายภาพเดียวกันสำหรับผู้เล่นทุกคนที่มีการปิงปิด

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

ประการที่สาม เราคำนวณตำแหน่งของอาวุธของไดโนเสาร์หรือจุดใช้งานความสามารถโดยใช้ข้อมูลจาก ECS ก่อนที่จะเริ่มการชดเชยความล่าช้าด้วยซ้ำ

เป็นผลให้ตำแหน่งที่แท้จริงของตัวชนของผู้เล่นที่ชดเชยความล่าช้านั้นไม่สำคัญสำหรับเรา ดังนั้นเราจึงใช้เส้นทางที่มีประสิทธิผลมากขึ้นและในเวลาเดียวกันก็ง่ายกว่า

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

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

ในเวอร์ชัน 2019 (และอาจจะเร็วกว่านั้นเล็กน้อย) Unity ได้เพิ่มการรองรับอย่างเต็มที่สำหรับฉากทางกายภาพที่เป็นอิสระ เราปรับใช้มันบนเซิร์ฟเวอร์เกือบจะทันทีหลังจากการอัพเดต เนื่องจากเราต้องการกำจัดโลกทางกายภาพที่เหมือนกันในทุกห้องอย่างรวดเร็ว

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

นอกจากนี้ เรายังวิจัยด้วยว่าฉากทางกายภาพสามารถนำไปใช้จัดเก็บประวัติศาสตร์ของโลกทางกายภาพได้หรือไม่ นั่นคือตามเงื่อนไขแล้วไม่ได้จัดสรรฉากหนึ่งฉากให้กับแต่ละห้อง แต่ต้องเป็น 30 ฉากและสร้างบัฟเฟอร์แบบวนรอบเพื่อจัดเก็บเรื่องราว โดยทั่วไปตัวเลือกนี้ใช้งานได้ แต่เราไม่ได้นำไปใช้: มันไม่ได้แสดงประสิทธิภาพการผลิตที่เพิ่มขึ้นอย่างบ้าคลั่ง แต่จำเป็นต้องมีการเปลี่ยนแปลงที่ค่อนข้างเสี่ยง เป็นการยากที่จะคาดเดาว่าเซิร์ฟเวอร์จะมีพฤติกรรมอย่างไรเมื่อต้องทำงานเป็นเวลานานกับฉากต่างๆ มากมาย ดังนั้นเราจึงปฏิบัติตามกฎ: “ถ้ายังไม่เกิดปัญหาไม่ควรแก้ไข'

ที่มา: will.com

เพิ่มความคิดเห็น