יישום סנכרון מצב למשחקי מובייל – Full Stack
סנכרון מצב הוא הבסיס של כל חוויית משחק מרובה משתתפים. הבעיה המרכזית: שני מכשירים חייבים לראות את אותו עולם משחק באותו רגע, למרות השהיית רשת, אובדן חבילות וכוח עיבוד שונה. קיימות מספר גישות, והבחירה תלויה בז'אנר. אנו מיישמים סנכרון מלא: החל מבחירת פרוטוקול ועד לבדיקות תחת השהיה של מילישניות. לדוגמה, עבור משחק יריות, השהיה נמוכה וחלקות הן קריטיות, ולכן אנו משתמשים בחיזוי לקוח ודחיסת דלתא. עבור משחק אסטרטגיה מבוסס תורות, מספיק Event Sourcing עם חישוב נקודה קבועה. ללא ארכיטקטורה נכונה, שחקנים יחוו פיגור, חוסר סנכרון ואובדן התקדמות.
מהו סנכרון מצב במשחקי מובייל?
סנכרון הוא מנגנון שמבטיח עקביות נתונים בין השרת לכל הלקוחות. בלעדיו, משחק מרובה משתתפים בלתי אפשרי: שחקנים רואים אויבים שונים, כדורים עוברים דרך קירות, והתקדמות אובדת. הדילמה המרכזית: דיוק מלא דורש רוחב פס שהאינטרנט הנייד ממעט לספק. לכן אנו מאזנים בין פירוט לתעבורה.
Snapshot לעומת State Delta לעומת Event Sourcing
שלוש גישות עיקריות להפצת מצב:
Snapshot מלא: השרת שולח את מצב העולם המלא כל N מילישניות. פשוט אך בזבזני. עם 20 ישויות ב-50 בתים כל אחת, ב-20 הרץ – 20 KB/s לכל לקוח. עבור 10 לקוחות, השרת שולח 200 KB/s. מתאים למשחקים קטנים.
דחיסת דלתא: שליחה רק של שינויים מה-Snapshot האחרון שאושר. הלקוח מאשר ack: last_received_tick, השרת מחשב דלתא. מפחית תעבורה פי 3-10 בסצנות דינמיות. היישום מורכב יותר: יש צורך לאחסן היסטוריית מצב לחישוב דלתא, ולטפל באובדן חבילת דלתא (ללא Snapshot בסיס, לא ניתן להחיל דלתא).
Event Sourcing: השרת משדר אירועים (PlayerMoved, BulletFired, EntityDied), והלקוח משחזר אותם על גבי המצב הבסיסי. טוב למשחקים דטרמיניסטיים: שחמט, משחקי קלפים, אסטרטגיה. רע לסימולציות פיזיקה: כל שגיאת דיוק של נקודה צפה מפצלת מצבים תוך 30 שניות.
כיצד דטרמיניזם משפיע על סנכרון?
עבור Event Sourcing, שתי הפלטפורמות (iOS ARM64, Android ARM64/x86) חייבות להפיק את אותה תוצאה מאותם חישובים. float סטנדרטי ב-C# הוא דטרמיניסטי בפלטפורמה אחת אך יכול לתת תוצאות שונות במעבדים שונים. פתרון – חישוב נקודה קבועה: במקום float 1.5f, השתמשו ב-FixedPoint 15000 (קנה מידה 1/10000). חיבור וכפל של מספרים שלמים הם דטרמיניסטיים בכל מקום. ספריות: FixedMath.Net עבור Unity, libfixmath עבור C++ מקורי. Godot משתמש בפיזיקה דטרמיניסטית דרך _physics_process – כל הצעדים קבועים. Unity Physics (DOTS) תומך בדטרמיניזם עם סדר עיבוד אובייקטים עקבי. Unity PhysX קלאסי – אינו דטרמיניסטי בין פלטפורמות.
מדוע חישוב נקודה קבועה עדיף על פני Float לסנכרון?
Float יכול להפיק תוצאות שונות בארכיטקטורות שונות עקב הבדלי עיגול. נקודה קבועה מבטיחה דטרמיניזם, קריטי עבור Event Sourcing. בנוסף, פעולות נקודה קבועה יכולות להיות מהירות יותר בחלק מהמעבדים מכיוון שאינן דורשות FPU חומרתי. עם זאת, הדיוק נמוך יותר: בקנה מידה של 1/10000, השגיאה המקסימלית היא 0.0001, מקובל למיקומים אך עשוי לא להספיק לפיזיקה במהירות גבוהה. אנו עוזרים לבחור את הפורמט האופטימלי למשחק שלכם.
חיזוי לקוח ו-Reconciliation
פירוט מלא של התבנית למשחקי זמן אמת:
Client tick 100: применяю ввод локально, отправляю InputPayload{tick:100, input}
Client tick 101-110: продолжаю предсказывать локально
Server: получает InputPayload{tick:100}, симулирует, отвечает StatePayload{tick:100, pos, vel}
Client tick 112: получаю ответ сервера для тика 100
→ Сравниваю предсказанное состояние на тик 100 с серверным
→ Если расхождение > threshold: откат к серверному состоянию тика 100
→ Повторно применяю буфер вводов 101-112מאגר הקלט הוא מערך מעגלי בגודל קבוע (בדרך כלל 64-128 טיקים). כל אלמנט: Client tick 100: применяю ввод локально, отправляю InputPayload{tick:100, input} Client tick 101-110: продолжаю предсказывать локально Server: получает InputPayload{tick:100}, симулирует, отвечает StatePayload{tick:100, pos, vel} Client tick 112: получаю ответ сервера для тика 100 → Сравниваю предсказанное состояние на тик 100 с серверным → Если расхождение > threshold: откат к серверному состоянию тика 100 → Повторно применяю буфер вводов 101-112 . ב-Reconciliation, עוברים על המאגר ומחילים מחדש קלטים. סף ל-Reconciliation: לא אפס. אם יש פער של 0.001 יחידות -> rollback -> הלקוח רועד כל הזמן. סף טיפוסי: 0.1-0.5 יחידות תלוי במהירות הדמות.
מתי יש צורך באינטרפולציה עבור שחקנים אחרים?
הדמות שלך: חיזוי לקוח. שחקנים אחרים: אינטרפולציה. הלקוח מאחסן מאגר של Snapshots עם חותמות זמן של השרת:
[{time: 1000ms, pos: (10,0,5)}, {time: 1050ms, pos: (10.5,0,5)}, ...] הרינדור מתבצע עם השהיית אינטרפולציה (בדרך כלל 2-3 Snapshots = 100-150 ms ב-20 הרץ). בזמן הרינדור, מוצאים שני Snapshots קרובים ומבצעים אינטרפולציה לינארית של המיקום:
float t = (renderTime - fromState.time) / (toState.time - fromState.time);
renderPosition = Vector3.Lerp(fromState.position, toState.position, t);עבור סיבוב – { tick, inputData, predictedState }. עבור מהירות – אינטרפולציית Hermite או Catmull-Rom על פני מספר נקודות – חלקה יותר בעת שינוי כיוון.
כיצד חיזוי לקוח מפחית השהיה?
ללא חיזוי, השחקן רואה השהיה השווה ל-RTT/2 (זמן לשרת ובחזרה). עם חיזוי, הקלט מוחל מיידית, והשרת רק מתקן. זה מקצר את ההשהיה הנתפסת פי 2-5. עבור משחקי יריות, זה קריטי: אפילו 50 ms השהיה מורגשים.
בעיית ה-Desync
במשחקים דטרמיניסטיים, Desync לא מופיע מיד. אבחון סטנדרטי: השוואת hash מצב – כל N טיקים כל הלקוחות שולחים hash של המצב הנוכחי לשרת. אם ה-hashים לא תואמים – Desync, השרת משדר Snapshot מלא לסנכרון. ה-hash מחושב משדות מצב קריטיים (מיקומים, נקודות חיים, סטטוסים) – לא מהכל, כדי להחריג הבדלים לא רלוונטיים (משקלי אנימציה, מצב ממשק).
כיצד האינטרנט הנייד משפיע על סנכרון?
האינטרנט הנייד אינו יציב. תכננו למקרה הגרוע: 150 ms RTT, 5% אובדן חבילות, תקציב תעבורה של 50 KB/s לשחקן. צעדים מעשיים:
- מיקומים: int16 במקום float32 עם קנה מידה (חיסכון של 50%)
- סיבוב: קווטרניון → שתי זוויות int8 (חיסכון של 75%)
- ישויות מחוץ לטווח ראייה: לא לשלוח או להפחית תדירות עדכון
- עדכונים מבוססי עדיפות: אובייקטים בתנועה מהירה מתעדכנים לעתים קרובות יותר
ספריות BitPacking: NetStack (C#), LiteNetLib BitWriter – אורזים מספר ערכים קטנים לבית אחד.
כיצד זיהוי Desync עובד בפועל?
אנו מיישמים השוואת hash מצב פעם בשנייה. במקרה של Desync, השרת שולח Snapshot מלא לכל הלקוחות לסנכרון מחדש. בנוסף, אנו משתמשים בבדיקות סכום לכל חבילה כדי לזהות שגיאות סיביות.מה כוללת העבודה
| שלב | תוצר | משך (הערכה) |
|---|---|---|
| ניתוח | פרוטוקול סנכרון, הערכת תעבורה | 3-7 ימים |
| עיצוב | מפרט ארכיטקטורה, בחירת מחסנית טכנולוגית | 5-10 ימים |
| פיתוח | יישום סנכרון, חיזוי לקוח, Reconciliation | 30-60 ימים |
| בדיקות | בדיקות עומס, סימולציית Desync | 7-14 ימים |
| פריסה | אינטגרציה עם CDN/backend, ניטור | 5-10 ימים |
העלות מחושבת באופן אישי לפי מורכבות. בקשו ייעוץ – נעריך את הפרויקט שלכם תוך 1-2 ימים. צרו קשר – נספר לכם כיצד להפחית השהיה ותעבורה.
לוחות זמנים
סנכרון Snapshot עם אינטרפולציה ל-4-10 שחקנים: 2-3 שבועות. חיזוי לקוח, Reconciliation, דחיסת דלתא, זיהוי Desync: 1.5-3 חודשים. סימולציה דטרמיניסטית עם חישוב נקודה קבועה: מוסיף 3-6 שבועות. צרו קשר – נמצא את הפתרון האופטימלי לתקציב שלכם.
השוואת גישות:
| גישה | תעבורה | השהיה | מורכבות |
|---|---|---|---|
| Snapshot | גבוהה | בינונית | נמוכה |
| דחיסת דלתא | נמוכה | נמוכה | בינונית |
| Event Sourcing | בינונית | גבוהה | גבוהה |
דחיסת דלתא יעילה פי 3-10 מ-Snapshot בתעבורה, ו-Event Sourcing נמנע מסמכות שרת במחיר מורכבות.







