去年拒绝了上家公司的提干和股票,毅然投身游戏行业,加入了一家创业公司,经历了一年多比996强度还高的加班,我们的第一款游戏终于顺利上线了。我全程参与了我们游戏服务器的开发,从架构到业务。现在游戏上线稳定了,也有时间总结下开发的的一些经验了。

我们游戏的是ARPG游戏,客户端用unity3d,服务器用java。我们有主城,做了场景同步;有组队战斗,做了帧同步;有排位赛,做了匹配。场景同步、帧同步、匹配的服务器都是由我做的,先记录下这几个吧。

我们的匹配规则和大家熟悉的moba游戏一样,根据玩家的分数(一般叫ELO值、或RANK值)匹配实力相近的玩家,并且根据匹配的等待时间扩大匹配范围,直到匹配到足够的人(或者超过一定时间还没凑够人,则补AI机器人)。

不啰嗦,直接上简版代码

 /*** 匹配线程*/private static ScheduledExecutorService sec = Executors.newSingleThreadScheduledExecutor();/*** 每个人需要匹配到的玩家数量*/private static int NEED_MATCH_PLAYER_COUNT = 1;/*** 匹配池*/private static ConcurrentHashMap<Integer,MatchPoolPlayerInfo> playerPool = new ConcurrentHashMap<Integer,MatchPoolPlayerInfo>();static{sec.scheduleWithFixedDelay(new Runnable() {@Overridepublic void run() {matchProcess(playerPool);}}, 1,1, TimeUnit.SECONDS);//每隔1秒匹配一次}/*** 把玩家放入匹配池* @param playerId* @param rank* @return*/public static void putPlayerIntoMatchPool(int playerId, int rank){MatchPoolPlayerInfo playerInfo = new MatchPoolPlayerInfo(playerId, rank);playerPool.put(playerId, playerInfo);}/*** 把玩家从匹配池移除* @param playerId*/public static void removePlayerFromMatchPool(int playerId){playerPool.remove(playerId);}private static void matchProcess(ConcurrentHashMap<Integer,MatchPoolPlayerInfo> playerPool) {long startTime = System.currentTimeMillis();log.debug("执行匹配开始|开始时间|"+startTime);try{//先把匹配池中的玩家按分数分布TreeMap<Integer,HashSet<MatchPoolPlayerInfo>> pointMap = new TreeMap<Integer,HashSet<MatchPoolPlayerInfo>>();for (MatchPoolPlayerInfo matchPlayer : playerPool.values()) {//在匹配池中是时间太长,直接移除if((System.currentTimeMillis()-matchPlayer.getStartMatchTime())>60 * 60 * 1000){log.warn(matchPlayer.getPlayerId()+"在匹配池中是时间超过一个小时,直接移除");removePlayerFromMatchPool(matchPlayer.getPlayerId());continue;}HashSet<MatchPoolPlayerInfo> set = pointMap.get(matchPlayer.getRank());if(set==null){set = new HashSet<MatchPoolPlayerInfo>();set.add(matchPlayer);pointMap.put(matchPlayer.getRank(), set);}else{set.add(matchPlayer);}}for (HashSet<MatchPoolPlayerInfo> sameRankPlayers: pointMap.values()) {boolean continueMatch = true;while(continueMatch){//找出同一分数段里,等待时间最长的玩家,用他来匹配,因为他的区间最大//如果他都不能匹配到,等待时间比他短的玩家更匹配不到MatchPoolPlayerInfo oldest = null;for (MatchPoolPlayerInfo playerMatchPoolInfo : sameRankPlayers) {if(oldest==null){oldest = playerMatchPoolInfo;}else if(playerMatchPoolInfo.getStartMatchTime()<oldest.getStartMatchTime()){oldest = playerMatchPoolInfo;}}if(oldest==null){break;}log.debug(oldest.getPlayerId()+"|为该分数上等待最久时间的玩家开始匹配|rank|"+oldest.getRank());long now = System.currentTimeMillis();int waitSecond = (int)((now-oldest.getStartMatchTime())/1000);log.debug(oldest.getPlayerId()+"|当前时间已经等待的时间|waitSecond|"+waitSecond+"|当前系统时间|"+now+"|开始匹配时间|"+oldest.getStartMatchTime());//按等待时间扩大匹配范围float c2 = 1.5f;int c3 = 5;int c4 = 100;float u = (float) Math.pow(waitSecond, c2);u = u + c3;u = (float) Math.round(u);u = Math.min(u, c4);int min = (oldest.getRank() - (int)u)<0?0:(oldest.getRank() - (int)u);int max = oldest.getRank() + (int)u;log.debug(oldest.getPlayerId()+"|本次搜索rank范围下限|"+min+"|rank范围上限|"+max);int middle = oldest.getRank();List<MatchPoolPlayerInfo> matchPoolPlayer = new ArrayList<MatchPoolPlayerInfo>();//从中位数向两边扩大范围搜索for(int searchRankUp = middle,searchRankDown = middle; searchRankUp <= max||searchRankDown>=min;searchRankUp++,searchRankDown--){HashSet<MatchPoolPlayerInfo> thisRankPlayers = pointMap.getOrDefault(searchRankUp,new HashSet<MatchPoolPlayerInfo>());if(searchRankDown!=searchRankUp&&searchRankDown>0){thisRankPlayers.addAll(pointMap.getOrDefault(searchRankDown,new HashSet<MatchPoolPlayerInfo>()));}if(!thisRankPlayers.isEmpty()){if(matchPoolPlayer.size()<NEED_MATCH_PLAYER_COUNT){Iterator<MatchPoolPlayerInfo> it = thisRankPlayers.iterator();  while (it.hasNext()) {MatchPoolPlayerInfo player = it.next();if(player.getPlayerId()!=oldest.getPlayerId()){//排除玩家本身if(matchPoolPlayer.size()<NEED_MATCH_PLAYER_COUNT){matchPoolPlayer.add(player);log.debug(oldest.getPlayerId()+"|匹配到玩家|"+player.getPlayerId()+"|rank|"+player.getRank());//移除it.remove();}else{break;}}}}else{break;}}}if(matchPoolPlayer.size()==NEED_MATCH_PLAYER_COUNT){log.debug(oldest.getPlayerId()+"|匹配到玩家数量够了|提交匹配成功处理");//自己也匹配池移除sameRankPlayers.remove(oldest);//匹配成功处理matchPoolPlayer.add(oldest);//TODO 把配对的人提交匹配成功处理//matchSuccessProcess(matchPoolPlayer);}else{//本分数段等待时间最长的玩家都匹配不到,其他更不用尝试了continueMatch = false;log.debug(oldest.getPlayerId()+"|匹配到玩家数量不够,取消本次匹配");//归还取出来的玩家for(MatchPoolPlayerInfo player:matchPoolPlayer){HashSet<MatchPoolPlayerInfo> sameRankPlayer = pointMap.get(player.getRank());sameRankPlayer.add(player);}}}}}catch(Throwable t){log.error("match|error",t);}long endTime = System.currentTimeMillis();log.debug("执行匹配结束|结束时间|"+endTime+"|耗时|"+(endTime-startTime)+"ms");}private static class MatchPoolPlayerInfo{private int playerId;//玩家IDprivate int rank;//玩家分数private long startMatchTime;//开始匹配时间private MatchPoolPlayerInfo(int playerId, int rank) {super();this.playerId = playerId;this.rank = rank;this.startMatchTime = System.currentTimeMillis();}public int getPlayerId() {return playerId;}public int getRank() {return rank;}public long getStartMatchTime() {return startMatchTime;}}

简单说下实现思路吧:把参与匹配的玩家都丢入匹配池,每个玩家记录两个属性(分数、开始匹配的时间),每秒遍历匹配池中所有分数段,找出每个分数上等待时间最长的玩家,用他的范围来进行匹配(因为匹配范围会因为等待时间边长而增加,等待时间最长的的玩家匹配范围最大,如果连他都匹配不够,那同分数段的其他玩家就更匹配不够了)。如果匹配到了足够的人,那就把这些人从匹配池中移除,匹配成功;如果匹配人到的人数不够并且没有达到最大匹配时间,则跳过等待下一秒的匹配;如果达到最大匹配时间,还是没匹配到足够的人,则给这个几个人凑机器人,提交匹配成功。

游戏 匹配算法 实现(基于ELO分数、等待时长)相关推荐

  1. Spark---JVM调优之调节executor离堆内存与连接等待时长

    1.executor堆外内存 有时候,如果你的spark作业处理的数据量特别特别大,几亿数据量:然后spark作业一运行,时不时的报错,shuffle file cannot find,executo ...

  2. 简单两步降低呼叫中心等待时长 让用户不再“循环等待”

    本文作者:桂陈 目录 步骤一 准备数据和指标 步骤二 聚焦考核目标 制定改进措施 在文章开头做个小调查,在工作和生活中为了打通客服电话,你愿意等多久?30秒?还是 1 分钟?根据调查,60% 的用户最 ...

  3. 电话卡插到终端服务器通话时长,如何降低呼叫中心通话时长而不影响服务质量...

    原标题:如何降低呼叫中心通话时长而不影响服务质量 新的一周,新的进步! 很多呼叫中心都把平均处理时长(AHT)当作一项绩效指标,并且经常想办法缩短AHT,希望这样能够从坐席的每次通话中节省几秒的时间, ...

  4. 邻居表项的delay_probe_time时长

    delay_probe_time控制首次发送邻居请求报文的等待时长,对于arp协议,内核默认的delay_probe_time时长为5秒钟. struct neigh_table arp_tbl = ...

  5. iPhone限制每天游戏时间,设置某些APP每天最长使用时间,未成年保护 - 《屏幕使用时间》密码设置

    目标 限制小孩子每天玩游戏(iPhone手机上)的时长为1小时,超过时间锁屏,需要提供4位密码进行解锁. [ 测试环境: iPhone系统版本12.3.1+ ] 方法 设置 屏幕使用时间 > & ...

  6. Android动画之仿美团加载数据等待时,小人奔跑进度动画对话框(附顺丰快递员奔跑效果)...

    Android动画之仿美团加载数据等待时,小人奔跑进度动画对话框(附顺丰快递员奔跑效果) 首句依然是那句老话,你懂得! finddreams :(http://blog.csdn.net/finddr ...

  7. 华为可以看游戏时长吗_有效管理孩子游戏时长 只需开启华为P10学生模式

    标签:华为p10(2550)双摄像头(84)华为(5027) 眼看暑假已经接近尾声,不知道各位学生党们的暑假作业有没有完成?而与暑期作业相比,更让家长们头疼的是,如今越来越多的孩子无论是在学校还是在家 ...

  8. 《皇室战争》体验报告:游戏时长被无限拉长

    <皇室战争>体验报告 http://www.gameres.com/678457.html by伊达政宗 2016.8.26 ↑ 笔者不久前才开出的传奇卡电磁炮 前言: 今年上半年的早一些 ...

  9. 另类手游研发数据:游戏是否受欢迎看会话时长

    本文来源:http://www.yuanfangti.com/youxi/chanye/7587.html 玩家获取的相关指标 目前,一名新玩家的获取成本介于0.5-2.5美元之间.玩家获取成本的高低 ...

最新文章

  1. 完美解决Linux服务器tomcat开机自启动问题
  2. 晒桌面 | 非主流五线产品汪的简单原木风工作台
  3. hdu 5636 Shortest Path(Floyd最短路)
  4. Android版同步工具豌豆荚实测 电脑给手机按软件 截图
  5. 使用ACME部署生成阿里云免费HTTPS证书
  6. logistic回归详解(四):梯度下降训练逻辑回归python实现
  7. [Java面试十]浏览器跨域问题.
  8. 日历控件--MaterialCalendarView 详解
  9. CFA大起底:三百六十度无死角详解CFA到底是个啥?
  10. gp3688 uhf2扩频_摩托罗拉GP3688_GP3188写频软件
  11. jacket for matlab,Jacket for Matlab常见问题
  12. 「运维有小邓」搜索日志数据以作为网络安全情报
  13. python搭建邮件服务器_手把手教你使用Python轻松搞定发邮件
  14. 蓝牙baseband概述
  15. TcaplusDB君 · 行业新闻汇编(12月04号)
  16. C语言有符号 存储,【填空题】在C语言中,对于整型变量采用有符号数和_________数两种存储形式。...
  17. Linux端NDS模拟器DeSmuME添加金手指
  18. LINUX压缩和解压和磁盘管理与维护命令
  19. CTF攻防世界之我在里面当炮灰(1)
  20. Promisification

热门文章

  1. 基于皮肤分割的磨皮算法
  2. 对计算机社团未来发展的看法,对社团发展的一些看法
  3. 堆叠降噪自动编码器 Stacked Denoising Auto Encoder(SDAE)
  4. 版式设计:什么是“三率一界”?
  5. 利用DHT网络,爬取bt种子。
  6. Python计算机视觉编程第1章基本的图像操作和处理
  7. -- 44、检索至少选修两门课程的学生学号
  8. 无人机路径规划1:orbslam2+VIO
  9. 微信群如何引流最快?微信群裂变引流工具首选这款
  10. DMOZ编辑申请流程