如何将对局配对集成到您的多人回合制游戏中
-> 本文基于對局配對文档。如果您遇到问题或差异,请确保参考原始指南,因为它们更新得更频繁。
以下示例将帮助您测试核心对局配对玩家流程,即:
在共享的主机集群上创建匹配器实例,
在匹配器配置中定义规则和设置,
最后,通过我们的玩家票和API管理玩家流程。
将我们的匹配器实现到您的游戏中有五个步骤:
第一步是创建一个账户并使用我们的策略游戏示例。Voilà,您已(技术上)完成一半!您只需在您的游戏中集成匹配器(见步骤5)。
现在,您不应该盲目跟随在网上找到的JSON示例,因此强烈建议您根据上述规则适应您的回合制游戏。步骤2(“探索配置”)是我们的“如何阅读”,深入探讨每个對局配對规则功能(“探索配置”)。
步骤3(“查看实例详情”)涵盖您的个人特定匹配器,以确保它已部署并与您的游戏设计配合工作。
第4步,顾名思义(“4. 测试票API”),是关于测试您的玩家对局配对请求是否被称为票的匹配器接收。
第5步(“将对局配对集成到您的游戏中”)重点介绍了如何在您的引擎项目中集成匹配器。
如果您遇到故障排除问题,我们的学习中心中有更多的故障排除技巧。
1. 设置免费层
注册您的免费Edgegap账户,然后导航至匹配器仪表板页面。
从那里开始,点击创建匹配器,然后输入:
匹配器的名称——仅供您自己参考,例如 quickstart-dev,
然后,上传以下简单示例作为回合制游戏的JSON配置:
{
"version": "1.0.0",
"max_deployment_retry_count": 3,
"ticket_expiration_period": "5m",
"ticket_removal_period": "1m",
"profiles": {
"casual-example": {
"application": {
"name": "my-game-server=>CHANGE-THIS-NAME-HERE",
"version": "2024.01.30-16.23.00-UTC=>CHANGE-THIS-HERE "
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 1
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"10": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"30": {
"beacons": {
"difference": 50
}
},
"60": {
"league_rank": {
"max_difference": 2
}
},
"180": {
"beacons": {
"difference": 100,
"max_latency": 500
}
}
}
}
},
"competitive-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"60": {
"beacons": {
"difference": 50
}
},
"180": {
"beacons": {
"max_latency": 250
}
}
}
}
},
"challenger-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"180": {
"beacons": {
"difference": 50
}
},
"240": {
"beacons": {
"max_latency": 250
}
}
}
}
}
}
}{
"version": "1.0.0",
"max_deployment_retry_count": 3,
"ticket_expiration_period": "5m",
"ticket_removal_period": "1m",
"profiles": {
"casual-example": {
"application": {
"name": "my-game-server=>CHANGE-THIS-NAME-HERE",
"version": "2024.01.30-16.23.00-UTC=>CHANGE-THIS-HERE "
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 1
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"10": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"30": {
"beacons": {
"difference": 50
}
},
"60": {
"league_rank": {
"max_difference": 2
}
},
"180": {
"beacons": {
"difference": 100,
"max_latency": 500
}
}
}
}
},
"competitive-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"60": {
"beacons": {
"difference": 50
}
},
"180": {
"beacons": {
"max_latency": 250
}
}
}
}
},
"challenger-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"180": {
"beacons": {
"difference": 50
}
},
"240": {
"beacons": {
"max_latency": 250
}
}
}
}
}
}
}{
"version": "1.0.0",
"max_deployment_retry_count": 3,
"ticket_expiration_period": "5m",
"ticket_removal_period": "1m",
"profiles": {
"casual-example": {
"application": {
"name": "my-game-server=>CHANGE-THIS-NAME-HERE",
"version": "2024.01.30-16.23.00-UTC=>CHANGE-THIS-HERE "
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 1
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"10": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"30": {
"beacons": {
"difference": 50
}
},
"60": {
"league_rank": {
"max_difference": 2
}
},
"180": {
"beacons": {
"difference": 100,
"max_latency": 500
}
}
}
}
},
"competitive-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"60": {
"beacons": {
"difference": 50
}
},
"180": {
"beacons": {
"max_latency": 250
}
}
}
}
},
"challenger-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"180": {
"beacons": {
"difference": 50
}
},
"240": {
"beacons": {
"max_latency": 250
}
}
}
}
}
}
}{
"version": "1.0.0",
"max_deployment_retry_count": 3,
"ticket_expiration_period": "5m",
"ticket_removal_period": "1m",
"profiles": {
"casual-example": {
"application": {
"name": "my-game-server=>CHANGE-THIS-NAME-HERE",
"version": "2024.01.30-16.23.00-UTC=>CHANGE-THIS-HERE "
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 1
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"10": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"30": {
"beacons": {
"difference": 50
}
},
"60": {
"league_rank": {
"max_difference": 2
}
},
"180": {
"beacons": {
"difference": 100,
"max_latency": 500
}
}
}
}
},
"competitive-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"60": {
"beacons": {
"difference": 50
}
},
"180": {
"beacons": {
"max_latency": 250
}
}
}
}
},
"challenger-example": {
"application": {
"name": "my-game-server",
"version": "2024.01.30-16.23.00-UTC"
},
"rules": {
"initial": {
"match_size": {
"type": "player_count",
"attributes": {
"team_count": 2,
"team_size": 5
}
},
"beacons": {
"type": "latencies",
"attributes": {
"difference": 25,
"max_latency": 100
}
},
"league_rank": {
"type": "number_difference",
"attributes": {
"max_difference": 0
}
},
"selected_maps": {
"type": "intersection",
"attributes": {
"overlap": 1
}
},
"selected_beacons": {
"type": "intersection",
"attributes": {
"overlap": 1
}
}
},
"expansions": {
"30": {
"beacons": {
"difference": 40,
"max_latency": 150
}
},
"180": {
"beacons": {
"difference": 50
}
},
"240": {
"beacons": {
"max_latency": 250
}
}
}
}
}
}
}(友情提醒,请确保更改应用程序 name 和 version 以匹配您的应用和版本!)
如果没有出现验证错误,点击 创建并开始 并等待过程完成。这将导致一个新的免费集群启动,使用您的简单示例匹配器。
您现在可以继续下一步。
2. 探索配置
独特的竞争性回合制游戏规则
特别是对于回合制游戏,您可以为特定游戏模式的规则和设置定义多个 對局配對配置文件:
限制两个玩家之间的排名差异,以便更休闲的游戏,
限制排名差异仅允许具有相同排名的对手进行排名游戏,
让玩家提供他们的地图偏好,并为每个人选择一个合适的地图,
添加Hub选择UI以限制对手到指定的Ping信标,
将對局配對延迟限制为防止匹配远离的玩家的最大阈值,
限制對局配對延迟为最大差异,以最大化ping的公平性,
在允许更多玩家时,使用不同的应用版本分配更多CPU或内存,
以组形式加入,用于预制大厅或不超过团队规模的团队补充。
在理想条件下开始,并扩展限制以确保快速匹配:
随着时间推移慢慢放宽延迟限制以找到更多玩家,
慢慢增加允许的排名差异以找到更多玩家,
增加最高等级(挑战者)的扩展时间,因为可用的玩家更少。
为晋级赛创建更高级别的票,以与更强的对手匹配。
定义单独的作弊者配置文件以确保被标记的作弊者或具有大量管理报告的玩家不会对排名比赛中合法玩家的体验产生负面影响。
语义版本控制
每个新版本使用 语义版本控制 通过解释格式major.minor.patch来清晰传达更改的影响:
major 版本包含破坏性更改,需要进行集成审查,
minor 版本包含实质性的向后兼容性改进,
patch 版本包含错误修复和小改进。
一些部署可能导致错误。我们尝试通过在不需要客户确认的情况下最多重试max_deployment_retry_count次来解决此问题。
为了确保意外的客户端崩溃或废弃的票不会滞留并占用您的匹配器资源,票将在ticket_expiration_period后被取消,导致其状态更改为CANCELLED,然后在ticket_removal_period后被永久删除。
我們的對局配對邏輯的核心配置在 對局配對配置文件中。每个配置文件是一个完全隔离的对局配对队列,指向具有预定义CPU和内存(RAM)资源的应用版本。
對局配對规则 在初始规则集中必须满足以便将玩家分组在一起,每个由三个属性定义:
名称由您选择,例如- 比赛规模,
规则类型,也称为操作符,例如- 玩家计数,
最后是操作符属性,例如 团队计数 或 团队规模。
玩家计数规则
这是定义需要多少玩家才能启动分配的特殊规则:
团队计数 指的是团队数量,一个团队可用于合作模式或自由模式,
团队规模 指的是每个团队的玩家人数。
我们的简单示例演示了一个拥有2名玩家的合作游戏。
请注意,“玩家计数”规则是在您的初始配置规则中是必需的且只能定义一次。
延迟规则
使用此规则为所有玩家提供最低可能的ping值。一旦客户测量并提交他们对所有可用信标的往返时间(延迟),Gen2只会在针对ping信标测量的ping值中的特定差异内考虑匹配。这是一个对您的玩家基础进行“软”拆分的解决方案,使跨邻近地区的匹配成为可能,特别是改善不太繁忙地区的匹配速度。使用max_latency防止匹配与位于遥远地方的玩家进行。
您现在可以继续下一步。
我们的示例信标规则在上面的最初设置为"difference": 50, "max_latency": 200:
Alice和Bob会匹配,因为北京被丢弃(>200)而其他的都在| A-B | <50内:
Alice {蒙特利尔: 12.3, 纽瓦克: 45.6, 达拉斯: 59.9, 北京: 264.4}; 并且
Bob {蒙特利尔: 27.3, 纽瓦克: 32.4, 达拉斯: 23.1, 北京: 252.2}。
Charlie和Dave将不会匹配,因为对于达拉斯信标来说| C-D | >50:
Charlie {蒙特利尔: 5.7 纽瓦克: 44.2, 达拉斯: 59.5, 北京: 263.2}; 并且
Dave {蒙特利尔: 57.8, 纽瓦克: 32.0, 达拉斯: 24.2, 北京: 272.3}。
请注意,“延迟规则”只能在您的初始配置规则中定义一次。
3. 查看实例详情
在我们的仪表板中查看 初始化后的新匹配器详情:

状态表示服务健康,可能是在线,离线或错误。
标识符帮助Edgegap员工在您需要故障排除支持时快速找到您的匹配器。
启动时间可用于追踪最新更新的时间。
大小对应于我们的定价层级之一。
API网址将由游戏客户端和游戏服务器用于与Gen2通信。
Swagger网址是我们提供的浏览API模式的开放API规范GUI。
身份验证令牌是一个由游戏客户端和游戏服务器用于身份验证的唯一秘密令牌。
要使用API测试您的新匹配器,您将需要Swagger网址,API网址和身份验证令牌。
您现在可以继续下一步。
4. 测试票API
首先,打开您的Swagger网址以在swagger GUI中检查您的开放API模式
单击“Matchmaker”标题下方的/...swagger.json网址以打开原始JSON模式:

将此页面保存为文件在您的驱动器上(CTRL/CMD+S)。
打开您的Postman应用程序并登录您的免费账户。
从上一步导入您的swagger.json文件:
保持Postman集合选中,
选择查看导入设置并更改设置参数生成为示例。
确认导入,这将导致新的集合出现在左侧的集合列表中,标题为Matchmaker。

查看更多操作,打开选项卡授权并选择:
认证类型 - API密钥,
键 - “授权”
值 - 在此处插入您的Auth Token值,
添加到 - 头部。
按下(CTRL/CMD+S)或保存图标以保存更改。您的Postman选项卡中的橙色点应该消失。
在您的Matchmaker集合中,选择票和创建对局配对票,打开新选项卡。
选择正文选项卡以预览您的玩家票请求:
注意player_ip设置为null - 这将导致使用自动添加到请求的IP地址(请参阅服务器到服务器集成以获取替代方案),
配置文件指的是您的對局配對配置文件,
属性包括对您的匹配器规则的值,在本例中是延迟规则,
规则玩家计数是唯一不需要在票中包含任何属性的规则。
注意:请确保参考样本的Swagger的导入配置。
单击发送以检查对您的玩家票请求的响应:
id是您的唯一对局配对票ID,请保存此信息以便稍后查看您的票,
配置文件确认對局配對配置文件的选择,
群体ID是分配给每张票的唯一群体ID,单个玩家代表为1人的群体,
player_ip是玩家的公共IP地址,不管使用何种识别方式。
分配设置为null以指示该票尚未匹配或分配到服务器,
创建于提供有关何时为游戏UI使用创建玩家票的信息,
状态指示票的当前状态,所有票均从搜索状态开始(有关详细信息,请参阅對局配對过程)。
通过再次点击发送创建第二张票,以便我们的两个玩家匹配并启动服务器。
在您的Matchmaker集合中,选择{ticketId}和读取對局配對票。
从上一步的响应中输入票ID并点击发送。

审查玩家票的更新分配:
状态更改为匹配找到首先,而分配保持为null以指示玩家已匹配且正在分配服务器,

再次点击发送检查您的票,并审查玩家票的更新分配:
状态更改为主机分配,并且分配中包含分配服务器的详细信息。
在我们的仪表板中检查您的新部署:
请注意每个部署都标记了所有票ID和配置文件,以增加可追溯性。

尝试从您的游戏客户端连接到分配的服务器。
一旦您确认能够无故障地连接到您的部署并完成了测试,停止您的部署以腾出帐户的容量用于下一个构建。
您现在可以继续下一步。
5. 将对局配对集成到您的游戏中
Edgegap的對局配對集成:
与游戏客户端集成以管理玩家票,
与游戏服务器集成以:
处理通过其票传递的玩家偏好,
可选支持回填以在开始后添加或替换玩家。
对于游戏客户端,我们建议在對局配對过程中使用游戏内UI提供票状态更新,以获得最佳玩家体验。了解:
在游戏客户端中,请确保您正在处理不可重试错误:
HTTP 404 未找到 - 票已被删除,
HTTP 500 内部服务器错误 - 暂时的服务中断。
在游戏服务器中,处理玩家偏好和初始服务器上下文。不需要API集成:
读取注入的环境变量(Gen2)以检索初始玩家的對局配對数据。
阅读注入的环境变量(应用版本)以获取版本特定参数、设置(玩家容量)和秘密。
阅读注入的环境变量(部署)以获取部署的信息,例如IP地址、位置等。
一旦玩家连接,游戏服务器和游戏客户端启动加载场景以执行同步步骤(例如选择和加载地图/场景/关卡)。我们建议使用完整的3D场景、类似大厅的社交UI或带有进度条的加载屏幕来表明初始化正在进行。
一旦游戏客户端完全加载,玩家便加载/前往主要游戏场景。
可选的,游戏服务器可能会创建和管理回填和玩家容量(添加或替换离开的玩家)。
确保您的部署将正确停止,使用注入的DELETE_URL,如果:
没有玩家加入比赛,
所有玩家已离开比赛,
比赛正确结束。
恭喜您,您已完成Edgegap Matchmaker集成!如果您想了解更多,请阅读我们学习中心中的所有相关内容。