先讲一个概念
不论是方法一 (卡片在服务器端早已序列产生好)
或方法二 (服务器端即时产生乱数,再依照乱数大小生成卡片)
如果你使用的是“真乱数”
那么,不论是用什么样的方式去生成抽卡结果
那对抽卡的人来说
期望值都不会有影响
1%的机率长期统计下来仍然是1%
会有影响的,是抽卡结果的常态分布
例如,方法一可以避免“连续N单大暴死”
以及“十抽十五星”这种状况发生
所以,担心被领石帐/工作室抢走的假设
完全是多余的
因为对方也可能帮你把杠龟的卡通通抽走
一来一往,等于没影响
接下来会针对两种方式做更仔细的解释跟分析
可能会有点无聊
方法一:
卡片在服务器端早已序列产生好
这就像是圣诞刷箱抽袜子,从一个固定的卡池抽奖的概念
这里先声明
卡池的大小并不一定要能包含所有卡片
因为再生成卡池的时候,也可以使用乱数
例如可以生成一百张卡的卡池
其中那一张五星卡,随机决定是哪一张五星等等
另外关于池的大小也是一个问题
当你的池子数量太大的时候,等于没作用
例如以1000抽为一池
依然可能发生900抽没五星,最后100抽10五星这种状况
所以如果有用卡池功能的话
这卡池的数目不能太大
卡池又有分,多人(全服务器或单一服务器节点)共用一个卡池,以下称公池
或是个人独立的卡池,以下称个人池
如果是个人池
那么,这种设计主要是避免“连续N单大暴死”或是“十抽十五星”
这种状况发生
但很显然FGO没这么做
为常常听过有几百抽0五星之类的状况
或是十抽好几只五星的状况出现
如果是公池
那没有讨论的必要了
在一大堆人共用卡池的状况下
跟方法二的效果是一样的
方法二:
服务器端即时产生乱数,再依照乱数大小生成卡片
这个太简单,我不知道该怎么解释
总之就是丢骰子那样的模式
每次抽卡都等于是丢一次100面骰,丢中就中奖
接下来讲关于程式的部分
方法二在实作上是最简单的方案
就叫系统生成一个乱数,再用乱数去查表
他们应该会有一个设定档案,决定了卡池的机率,记录了每张卡的机率是多少
再用那张表,决定该生成什么卡片出来
这种程式不需要考虑其他节点,或是各种锁定、同步问题
所以说这是最简单的方式
方法一,在实作上比较麻烦一点
如果是个人池的话,你需要去储存他的卡池状况
在他下次抽卡时,再拿出看看要给他什么卡
如果是公池,那是非常麻烦的事情
如果A、B两个人同时抽卡的话
你需要考虑两者的执行绪安全问题
因为这可能会导致A、B两个人都抽到同一张卡
这个bug在抽卡上造成的影响,听起来好像没什么
但如果用购物网站来当例子的话.....
商品的数量只剩下1个
A、B两个人同时按下结帐,结果两个人都同时成交了。
大概是这种层级的问题
所以,实际上在写这段程式码的时候要这样:
1. A锁定卡池
2. A抽卡
3. A释放卡池
B要等A把1~3都做完,才能抽卡
同一时间可能有几百,甚至上千上万人在抽卡
每个人都要一个个排队、抽卡
这对服务器来讲是吃力不讨好的工作
而且没有必要搞成这样
同样的道理,把乱数交给第三方也只是把系统搞得更麻烦,更复杂而已
另外关于这个影片
https://youtu.be/Nehvzf9esQs
发生的原因可能是这样
有些程式语言中的乱数
在初始化的时候要给他一个seed
而同样的seed会导致生成同样的乱数数列出来
而程式设计师给他的seed
是用“时间”,而且单位只到“秒”
例如php当中就是 srand(time()); 这样
这会导致同样时间点击抽卡
抽到的卡都一样的状况
我的工作虽然不是转蛋游戏
但也是线上机率游戏,跟游戏、机率有扯上点关系
像这种机率游戏,常常会有人怀疑站方作弊
或是在机率上动手脚
但营运单位才不会搞这种麻烦事
因为规则上就已经写白了
营运单位就是要赚你的钱
拿大乐透来举例
如果你看得懂游戏规则的话
你就能看出“买一张大乐透平均下来会损失20元”这件事情
但还是有人会去赌
所以在评估“最差的状况”的时候
基本上拿营运方公告的数值下去估算就可以了
以FGO来讲,就是“真随机,1%SSR”
转蛋游戏跟线上机率游戏,有本质上不一样的地方
转蛋游戏的营运方跟玩家的对立,不像线上机率游戏那样强
反而是,防止极端值出现,比较能避免玩家弃坑
这也是为什么B服之前搞了个120抽保底出来的原因
像是保底机制,比较有可能的实现方法
就是在没抽中的时候,在他个人纪录上+1
当这个纪录超过某个数值的时候,偷偷调高抽卡机率,这样
B服那时候的状况是直接设成100%,所以看起来很明显
这种做法比个人池的方式,还要来的简单方便的多
我认为在推断卡池的机制的时候
应该要把日服跟B服分开来看
嘛.... 因为B服有前科