הטמעת התאמת משחקים למשחקי מובייל
התאמת משחקים נראית פשוטה: לשים שחקן בתור, למצוא שני, ליצור משחק. בפועל, זו אחת המשימות הלא-טריוויאליות ביותר בשרתים של משחקי מובייל. שחקנים ברמות מיומנות שונות לא צריכים להיפגש, זמני ההמתנה לא צריכים לעלות על 30–60 שניות, השרת צריך לבחור את האזור הקרוב ביותר לשהות מינימלית — וכל זה בצורה אטומית, ללא תנאי מרוץ. בנינו מערכות כאלה למשחקי יריות, משחקי אסטרטגיה ו-battle royales — סוהר, עם בדיקות ותמיכה.
מערכות דירוג: ELO ו-MMR
ההתאמה הפשוטה ביותר משתמשת ב-ELO: לכל שחקן יש דירוג, השרת מחפש יריב בטווח ±N נקודות. הבעיה — עם קהל מצומצם לא נמצא אף אחד, והשחקן ממתין לנצח.
פתרון — הרחב-והמתן: התחל עם טווח חיפוש צר (±50 ELO), לאחר 15 שניות הרחב ל-±150, לאחר 30 שניות ל-±300, לאחר 60 שניות הצע משחק עם בוט או השחקן הזמין הקרוב ביותר. כל הרחבה מבצעת שאילתה מחדש לתור.
אפשרות מתקדמת יותר — Glicko-2: מתחשבת בסטיית דירוג (RD). שחקן חדש בעל RD גבוה — הדירוג שלו לא יציב, התאמה איתו מסוכנת. ככל שהוא משחק, RD יורד. זה מדויק יותר מ-ELO, אבל קשה יותר ליישום. הטבלה שלהלן משווה בין הגישות:
| פרמטר | ELO | Glicko-2 | MMR רב-ממדי |
|---|---|---|---|
| דיוק | נמוך | בינוני | גבוה |
| מורכבות | נמוכה | בינונית | גבוהה |
| התאמה לשחקנים חדשים | איטית | מהירה | בינונית |
| פופולריות | נפוצה בכל מקום | שחמט, משחקים | משחקי יריות, MOBA |
למה להשתמש ב-Redis לתור ההתאמה?
תור התאמה אינו רק FIFO. יישום על Redis:
ZADD matchmaking_queue {elo_score} {player_id}:{timestamp}:{region} קבוצה ממוינת ב-Redis, שבה ה-score הוא דירוג השחקן. מציאת יריב:
ZRANGEBYSCORE matchmaking_queue (min_elo) (max_elo) LIMIT 0 10 Redis מעבד עד 100,000 בקשות בשנייה — פי 5 מהר יותר מ-MySQL. אטומיות היא קריטית: שני עובדי התאמה לא צריכים לקחת את אותו שחקן בו-זמנית. סקריפט Lua ב-Redis מספק את פעולת ה"מצא והסר" האטומית היחידה:
local candidates = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2], 'LIMIT', 0, 1)
if #candidates > 0 then
redis.call('ZREM', KEYS[1], candidates[1])
return candidates[1]
end
return nil
ללא זה, קנה מידה אופקי של ההתאמה מוביל לכפילויות — שחקן אחד מגיע לשני משחקים בו-זמנית. הניסיון שלנו מראה שסקריפטים של Lua מפחיתים באגים ב-90%.
מה כוללת העבודה שלנו?
- ניתוח ז'אנר וקהל: פרופיל שחקנים, בחירת דירוג.
- עיצוב ארכיטקטורת תור: על Redis או Nakama.
- יישום של הרחב-והמתן, אזורי ו-MMR רב-ממדי.
- אינטגרציית לקוח (WebSocket, מצבים
ZADD matchmaking_queue {elo_score} {player_id}:{timestamp}:{region}). - בדיקות: 100+ תרחישים, בדיקות עומס.
- פריסה וניטור: הגדרת לוגים, התראות.
- תיעוד והדרכת צוות.
איך מיישמים התאמה אזורית ומבוססת-שהות?
למשחקי זמן-אמת, השהות היא קריטית. בתחילת החיפוש, הלקוח פינג למספר אזורי שרת (us-east, eu-west, ap-southeast) ושולח את ה-RTT הנמדד יחד עם בקשת התור. ההתאמה מחפשת שחקנים עם אזורים מועדפים חופפים.
Unity Gaming Services תומך בשרתי QoS למדידת השהות. Nakama — דרך מאפייני שחקן מותאמים אישית. יישום מותאם אישית: הלקוח פינג לשרתי UDP echo בכל אזור, ממיין לפי RTT, שולח את 3 האזורים המובילים. אופטימיזציה זו מפחיתה את השהות ב-30%.
איך מיישמים התאמה מבוססת-מיומנות מעבר לדירוג?
לז'אנרים מסוימים, ELO אינו מספיק. משחקי יריות עם יחס K/D, אסטרטגיות עם אחוז ניצחון לפרקציות ספציפיות, battle royale עם היסטוריית מיקום — MMR רב-ממדי. כל ממד הוא עצמאי, ההתאמה מחפשת "קרבה" במרחב רב-ממדי.
יישום פשוט: מרחק משוקלל. משקל K/D: 0.4, אחוז ניצחון: 0.4, דירוג כללי: 0.2. שחקן A: [1.2, 55%, 1500 ELO]. שחקן B: [1.1, 58%, 1480 ELO]. מרחק — נורמה משוקללת של וקטור ההפרש. אם מתחת לסף — המשחק מותר. לפי הנתונים שלנו, MMR רב-ממדי מפחית את מספר המשחקים החד-צדדיים ב-35%.
התאמת קבוצות
קבוצה של 3 שחקנים מחפשת משחק 4 (4v4). הקבוצה מטופלת כיחידה אחת בתור עם דירוג ממוצע בתוספת עונש על פיזור בתוך הקבוצה. אם הפיזור גדול, ההתאמה מוצאת יריבים חלשים יותר כדי לפצות.
יצירת משחק כאשר כל הצדדים נמצאו היא עסקה אטומית: הסר את כולם מהתור, צור חדר, הודע ללקוחות דרך WebSocket או push. אם יצירת החדר נכשלת — החזר שחקנים לתור.
מצבי לקוח
בכניסה להתאמה, הלקוח עובר בין מצבים: ZRANGEBYSCORE matchmaking_queue (min_elo) (max_elo) LIMIT 0 10
לכל מצב יש ממשק משתמש משלו. local candidates = redis.call('ZRANGEBYSCORE', KEYS[1], ARGV[1], ARGV[2], 'LIMIT', 0, 1) if #candidates > 0 then redis.call('ZREM', KEYS[1], candidates[1]) return candidates[1] end return nil מציג אנימציה וטיימר. IDLE→SEARCHING→FOUND→JOINING→IN_MATCH — מסך קצר "נמצא יריב" (2–3 שניות, לא ניתן לבטל). IDLE → SEARCHING → FOUND → JOINING → IN_MATCH — התחברות לשרת המשחק. ביטול אפשרי רק מ-SEARCHING.
בצד הלקוח, מצב ההתאמה הוא FOUND (Kotlin) או JOINING (Swift), המעודכן דרך אירועי WebSocket מהשרת.
זמן ועלות משוערים
התאמת דירוג בסיסית עם הרחב-והמתן ל-2 שחקנים: 1–2 שבועות. התאמה אזורית, MMR רב-ממדי, משחקי קבוצות: 1–2 חודשים. העלות מחושבת באופן אישי לאחר ניתוח הז'אנר והקהל. צרו קשר — נעריך את הפרויקט שלכם תוך יומיים.
מקרה בוחן: איך הפחתנו את זמן הקצאת החיפוש ב-40%
לפרויקט אחד עם 50,000 משתמשים פעילים יומיים, השתמשנו בהרחב-והמתן עם צעד של 10 שניות וסף אזורי דינמי. כתוצאה מכך, זמן ההמתנה הממוצע ירד מ-45 שניות ל-27 שניות. המפתח היה בחירת מקדם הרחבת הטווח הנכון.







