PvP竞赛游戏设计与实现

已注销

写在前面

这个月接手了两个较大的需求,其中一个是竞赛类游戏,还有一个是抢红包的玩法。两种都涉及人与人之间一定的互动,游戏趣味性相对较高,从数据上反应用户也会相对更加喜欢。

本文先介绍第一个需求的背景、设计与实现。关于抢红包的将放在下一篇文章进行讨论。

需求背景

之前有一阵子,有个很火的小游戏-阳光养猪场,那个养猪合成然后提现的,经常在短视频平台做广告。本质就是用户会每天来进行养猪,到一定等级就可以提现。

产品觉得每个用户与猪场是独立互动的,用户与用户之间没有什么互动,想搞一场养猪大赛来提高用户与用户之间的互动,并通过PvP竞赛机制刺激用户养猪,从而提高用户对产品的各项数据,比如UV、DAU啊等等……

最终效果大概是,用户进入猪场便开启一轮养猪大赛,随机匹配几名对手,在规定时间内按照养猪次数进行排名,最终按照名次发放一定的奖励。

需求分析

首先,一场养猪大赛的流程是用户上线,系统自动为其随机匹配N名对手组成一队。竞赛在有效时间内,需要记录下每名玩家的养猪次数,便于用户实时统计查看对手的次数,确认自己的名次。所以,一场竞赛主要包括竞赛固有信息、队伍成员信息、选手养猪次数信息等核心信息。当然还会有一些其他辅助类信息,比如给用户展示所有对手的头像昵称、地区等级等信息给用户更逼真的游戏体验。

那么进行一场这样的竞赛,需要哪些数据呢?

竞赛固有信息包含开始时间和结束时间,每个竞赛都会有一只队伍,之间是一对一的关系,除非同一批人进行多轮竞赛。每支队伍包括成员数量,每名成员的基本信息以及最核心的实时养猪次数信息。结算时还包括各个排名对应的奖励信息。

可能以上都还好,读者应该和我刚拿到需求的时候一样,对如何匹配对手感到好奇。不禁想到我们在玩LOL之类的游戏是如何匹配的,它会按照我们自己的胜率、段位水平匹配旗鼓相当的对手,这样保证了游戏体验,维持平衡。所以养猪小游戏也是一样,用户的养猪能力也是分三六九等的,我们需要根据用户当前的等级匹配对应等级的对手。至于如何确定用户当前的等级,这就需要你提前设置好一些规则对用户进行划分,在竞赛上线之前,这个划分等级先先提前预埋。比如每天养猪100次的是A等,100~200次的是B等……这样划分好后,我们还需要准备一个反向索引,即A等有哪些用户(uid),B等有哪些,这样在匹配对手时,我们就可以先判断自身是哪一等,再去按规则随机去其他等级的桶里捞uid,这样一队人就组成功了。

技术选型

知道流程,定义好了数据结构,我们就可以进行大致的技术选型。

首先,考虑到竞赛本身是一个有时限的游戏,相当于有一定的过期性,最适合存储数据的首先想到用redis。而且这种实时PvP的竞赛,查询与写入量都很大。

考虑到当结算已经到了结算时间,用户可以领取了,但是redis过期了,数据岂不是没了?这一点倒不用担心,一是本身就可以设置奖励领取时限,告知用户规则;二是我们可以竞赛结算时将结果进行落库等方式持久化。

对于上述讲的匹配对手部分,这主要涉及的是一些离线数据,关乎到用户的历史行为,比如用户的养猪次数,他们的等级划分。目前业务遵循,用户的在游戏内的行为会进行记录,会落到MySQL、ES等存储上,甚至进一步异步抽数到大数据侧。BI同学可以通过大数据分析平台离线计算用户行为,按照业务的指标重新整合出一份数据。比如业务代码会将用户每次养猪行为记录到ES,之后同步到HIVE,BI便可以直接从HIVE表中对用户过去一整天的数据进行统计,最终按照规则划分出ABCD几个等级,也能建立反向索引。所以,这整个形成了一个闭环。(当然我对这个环认知浅薄,明显感觉到了大数据那块说的比较笼统不专业)

具体实现

考虑到实现成本,一期实现的效果是一个单向的竞赛,即用户随机匹配n个对手,但自己并不是他们的对手。仔细想想这样的差别?

如果是双向的,那么每场竞赛是n个成员公用的信息,那么竞赛和队伍、成员的关系是一对一和一对多的关系。而如果是单向的,完全可以理解为竞赛和成员也是一对一的关系,因为对手们不care是谁把“我”当成了对手,只有竞赛的主角才care,只需要创建竞赛的时候记录下匹配的对手名单即可。在技术实现上,单向的也更为简单,只需要将竞赛信息、队伍中对手信息存在一个redis key中即可。如果是双向的,这需要竞赛信息和队伍id保存在一个key,其实队伍id对应n的选手信息保存在一个key,所以成员都要保存一个自己当前对应的竞赛信息key。

在对手匹配上,BI同学可以提前把每个等级用户们的uid进行存储到redis中,结构例如是:A-uidList,B-uidList……在匹配对手时,可能会所有人都访问同一个redis key,所以也会造成热key问题。同时每个等级的uid集合会非常庞大,而我们每次匹配对手也仅仅随机取其中几个。这样势必需要我们将uid集合进行打散到多个桶中,既能解决热key也能解决大key问题。比如A等级10w个uid分到1000个桶中,每个桶仅存放100个uid即可。

至于最核心的养猪次数怎么实时统计呢?既然是单向的竞赛,我可能是周期是昨天今天的48h,对手的可能是今天明天的48h,所以必须将用户养猪次数按最小的时间粒度存储。这里的最小指的是满足业务需求的最大粒度。比如每小时的用户养猪次数记录在同一个redis key下,key的结构可以为:prefix+uid+date+hour。这样在统计每个竞赛的所有成员养猪次数信息时,可以按照竞赛开始时间截止当前时间,批量查询所有符合时间段的redis进行聚合。当然按小时粒度已经足够小了,天级别的粒度也够我们的需求了。

其他一些细枝末节的信息,比如奖励,用户昵称头像等,这些存储到合适的地方都行,有现成接口调用不存储也可,怎么方便怎么来。

双向竞赛

不妨想一想,如果要实现双向的怎么搞呢?

首先匹配对手的方案还是不变,主要变化的是竞赛信息的存储,以及养猪次数的记录。

因为竞赛和队伍、成员已经不能看做是一对一的关系,那么就需要按照设计DB的那套方法对其关系进行梳理。每个竞赛id关联一个队伍id,每个队伍id关联着n名uid,每个uid又关联一个竞赛id,同一队伍的uid关联的当然也是同一竞赛id。

从玩家角度讲,自己首先在redis存储自己当前的竞赛id,然后redis对应会有一个以竞赛id为key的值,里面存放了所有成员uid(这里简化了队伍id)。

由于处于一队的成员们共享同一个竞赛,所以每个人每次喂食都去修改自己的次数即可,完全可以把养猪次数记在竞赛id的key信息里面。链路即用户养猪后,先,查到当前自己的竞赛id,然后再去查竞赛信息,然后给自己的次数+1。别的成员会立刻看到次数上涨了。

看似好像更简单了?

其实问题不在这里,而在于创建竞赛时可能出现并发行为,A选了B和C,B又选了D和E,这可能是同时的。所以这样游戏的整体交互流程需要进行变动,创建竞赛不是由每个人自己创建,而应该由系统统一进行创建分配,让系统“串行”的去创建队伍,分配人员,解决并发。仔细想想,我们开始一局LOL比赛时,匹配等待一段时间是为什么?

机器人

养猪竞赛允许并列名次,如果大家都跟商量好一样,都不养猪,都能拿第一,是不是很不错,一点也不卷。所以我们要给它一个push,更好的调动用户们的养猪积极性。

产品说机器人可不能很呆,次数一成不变,也得像真人一样,过一会儿养一次猪,23333。而且机器人也得有难度之分,就像LOL人机模式普通、一般、末日,其他网页游戏不同等级的BOSS一样。

怎么实现这种mock行为呢?

首先机器人这种模拟真人,完全可以将“过去式”的真人行为表现在机器人身上,一样BI统计好养猪次数,按照小时划分好,业务代码计算机器人养猪次数时进行聚合即可。按照这种思路,相信难度之分完全可以实现。

阅读 138
1 声望
0 粉丝
0 条评论
你知道吗?

1 声望
0 粉丝
宣传栏