Реалізація матчмейкингу для мобільної гри
Матчмейкинг здається простим: покладіть гравця в чергу, знайдіть другого, створіть матч. На практиці це одна з найскладніших серверних задач у мобільних іграх. Гравців з різним рівнем умінь не повинні зустрічатися, час очікування не повинен перевищувати 30-60 секунд, сервер повинен вибрати найблизший регіон для мінімальної затримки—все це атомарно, без гонок стану.
Рейтингові системи: ELO та MMR
Найпростіший матчмейкинг по ELO: у кожного гравця є рейтинг, сервер шукає опонента з рейтингом ±N очків. Проблема—на вузькій аудиторії такі не знаходяться, гравець чекає вічно.
Рішення—expand-and-wait: початковий діапазон пошуку вузький (±50 ELO), через 15 секунд розширюється до ±150, через 30 секунд—до ±300, через 60 секунд пропонується матч з ботом або найближчим доступним гравцем. Кожне розширення—повторний запит до черги.
Складніший варіант—Glicko-2: враховує невизначеність рейтингу (RD, rating deviation). Новий гравець має високий RD—його рейтинг нестабільний, матчмейкинг з ним ризикований. За мірою ігор RD знижується. Точніше за ELO, але складніше у реалізації.
Архітектура черги
Матчмейкингова черга—не просто FIFO. Реалізація на Redis:
ZADD matchmaking_queue {elo_score} {player_id}:{timestamp}:{region}
Sorted Set, де score = рейтинг гравця. Пошук опонента:
ZRANGEBYSCORE matchmaking_queue (min_elo) (max_elo) LIMIT 0 10
Атомарність критична: два матчмейкери не повинні одночасно забрати одного гравця. 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
Без цього при горизонтальному масштабуванні матчмейкера один гравець попадає у два матчі одночасно.
Регіональний матчмейкинг та latency-based
Для real-time затримка критична. Клієнт при старті пошуку пингує кілька серверних регіонів (us-east, eu-west, ap-southeast) та відправляє вимірювані RTT разом з запитом у чергу. Матчмейкер шукає гравців з перекриваючимися бажаними регіонами.
Unity Gaming Services Matchmaker підтримує QoS-сервери для вимірювання latency з коробки. Nakama—через кастомні властивості гравця. Кастомна реалізація: клієнт пингує UDP-echo-сервери у кожному регіоні, сортує по RTT, відправляє топ-3 регіони.
Умінь за межами рейтингу
Для деяких жанрів ELO недостатньо. Шутери з K/D ratio, стратегії з win rate по конкретним фракціям, bataille royale з placement history—мультимерний MMR. Кожне вимірювання незалежно, матчмейкинг шукає «близість» у мультимерному просторі.
Проста реалізація: зважена відстань. Вага K/D—0.4, win rate—0.4, загальний рейтинг—0.2. Гравець A: [1.2, 55%, 1500 ELO]. Гравець B: [1.1, 58%, 1480 ELO]. Відстань—зважена норма вектора різниці. Якщо менше порога—матч допустимий.
Партійний матчмейкинг
Група з 3 гравців шукає 4-й матч (4v4). Група—одна одиниця у черзі з усередненим рейтингом + штраф за розброс у групі. Якщо розброс рейтингів у групі великий—матчмейкер знаходить слабіших опонентів, щоб компенсувати.
Створення матчу при знаходженні всіх сторін—атомарна транзакція: видалити всіх з черги, створити room, сповістити клієнтів через WebSocket або push. Якщо створення room упало—повернути гравців у чергу.
Стани клієнта
Клієнт при входженні у матчмейкинг переходить по станам:
IDLE → SEARCHING → FOUND → JOINING → IN_MATCH
Кожен стан—окремий UI. SEARCHING показує анімацію та таймер. FOUND—короткий екран «Опонент знайден» (2-3 секунди, не можна скасувати). JOINING—підключення до ігрового сервера. Скасування доступне тільки з SEARCHING.
На клієнті стан матчмейкингу—StateFlow (Kotlin) або @Published (Swift), оновлюється через WebSocket-события від сервера.
Графік
Базовий матчмейкинг по рейтингу з expand-and-wait для 2 гравців: 1-2 тижні. Регіональний матчмейкинг, мультимерний MMR, партійні матчи: 1-2 місяці. Вартість розраховується індивідуально після аналізу жанру та аудиторії.







