使用Unity Game Services实现ELO匹配

解读

面试官抛出这道题,核心想验证三件事:

  1. 你是否真正用过Unity Game Services(UGS)而不仅停留在“听过”层面;
  2. 能否把ELO算法与UGS的Matchmaker、Lobby、Relay三大模块无缝串起来,而不是各说各话;
  3. 对国内版号+实名+防沉迷合规环境下的分池、分段、分时段匹配策略有没有落地经验。
    回答时务必把“ELO值如何存、如何算、如何同步”和“UGS哪条API负责哪一步”一一对应,让面试官一听就知道你独立上线过

知识点

  1. UGS Matchmaker规则脚本(Rule Script)语法:必须会写bucket、attribute、expansion三段式,否则无法按ELO分段。
  2. ELO-K值动态曲线:国内竞技手游普遍用“K=40→16递减”模型,前端只算预测值,最终分值由Cloud Code二次校验防止客户端作弊。
  3. 属性同步顺序:Lobby创建时把PlayerElo写进Lobby Data → Matchmaker把该值映射到Queue Attribute → Relay分配服务器后通过NetworkManager.Singleton广播给房间。
  4. 合规分池:实名未成年玩家必须在规则脚本里加**"age<=18"独立bucket,且22:00-08:00时段直接返回matchmaking failed (1040)**,否则审核被打回。
  5. 性能红线:Matchmakerexpansion interval不得小于3s,否则国内低端安卓会出现**“匹配超时”**崩溃,被华为应用市场拒审。

答案

分五步落地,全部用UGS官方API,不自己搭服务器。

第一步:在Unity Dashboard新建**“EloQueue”**,规则脚本核心三行:

attributes { 
  playerElo: 1500..3000 
}
bucket {
  range: 100
  expansion: 5s +50
}

range 100即±50分快速匹配,5秒后扩大到±100,兼顾速度与精度。

第二步:客户端Authentication成功后,把本地PlayerPrefs.GetInt("Elo",1500)写进Lobby Data

var lobbyOptions = new CreateLobbyOptions{
    Data = new Dictionary<string, DataObject>{
        {"elo", new DataObject(DataObject.VisibilityOptions.Public, localElo.ToString())}
    }
};

注意:DataObject只能存string,转int时先TryParse防崩。

第三步:调用MatchmakerService.StartMatchmakingAsync,把elo作为Attribute塞进去:

var matchmakingResponse = await MatchmakerService.StartMatchmakingAsync(
    new MatchmakingPlayer{
        Attributes = new Dictionary<string, object>{
            {"playerElo", int.Parse(localElo)}
        }
    },
    "EloQueue"
);

此时Matchmaker会按规则脚本自动分桶,无需客户端写任何排序逻辑

第四步:匹配成功后拿到AllocationID,用Relay分配服务器:

var joinCode = await RelayService.GetJoinCodeAsync(allocation.AllocationId);
NetworkManager.Singleton.GetComponent<UnityTransport>().SetRelayServerData(
    allocation.RelayServer.IpV4, 
    (ushort)allocation.RelayServer.Port, 
    allocation.AllocationIdBytes, 
    allocation.Key, 
    allocation.ConnectionData
);

Elo值随Lobby Data自动同步到房间内,开局即可做平衡队伍逻辑。

第五步:比赛结束,客户端只算预测分值真正的Elo增减Cloud Code里用K=30*(1-预测胜率)重新计算,再写回Cloud Save

const k = 30;
const delta = Math.round(k * (actualScore - expectedScore));
await Data.Set("elo", newElo, playerId);

Cloud Code里还能顺手做连胜衰减、恶意挂机扣分,保证数值安全

至此,零后端、全UGS、可过审的ELO匹配闭环完成。

拓展思考

  1. 如果项目要上字节小游戏渠道Relay UDP会被企业防火墙拦截,需降级为WebGL + WebSocket,此时要把Matchmakerallocation strategy改成**“WebGLOnly”自建WebSocket中继**,否则匹配成功却连不上服务器。
  2. Elo与MMR双轨制:国内玩家对“星钻王者”段位更敏感,可在Cloud Code里维护hidden MMR做匹配,display rank只做展示,两值分离降段保护赛季重置更灵活。
  3. 实时校验:UGS的Rate Limit100次/100秒/玩家Elo更新必须走批量聚合(一局游戏只写一次),否则高频写会触发429导致玩家掉线,华为渠道审核会以此为由打回。