游戏中的商城和抽奖系统设计

发表于2015-12-29
评论4 6.4k浏览

游戏中的商城和抽奖系统设计


  最近两天的设计遇到了一些商城和抽奖相关系统的设计,之前执着于游戏乐趣的设计却一直冷淡了这两块系统,事实上真的动起手来却发现有时候在这两块的系 统设计和制作中,存在很多混乱的东西——太多需要整理成机制的东西一直没有去思考,以至于需求堆多了之后写死的垃圾代码和数据表的列数都在骤增。于是总结 了一些关于这两个系统的设计和思考,随笔记下。

商城系统背后的供货系统的设计

  最早和大多系统策划的理解一样,我也觉得商城就是一个买东西的地方,最近新加入团队的策划妹子设计了商城系统后,发现自己的一个错误——忽略了在商城背后还应该有一个“供货系统”。

  供货系统是一个什么玩意儿?顾名思义,也就是你商城里面卖什么东西的系统。其实乍一想,不就是配一张表填点东西和价格进去就行了么?但事实上真不是这 样简单的,看完了详细设计后,我发现其实这个供货系统背后有一个不算很复杂的机制存在。(为了避免一些问题,于是决定图全用Windows自带小画家而不 是Office软件制作)。

[随笔记录]游戏中的商城和抽奖系统设计 ...

买入系统:一个Currency与GameItem或者Currency转换的过程,也就是通常我们都 能想到的买入,不论是买入钻石还是用钻石买金币、金币买道具、钻石买道具,都是通过Currency(钻石、金币、RMB)来购买道具 (GameItem)或者金币、钻石(Currency)。

供货系统:这其实是在读表(我们假设这个表名为MarketMall),从表中读取(或者说过滤出)一 批道具是要在商城进行贩卖的,当然这里并不是直接就开卖这些道具了,最早我是这么错误的理解的,但是现在发现由于活动等的需要,其实读取这个表只是第一 步,而读取这个表的本身意义只是将这些要贩卖的道具从整个世界全部种类的道具中归纳过滤出来,或者直白点说就是“缩小排查范围”。

货源到库存货源:当读取表后,我们有了货源,货源与道具不同的地方在于,货源应该有一些道具没有的信 息,这些信息也就是建立这个MarketMall的最初意义,我们需要将其中的一部分放入库存货源。通常情况下,策划会有类似需求就是:几点到几点卖什 么、卖的东西轮流在切换;什么日子特别卖什么;每周的什么时候卖什么。那么这里就是根据当前服务器的时间从“货源”中过滤出一批来。这里的过滤,其实是一个机制的核心点所在, 如果只是初步理解,你会想到的是“策划往往需求的是某个时间段”,但是如果从一个游戏设计师的角度出发,思考结果是什么?“策划也许会要求:当玩家满足某 个条件的时候,可以购买什么”,举一些简单的例子:当玩家完成某个副本后可以购买A,当玩家在游戏中结婚后可以购买B,当玩家在游戏中消费满998元之后 就可以购买C。那么我们可以看出,其实这个过滤条件并非只有“时间段”这么简单,于是,我们在这里穿插一个脚本给策划,当然因为时间段是最常用的,我们仍 然应该把时间段作为表的通用列放在MarketMall里面,以提高判断效率(虽然地球人感觉不到)。

库存货物到柜台货物:这里就是将过滤的东西告诉客户端的过程,但是之所以添加出这一步来,其实我是将他 们记录在了服务器内存中,一旦当客户端提出申请要购买东西的时候,我优先看这个表里面是否有申请购买的道具,并且判断和理性,如果不合理,那么这个表就脏 了,我需要重新计算,并告诉服务器错了,如果该道具合理,那么我认为这张列表仍然是干净的。

  在这个设计环节中,有一个小小的技巧——因为目前大多手游并没有必要去做心跳,所以服务器并不知道客户端在线,因此不可能做到服务器广播给客户端,那 么其实我们应该让客户端去敲服务器的门——客户端计算时间到点了、或者登陆的时候敲服务器的门,提醒服务器该看看东西脏了没。

  之所以去设计这样一个机制,其实还是考虑到大多时候运维人员并不乐意去为你维护这种活动数据表,并不是工作不认真,而是每次上百台服务器搞一次,出错率又高,时间开销又大,劳命伤财,不如在设计师这里就预先安排掉一些该做的事情。

X次抽奖中必定有Y次出Z种物品的设计

  这是现代策划设计抽奖转盘的时候常见的需求(现在连转盘都吹牛了,这世界……)——“我有一个极品装备,希望玩家抽1000转盘,一定能抽到1个,脸 好点的可能1、2次就有了,脸黑的要到第1000次”。其实如果我们把这个问题简化到这步来做,这是在给自己找麻烦,从游戏设计师的角度来看这个问题,其 实是“我有Z种物品,他们分别会在X次抽奖中出Y个”,当然其中的X和Y,应该是最小次数的最小公倍数,比如a1物品要在30次内必定出1个,a2物品要 在17次内出2个,那么此时,X=510,Z=[a1,a2],Y[a1]=17,Y[a2]=60。

  这种设计我们不能采用传统的针对每一次抽取来做转盘,也许好的数值策划能够设计出来(在我看来,数值设计师应该是那种当我写了一大段逻辑和复杂的算法 之后,我告诉他他能想到某个我没听说过的数学算法,完美解决这个问题的人,不是那种只能推推加减乘除4则运算的),但我想我的能力不够,我才用代码的方式 解决这个问题:

假如我们掉落的东西结构是:
DropItem = {
   ItemIds:Array<Int>; //物品的id,这里忽略StackCount等因为项目需要的特殊需求,因此也就这么一个东西了。之所以采用IntArray来,这是考虑到每次抽取可能会抽到多个东西的情况,如果确定每次就掉一个,那么就可以不用Array。
}
而上面说到的掉落物,我们整理一个结构就是:
LootItem = {
   ItemIds:Array<Int>; //物品Id,原理同DropItem的ItemIds
   DropCountMin:Int; //最少会抽到多少个
   DropCountMax:Int;//最多能抽多少个,和最少的相等,则形成通常我们说的Z个,如果不等,则会现有一次随机。
}
那么我们要在每个角色(或者每个账号,这看转盘的基本规则)下记录一个数组:

var WillDropItem = new Array<Int, DropItem>();

  这个就是角色在今后的X次抽奖中获得东西的结果,当这个数组长度<=0的时候,就是应当重新计算数组的时候。计算这个数组内容的核心在于:

var turntableSet:Int = 0; //转盘上已经多少个坑被占了。
for (loot in Z){
   var loopCount:Int = (loot.DropCountMin < loot.DropCountMax)?
                        Math.round(Math.random()*(loot.DropCountMax-loot.DropCountMin)+loot.DropCountMin):
                        loot.DropCountMax; //这轮会抽到多少个。
   var lastIndex:Int = 0; //上次落在第几次抽取上。
   var setTimes:Int = 0; //已经成功放上转盘了多少个了。
   while (setTimes < loopCount && turntableSet < X){
       lastIndex = Math.round((lastIndex + Math.random()*X)%X);
       if (canPutHere(lastIndex )==true){ //主要负责判断转盘这个坑能否放奖品了,写法各异,略。
           putLootToTurntable(loot.ItemIds); //这个主要负责把东西放上转盘,写法各异,略。
           setTimes += 1;
           turntableSet += 1;
       }
   }
}
  这样,我们已经把所有“重要抽取”放进转盘里面了,至于空的坑,就用“不重要的奖品”去填充了,具体做法各异,略。

 


如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引