码农之家

如何做一个卡牌游戏

简单分享下在前司做的一个有意思的项目

如果是喜欢 DNF 且喜欢混迹游戏社区(DNF助手) 薅羊毛的同学,可能玩过这个项目。印象中应该是叫 DNF Lite,在助手的侧边栏。

简单来说这个游戏主要内容如下:

  • 大富翁式探索(摇骰子探索地图,类似于蚂蚁森林巡护保护区)
  • 抽卡(卡牌游戏的必备项目,重复角色升星、不同卡池)
  • 战斗(回合制,有暴击之类的特殊效果)
  • 养成(获得装备与角色提升数值、能降低探索难度)
  • 随机事件(除了Boss战之外,有各种增益状态)

当然这个游戏已经下线了,并且我也离职了(没有竞业),咱也不想招惹南山必胜客

背景

当时 DNF 手游准备上线,于是决定搞一波预热,所以打算做个小游戏提升用户活跃。(结果由于版号的原因没能上)

由于部门内在探索 node ,拓展前端边界,加上 Server 人力不足,所以这个项目的:前端、后端、运维都有参与(拿着不如宇宙条实习生的工资干三个正职的活)

运维部分主要是:

  • 申请机器资源
  • 资源接入CI/CD

可以忽略,就不讲了

这个项目主要有意思的地方在于 server 部分,前端部分就是做了一些动画(外包出去)、调试角色在地图上的移动等等体力活。

架构

技术选型为:

  • FE

    • react
    • umi(react 框架)
  • Sever

    • node
    • egg(node框架)
    • redis
    • mongo
    • mongoose(ORM)
    • 消息队列(内部自研)
  • 运维

    • K8S
    • Dockers

问题

由于并不是专业server大家看看乐子就行~

用户状态

一般来说,用户状态都是存在数据库的,但是游戏场景不同,需要频繁读写。如果将数据落库,那么会对数据库造成较大的压力,一个服务实例挂了还好,也就影响这个实例上的用户,如果数据库挂了直接凉凉。所以直接落库不太可行,得想办法。

可以在 redis 进行缓存,等用户交互完再落库。

那么这就有一个问题,怎么知道什么时候用户操作完了呢?

一般来说上长连接能解决这个问题,但是上长连接对服务器的开销太大,并且本质上来说这并不是一个真正的游戏,数据交互没有那么频繁(角色移动、战斗实时操作等)所以采用的还是 Http 请求。

这里用了一个折中的办法,设置一个活动时间,超出这个时间没有这个用户的请求进来,就认为该用户已经“断开链接”,将数据落库。(类似于防抖)

奖池抽奖

众所周知,卡牌游戏的核心就是抽奖。

理所当然,奖池里有各种等级的奖励,每种等级的概率不同。每个等级的奖励里面有多种物品,每个物品的概率应该相等。

这里有两种情况:

  • 不用限制总数,保持整体概率就行
  • 限制产出,概率低的同时还有数量上限

第一种情况问题不大,直接随机生成一个 0 ~ 10000 的整数,将这个数除以100,获得的结果用于判断抽中了什么等级的物品。

比如生成的数字是 1111, 计算后为 11.11,最低一档的奖品概率为 50%, 11.11小于50所以是最低的一档,以此类推。如果概率最低的是三位小数,那就生成一个 100000 以内的数。

获得抽中奖品的档次后,再生成一个这个档次物品总数以内的随机数,这样就选中了最终结果。如果是有什么物品概率 up ,那就把这些物品按比例求最小公倍数,然后在公倍数的范围内随机。

麻烦的在于限制产出的情况,比如有一个隐藏角色一周内只能产出200个,如果按前面的做法计算,有可能在一个小时内这些角色就没了(欧皇多的情况),也可能一周过去了 200 个一个都没抽走(全是非酋)

这里想了一个相对公平一点的方法,即过固定时间就往奖池里塞一个。 比如一周产出7个那就一天塞一个,也就是一周的数量最多7个,可以少不能多(总不能到最后看着池子里还有直接塞给最后抽的人吧)

战斗计算

由于有暴击之类的设定,最开始进行战斗的时候都是每回合进行一次计算。测试的时候发现如果请求量上来了,很有可能这回合的动画放完了,下回合的结果还没返回,流程就卡住了。

后来想到这个项目战斗时不能操作,也就是进入游戏后,结果就已经注定了,那干脆在加载时就把每回合的数据生成,返回一个数组,前端直接按这个数组播放动画就行。还节省了服务开销,美滋滋~

结语

本文是大概介绍,具体技术细节太多就不展开了(感兴趣的同学可以留言),虽然本职是前端,但偶尔搞搞其它东西还是挺有意思的~

原文链接:https://juejin.cn/post/7216895561608216633 作者:tomhyluo