笃行
首页
个人 & 心法
互联网/硬件后台
游戏基础架构
UE 引擎
游戏业务
AI / 大模型
数据结构与算法
机器学习数学
通用基础
GitHub
首页
个人 & 心法
互联网/硬件后台
游戏基础架构
UE 引擎
游戏业务
AI / 大模型
数据结构与算法
机器学习数学
通用基础
GitHub
  • 游戏业务实现(6 年)

    • 游戏业务实现
    • 多模板游戏活动框架
    • 赛事玩法(Bracket / 瑞士轮 / BO 系列)
    • 业务幂等性设计
    • 跨渠道限购与游戏侧分布式事务落地
    • Redis 房间推荐列表
    • 排行榜 / 榜单
    • 抽卡 / 掉落与保底
    • 邮件 / 发奖补偿
    • 匹配 / 段位(MMR)
    • 游戏反作弊
    • 游戏经济与成长
    • 游戏与互联网后台的本质差异
    • 业务代理 · 支付 · 商城

匹配 / 段位(MMR)

匹配队列 + 隐藏分(MMR) + 扩圈搜索——在"匹配质量"和"等待时长"之间动态权衡,把水平相近的人凑成一局。

一句话结论

用隐藏 MMR 扩圈搜索,在匹配质量、等待时长、池内人数的三角里动态取舍。

场景问题

匹配系统的目标一句话说清:"尽快把水平相近的人凑成一局",但这两个目标天生打架:

  • 公平 vs 快:要匹配质量高(双方实力接近),就得等更多人进池、挑更合适的对手 → 等待变长;要秒进对局,就得放宽实力差 → 局内碾压、体验崩。
  • 段位分 ≠ 真实力:玩家看到的段位(黄金、钻石)是激励用的展示分,用它匹配会不准。真正驱动匹配的是隐藏分 MMR。
  • 组队匹配:5 人车队,怎么算车队整体实力?按平均分匹配会被"一个高分带四个低分"钻空子。
  • 匹配后的更新:一局打完,赢了加多少、输了扣多少?加太多分数膨胀,加太少玩家没成长感。
  • 人数稀薄:冷门模式/深夜/高分段,池子里根本没几个人,硬等就是无限转圈。

核心矛盾:匹配质量、等待时长、池子人数三者不可能同时最优。所有匹配算法都是在这个三角里做动态取舍。

实现方案

一:隐藏分 MMR(ELO / Glicko)

匹配用的是隐藏分,不是展示段位。经典 ELO:按双方分差算期望胜率,赛后按"实际结果 - 期望"调分。

// ELO:预期胜率 + 赛后调分
func expectedWin(myMMR, oppMMR float64) float64 {
    return 1.0 / (1.0 + math.Pow(10, (oppMMR-myMMR)/400.0)) // 分差 400 → 约 91% 胜率
}

func updateMMR(myMMR float64, win bool, expected float64, K float64) float64 {
    actual := 0.0
    if win { actual = 1.0 }
    return myMMR + K*(actual-expected) // 赢强敌加得多,赢弱敌加得少
}
  • K 因子:控制调分幅度。新手用大 K(快速定级),老手用小 K(分数稳定)。
  • Glicko/Glicko-2:在 ELO 上加了 RD(分数可信度) 和波动率——长期不玩的人 RD 变大,回归后一场定级更快、调分更猛。比 ELO 更贴合"很久没打的人分数不可信"的现实。

展示段位与隐藏分解耦

玩家看到的是"钻石 II 星",系统用的是隐藏 MMR。段位只负责激励和防掉分焦虑(连胜升星、有保护局),MMR 负责真实匹配。两者可以有偏差——高手小号的段位低但 MMR 高,靠 MMR 匹配才不会去虐菜。

二:匹配队列 + 扩圈搜索

玩家进入匹配池,系统按 MMR 找区间内的对手;等得越久,可接受的分差区间越宽(扩圈):

type Ticket struct {
    UID       uint64
    MMR       float64
    EnqueueAt int64
}

// 可接受分差随等待时间线性放宽:等得越久越"不挑"
func acceptRange(waited time.Duration) float64 {
    base := 50.0
    return base + float64(waited/time.Second)*10.0 // 每秒放宽 10 分
}

func (m *Matcher) tryMatch(t *Ticket, now int64) *Ticket {
    r := acceptRange(time.Duration(now-t.EnqueueAt) * time.Second)
    // 在 [MMR-r, MMR+r] 区间找池内最接近的对手
    return m.pool.FindClosest(t.MMR, r)
}
  • 扩圈:base + 时间×斜率,前期严格保质量、后期放宽保等待。
  • 超时兜底:等太久还没人 → 放宽到最大区间、跨区匹配、或填 AI 机器人(PVE 化保证不空等)。
  • 双向满足:A 接受 B 不代表 B 接受 A(B 刚进池区间还窄)——凑局要双方区间都覆盖对方。

打个比方:匹配系统像公园相亲角——刚上场时大爷大妈严格按条件挑对象(同段位、同模式、同大区),列表里一个不合适就等;等得越久,家里人越焦急,筛选条件被一档一档松开(分差可接受范围随时间线性放宽),身高、年龄、户籍逐条让步;实在再等下去要凉,宁可"先随便配一个凑合聊"(跨区/填 AI 机器人)也不让人空等。类比失效边界:相亲不追求实时,等两周、两个月都算正常;游戏匹配是秒级刚需——业界公认必须在 15 秒左右给玩家结果,超过就掉队/退出。所以匹配系统必须主动放宽段位换出对手,代价就是"体验波动":偶尔碰上强度悬殊的对局。这也是为什么"匹配质量、等待时长、池内人数"是不可能三角,任何算法都是在这三角里做取舍,而不是消灭取舍。

三:组队匹配

车队要折算成一个"队伍实力值",且防止"大腿带妹"钻空子:

折算方式问题适用
队内平均 MMR一个高分带四个低分 → 均值被拉低,虐菜不单独用
队内最高 MMR过于保守,车队总匹配到强敌偏保守场景
平均 + 分差惩罚队内分差越大额外加权,抵消带飞常用

实践里常用"平均分 + 组队惩罚项":队内实力差距越大、队伍规模越大,额外上调匹配实力(让带飞车队去碰更强的对手),压制 carry 收益。

四:连败保护与体验平滑

纯 MMR 匹配数学上公平,但体验未必好。常见修正:

  • 连败保护:连输 N 局,临时下调匹配到的对手强度(或降低掉分),避免玩家被劝退。
  • 新手保护期:前若干局只匹新手/AI,且大 K 快速定级。
  • 胜率收敛:系统隐含目标是让每个人长期胜率趋近 50%——这既是"匹配准"的标志,也是留存的关键。

为什么这么做

为什么匹配用隐藏 MMR 而不是展示段位?
展示段位是为了激励设计的(连胜多升、有保护局、不轻易掉段),它被人为"扭曲"过,不反映真实力。用它匹配会让高手小号去虐菜、让运气上分的人被吊打。MMR 是纯粹的实力估计,匹配准不准全看它,所以两套分必须解耦。

为什么要扩圈而不是固定区间?
固定窄区间保证质量但人少时永远匹配不到(无限转圈);固定宽区间秒匹配但经常被碾压。扩圈让系统先按高标准找、找不到再逐步妥协,在"质量"和"等待"之间沿时间轴动态滑动——这是三角取舍的工程解。

为什么组队要惩罚项?
按平均分匹配时,"1 个大腿 + 4 个鱼"的均值不高,却能靠大腿一个人打崩对面——这是对匹配公平性的套利。加分差/规模惩罚,让这种车队去匹配更强的对手,把 carry 的收益还回去,堵住套利。

为什么别的选择不行

方案为什么不行
用展示段位匹配段位被激励规则扭曲,高手小号虐菜、运气分被吊打
固定分差区间太窄→人少时无限转圈;太宽→经常被碾压,体验崩
组队按纯平均分"大腿带鱼"套利,均值不高却单方面碾压
只用 ELO 不管不活跃长期不玩的人分数早已失真,回归后乱杀或被杀;Glicko 的 RD 才能快速重定级
K 因子全程固定大 K 老手分数抖动、小 K 新手定级慢;要分阶段
匹配不到就无限等冷门模式/深夜/高分段直接卡死;必须超时兜底(扩圈到底/跨区/填 AI)

池子稀薄是最现实的敌人

高分段、冷门模式、深夜时段,池子里可能就几个人。此时"匹配质量"是奢望,保证不无限转圈才是第一位:设超时上限,到点就跨区、跨模式、或填 AI 机器人。匹配算法在纸面上再漂亮,也架不住池子里没人——先保证有得匹,再谈匹得好。

沉淀结论

  • 匹配的三角:匹配质量、等待时长、池内人数不可能同时最优,所有设计都是这三者的动态取舍。
  • 隐藏分驱动匹配:ELO/Glicko 算 MMR,展示段位只管激励,两者解耦;Glicko 的 RD 解决"不活跃玩家分数失真"。
  • 扩圈搜索:可接受分差随等待时间放宽,前期保质量、后期保等待,超时有兜底(跨区/填 AI)。
  • 组队折算:平均分 + 分差/规模惩罚,堵"大腿带鱼"的套利。
  • 体验修正:连败保护、新手保护期、胜率向 50% 收敛——数学公平之外还要体感公平。
  • 池子稀薄优先保"有得匹":设超时上限兜底,别让玩家无限转圈。

与 game-infra 的衔接

匹配成功后要把这一队人分配到一个对局服(DS)上开局——DS 是有状态单进程,涉及开区/选服/清场,属于 game-infra 的有状态服务范畴。匹配(局外、可水平扩的互联网式服务)与对局(局内、有状态实时)的分界,正是 游戏与互联网后台的本质差异 讲的那条线。

记忆口诀

  • 核心三角:匹配质量 / 等待时长 / 池内人数(不可能同时最优)
  • 两套分:隐藏 MMR(真匹配·ELO/Glicko)/ 展示段位(纯激励)/ 必须解耦
  • 扩圈:分差随时间放宽 / 前期保质量后期保等待 / 超时兜底(跨区·填 AI)
  • 组队防套利:平均分 + 分差/规模惩罚 / 堵"大腿带鱼"

内容来源

综合整理自竞技匹配 / 段位系统的实现经验;ELO/Glicko 评分、扩圈搜索与组队折算为业界通用做法;匹配与对局服的分界呼应本域 游戏与互联网后台的本质差异。

扩展阅读

  • tournament-esports —— 赛事场景中的应用:本篇讲匹配算法(MMR/ELO/Glicko、扩圈搜索),tournament-esports 讲赛事场景的种子分配与队伍对齐(Seed by rating / Snake seeding / Random within tier、跨赛区时区约束)。

自测:合上资料能说清楚吗?

  1. 匹配系统的"不可能三角"是哪三者?为什么它们无法同时最优?
参考答案

匹配质量(双方实力接近)、等待时长、池内人数。要质量高就得等更多人进池挑对手,等待变长;要秒进就得放宽分差,被碾压。池子人数决定前两者的天花板——人少时质量和速度都保不住。所有算法都在这三角里动态取舍。

  1. 为什么匹配要用隐藏 MMR,而不是玩家看到的展示段位?两者关系如何?
参考答案

展示段位为激励设计(连胜升星、有保护局、不轻易掉段),被人为扭曲,不反映真实力;用它匹配会让高手小号虐菜、运气分被吊打。MMR 是纯实力估计,负责真匹配。两者解耦:段位管体感,MMR 管公平。

  1. "扩圈搜索"是什么?相比"固定分差区间"好在哪?
参考答案

可接受分差随等待时间线性放宽(base+时间×斜率),前期严格保质量、后期放宽保等待,超时有兜底。固定窄区间人少时无限转圈,固定宽区间经常被碾压;扩圈让系统先按高标准找、找不到再逐步妥协,沿时间轴动态滑动。

  1. 对比组队折算的三种方式(纯平均 / 纯最高 / 平均+惩罚),为什么最终用第三种?
参考答案

纯平均会被"1 大腿带 4 鱼"套利——均值不高却单方面碾压;纯最高过于保守,车队总碰强敌。平均+分差/规模惩罚:队内实力差越大、规模越大就额外上调匹配实力,让带飞车队碰更强对手,把 carry 收益还回去,堵住套利。

  1. Glicko 相比经典 ELO 多解决了什么问题?
参考答案

ELO 只有单一分值,无法表达"分数可信度"。Glicko 加了 RD(分数偏差/可信度) 和波动率:长期不玩的人 RD 变大,回归后一场定级更快、调分更猛,解决"很久没打的人分数已失真"的问题——比 ELO 更贴合活跃度现实。

最近更新: 2026/9/10 11:38
Prev
邮件 / 发奖补偿
Next
游戏反作弊