สวัสดี ฉันชื่อ 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 ฟิสิกส์เป็นแบบคงที่ โลกทางกายภาพมีอยู่ในสำเนาเดียว ไม่มีวิธีใดที่จะบันทึกสถานะแล้วกู้คืนจากกล่องได้ แล้วต้องทำอย่างไร?
วิธีแก้ปัญหาอยู่บนพื้นผิว เราใช้องค์ประกอบทั้งหมดแล้วเพื่อแก้ไขปัญหาอื่น ๆ :
- สำหรับลูกค้าแต่ละราย เราจำเป็นต้องรู้ว่าเขาเห็นคู่ต่อสู้ในเวลาใดเมื่อเขากดปุ่ม เราได้เขียนข้อมูลนี้ลงในแพ็คเกจอินพุตแล้วและใช้เพื่อปรับการคาดการณ์ของลูกค้า
- เราจำเป็นต้องสามารถจัดเก็บประวัติสถานะของเกมได้ มันอยู่ในนั้นที่เราจะรักษาตำแหน่งของคู่ต่อสู้ของเรา (และด้วยเหตุนี้ผู้ชนของพวกเขา) เรามีประวัติสถานะบนเซิร์ฟเวอร์อยู่แล้ว เราใช้มันเพื่อสร้าง
สันดอน . เมื่อรู้เวลาที่เหมาะสม เราก็สามารถค้นหาสถานะที่ถูกต้องในประวัติศาสตร์ได้อย่างง่ายดาย - ตอนนี้เรามีสถานะเกมจากประวัติศาสตร์อยู่ในมือแล้ว เราจำเป็นต้องสามารถซิงโครไนซ์ข้อมูลผู้เล่นกับสถานะของโลกทางกายภาพได้ ชนที่มีอยู่ - ย้าย, อันที่หายไป - สร้าง, อันที่ไม่จำเป็น - ทำลาย ตรรกะนี้เขียนไว้แล้วและประกอบด้วยระบบ 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! อาวุธส่วนใหญ่ในเกมสร้างขีปนาวุธ - กระสุนอายุยืนที่บินเพื่อจำลองเห็บหลายตัว (ในบางกรณีอาจมีเห็บหลายสิบตัว) จะทำอย่างไรกับพวกเขาพวกเขาควรบินกี่โมง?
В
เราไม่มีตัวเลือกนี้ อาวุธที่ใช้กระสุนเป็นคุณลักษณะสำคัญของการออกแบบเกม ดังนั้นเราจึงต้องคิดอะไรบางอย่างขึ้นมา หลังจากการระดมความคิด เราก็ได้กำหนดทางเลือกสองทางที่ดูเหมือนว่าจะได้ผล:
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