Unity Multiplayer עם Mirror: סנכרון, חיבור מחדש ושרת Headless
משחק Unity למובייל לעיתים קרובות נתקע כשמוסיפים Multiplayer. סנכרון רשת בטלפון הוא אתגר נפרד: מעבר לרקע בטעות מנתק את החיבור, ועיכובים של 200 אלפיות השנייה הורגים את המשחקיות. אנחנו פותרים זאת באמצעות קוד הרשת הפתוח Mirror Networking — מסגרת שנותנת שליטה מלאה על רשת Unity והתשתית. בניגוד ל-Photon, Mirror אינו קשור לענן: חברו את kcp2k, Telepathy או WebSockets, ואחסנו את השרת על החומרה שלכם. עבור פרויקטים למובייל זה אומר חביון של 10–50 אלפיות השנייה במקום 100+, חיסכון של עד 70% באחסון, ואופטימיזציית תעבורה שמפחיתה עלויות העברת נתונים עד 50%. באופן ספציפי, אירוח שרת Mirror עבור 50 שחקנים עולה כ-$50-100 לחודש, חיסכון של 70% לעומת $200-300 של Photon עבור אותו עומס. עבור 1000 שחקנים בו-זמנית, Mirror עולה כ-$500 לחודש לעומת $1500 של Photon, חיסכון של $1000 בחודש. הניסיון שלנו: 5+ שנים בפיתוח משחקי מובייל מרובי משתתפים על Unity, מעל 30 הפצות מוצלחות. אנחנו מבטיחים סנכרון יציב אפילו עם 50+ שחקנים בו-זמנית.
איך Mirror פותר את בעיית ניתוק החיבור במובייל
רשתות מובייל אינן יציבות. במעבר לרקע, iOS סוגר את ה-socket לאחר 10 שניות, Android משתנה. Mirror עצמו אינו מתחבר מחדש, ולכן אנו מיישמים חיבור מחדש מותאם אישית:
public class MobileNetworkManager : NetworkManager
{
private bool wasPaused;
void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
wasPaused = true;
}
else if (wasPaused)
{
wasPaused = false;
if (!NetworkClient.isConnected)
{
StartCoroutine(TryReconnect());
}
}
}
IEnumerator TryReconnect()
{
int attempts = 0;
while (attempts < 5 && !NetworkClient.isConnected)
{
NetworkClient.Reconnect();
yield return new WaitForSeconds(2f * attempts);
attempts++;
}
}
}לפי תיעוד Mirror Networking, המתודה NetworkClient.Reconnect() משמשת לשחזור החיבור.
הקוד למעלה הוא הבסיס. בנוסף אנו מגדירים התראות push (APNs/FCM) כדי להחזיר את השחקן לסשן הפעיל ומשתמשים ב-NetworkManager.singleton לגישה גלובלית. חשוב: אל תיצרו מספר NetworkManager בסצנה — Mirror ייתן אזהרה, אך ההתנהגות תהפוך לבלתי צפויה.
סנכרון מצב: SyncVar, SyncList וקריאות
public class MobileNetworkManager : NetworkManager { private bool wasPaused; void OnApplicationPause(bool pauseStatus) { if (pauseStatus) { wasPaused = true; } else if (wasPaused) { wasPaused = false; if (!NetworkClient.isConnected) { StartCoroutine(TryReconnect()); } } } IEnumerator TryReconnect() { int attempts = 0; while (attempts < 5 && !NetworkClient.isConnected) { NetworkClient.Reconnect(); yield return new WaitForSeconds(2f * attempts); attempts++; } } } מעביר שינויים אוטומטית מהשרת לכל הלקוחות. השתמשו ב-hook לעדכון ממשק משתמש:
public class PlayerHealth : NetworkBehaviour
{
[SyncVar(hook = nameof(OnHealthChanged))]
public int health = 100;
void OnHealthChanged(int oldVal, int newVal)
{
healthBar.fillAmount = newVal / 100f;
}
[Command]
public void CmdTakeDamage(int amount)
{
// Выполняется только на сервере
health = Mathf.Max(0, health - amount);
}
}עבור אוספים (מלאי, תור באפים) השתמשו ב-SyncList<T> עם callback של OnChange. השלישייה [Command] (לקוח→שרת), [ClientRpc] (שרת→כל הלקוחות), [TargetRpc] (שרת→לקוח יחיד) היא היסוד של כל Multiplayer. שלטו בהם לפני שמתחילים באינטגרציה.
למה בחירת ה-Transport קריטית ל-Multiplayer במובייל
| Transport | פרוטוקול | מובייל |
|---|---|---|
| kcp2k | מבוסס UDP | מומלץ, חביון נמוך (10-50 אלפיות השנייה) |
| Telepathy | TCP | אמין, חביון גבוה יותר (100+ אלפיות השנייה) |
| WebSockets (Mirror) | TCP/WS | עבור WebGL, לא אופטימלי ל-native |
| Ignorance | UDP (ENet) | חלופה טובה ל-kcp2k |
עבור אפליקציית מובייל native בחרו ב-kcp2k או Ignorance. kcp2k מובנה ב-Mirror מגרסה 50+, הגדרה: NoDelay = true, Interval = 10ms, FastResend = 2. עבור אקשן במובייל, kcp2k מהיר פי 5-10 מ-TCP. מערכת ניהול העניין של Mirror מפחיתה נתונים נוספים ב-20-30% לכל שחקן. שימוש ב-Spatial Hashing Interest Management יכול להפחית עדכונים ב-70% ברמות גדולות. השתמשו ב-TCP (Telepathy) למשחקים מבוססי תורות שבהם אמינות חשובה יותר מחביון. במכשירי מובייל kcp2k מספק חביון נמוך פי 5-10 בהשוואה ל-TCP.
השוואת מודלי סמכות:
| מאפיין | Authoritative | P2P (עמית-לעמית) |
|---|---|---|
| שליטה במצב | השרת — מקור אמת יחיד | לכל לקוח יש מצב מקומי |
| אנטי-צ'יט | גבוה — כל ההחלטות מאומתות על ידי השרת | נמוך — לקוחות יכולים לתמרן נתונים |
| עלות שרת | דורש שרת קבוע (headless) | אין צורך בשרת, אך דורש NAT traversal |
| חביון | גבוה יותר עקב round-trip | נמוך יותר, אך תלוי בעמיתים |
שרת Headless ותשתית
שרת Mirror הוא אותו build של Unity עם הדגל [SyncVar]. עבור משחקי אקשן — שרת headless אחד לכל חדר, עבור משחקי אסטרטגיה עם מעט שחקנים — מספר חדרים לכל instance. הפקידו את האורקסטרציה בידי Unity Game Server Hosting (Multiplay) או Kubernetes עם autoscaling. פרויקטים קטנים מתחילים על VPS (DigitalOcean/Hetzner) עם סקריפטים של systemd. עבור transport של UDP (kcp2k) ודאו שמאזן העומס תומך בהעברת UDP.
הגישה שלנו: מקרה אמיתי
עבור משחק אקשן למובייל עם עד 50 שחקנים בו-זמנית, שילבנו את Mirror עם transport של kcp2k. יישמנו מנגנון חיבור מחדש מותאם אישית באמצעות exponential backoff והתראות push כדי לשחזר סשנים לאחר מעבר לרקע. השרת ה-headless רץ על VPS עם Docker. לאחר בדיקות עומס, השגנו חביון יציב של 10-50 אלפיות השנייה אפילו בעומס מלא, ללא אובדן מנות. אופטימיזציית התעבורה הפחיתה את השימוש ברוחב פס ב-40% בהשוואה לפתרון הקודם מבוסס TCP.
תהליך העבודה על אינטגרציה
- אנליטיקה — ביקורת עיצוב משחק, בחירת מודל (authoritative / P2P), אב טיפוס חביון.
- עיצוב — ארכיטקטורת רשת, תוכנית סנכרון, תרחישי חיבור מחדש ואנטי-צ'יט.
- יישום — הגדרת NetworkManager, transport, SyncVar, פקודות, יצירת אובייקטים.
- בדיקות — בדיקות עומס (50+ לקוחות), סימולציית אובדן מנות, התנהגות ברקע.
- פריסה — הגדרת שרת headless, CI/CD לעדכונים, ניטור.
מה כלול בעבודה
- ארכיטקטורת רשת (בחירת מודל, transport, איזון עומסים).
- הגדרת Mirror: NetworkManager, prefabs, יצירה, פקודות.
- יישום חיבור מחדש וטיפול במעבר לרקע.
- בנייה ופריסה של שרת headless עם systemd/Docker.
- בדיקות עומס תחת 50+ חיבורים בו-זמנית.
- תיעוד API והוראות פריסה.
- תמיכה לאחר השחרור (חודש אחד).
שגיאות נפוצות בעת שילוב Mirror במשחק מובייל
- NetworkIdentity לא נמצא. ה-prefab לא נרשם ב-NetworkManager.spawnPrefabs. יצירה דרך NetworkServer.Spawn תקרוס בזמן ריצה.
- בלבול סמכות. isServer, isClient, isLocalPlayer, hasAuthority — ארבעה ערכים בוליאניים שונים. קוד ב-Update() ללא בדיקת isLocalPlayer רץ על כל הלקוחות.
- ניתוק במובייל. במעבר לרקע, iOS סוגר את ה-socket לאחר כ-10 שניות. Mirror אינו משחזר את החיבור אוטומטית — נדרשת לוגיקה מותאמת אישית.
לוחות זמנים
אינטגרציה בסיסית של Mirror עם סנכרון מיקום/בריאות ופקודות: 1–2 שבועות. מערכת מלאה עם שרת headless, matchmaking, חיבור מחדש ואופטימיזציה למובייל: 1–2 חודשים. העלות מחושבת באופן אישי. צרו קשר לניתוח פרויקט — נעריך את הפרויקט. קבלו ייעוץ על ארכיטקטורת רשת ולוחות זמנים. הזמינו אינטגרציה של Mirror למשחק שלכם — נבצע ביקורת ונציע פתרון תוך יום אחד.







