智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...

导航

游戏设计中有经典的计算公式

Posted on 2014-08-08 12:58  Bill Yuan  阅读(22745)  评论(0编辑  收藏  举报

转自:http://www.zhihu.com/question/20396457

没做过DOTA类游戏的平衡,LOL也玩得不多,只能说下我自己的办法

第一个问题:符文系统和天赋系统存在的必要性和对战局的影响?

先引入两个概念:
Ehp:effective health point 有效生命
Ehp受自身的HP、闪避、防御、护甲、韧性、格挡、招架、免伤、技能系数、等级加成、各种抗性等等数值影响。
举例:假如你有1W生命,且有50%减伤(与对方命中无关),那你的ehp就是2W。

Edps:effective damage per second有效输出
Edps受自身的攻击力、命中值、暴击率、暴击效果、穿刺、急速、等级加成、各种属性攻等等数值影响。
举例:假如你的输出间隔是1S,100%命中,攻击力为100,暴击率为100%,暴击效果为150%,则你的edps为150。

则在在两个人之间的胜负结果,始终可以用下面的公式来表示:

所谓的符文、天赋、装备等等各种系统,归根结底都是对人物数值的影响,本质上并无任何区别,每新增一个系统,不过是把每个属性的数值成长上限放高而已。所有以上没有哪个系统是必要存在的,今天可以做成符文系统,明天还可以换成后宫加成系统,表现形式不同而已。至于对战局的影响,最优解的问题会谈到。




第二个问题:关于数值成长中最优解的存在
(开会去。。。晚点填)



==============================================================


ELO等级分体系
(很久之前做过的PPT,东西太多有点乱,粘一部分上来)

简介


ELO等级分体系是根据它的推广者埃洛(elo) 名字命名,它是一种以数值表示的体系,将等级差别转化 为分数或取胜概率。

理论基础


粗略看一下很多比赛的成绩表,能够看出某个选手的表现是有起伏的,即有“状态”之分。强手未必恒定表现好于弱手,每人之状态在不同的日子不同的比赛里都会有好坏不同。而总的来说整个生涯里每一点上,一名选手的表现将大致围绕在某个平均水平上下波动,有时会有背离,而出现大背离的情况比出现小背离的情况频率要低。

于是,有了ELO体系的第一个假定:可以建立这么一个等级量表,某个体在这个表里的不同表现将正常分布。

第二个假定简单地说:在某一个特定的实力范畴里,尽管可能存在实力上的差异,但总的来说可以预期没有谁比谁好或差很多。由此可以定量划分区间。

等级量表-区间尺度

等级差别是可能性方面的唯一重要因素。

指对阵双方谁有多大机会胜/负/和,唯一影响因素在于他们之间的等级差别,这样排除了难以确定的“状态好坏”(第一假定),也排除了具体谁对谁的影响(第二假定)。

因此即使出现“爆冷”或 “克星”,也视为偶发情况,因为ELO体系是建立在样本足够大量的统计基础上的。

标准分类间隔(附表)

即某一个等级里最高与最低之间的差别。在国际象棋里,FIDE(即国际棋联)采用每200分为一个间隔的分类法,并且给每一个间隔取一个名字或排名以更好分辨。

常态分布函数

强手未必总是胜过弱手,于是就使用一个“常态分布函数来代表一名选手的波动表现,这是统计学上的概念。从这个函数里可以得出另一个函数叫“常态概率函数” ,这个就定义了根据比赛对抗结果可得出的等级差别,或者根据已知的等级差别可得出的预期比赛对抗结果。它是这样的:

P(D)这个值,就是以等级分差别D为自变量计算出来的预期取胜可能性(预期得分率),它可近似用下面这个公式来计算:

举例一

Ra:表示队伍A当前的等级分
Rb:表示队伍B当前的等级分
Sa表示队伍A的比赛结果,胜利S值为1,平局S值为0.5,失败S值为0
Ea:表示A在这场比赛中的胜负值期望

Eb:表示B在这场比赛中的胜负值期望

其中Ea+Eb=1,也就是胜率和为1

举例二

一个等级分1950的选手,她的对手等级分1700分,那么她取胜的机会有多大呢? 
等级分差别D=1950-1700=250,代入上面那么近似计算公式,就是:

也就是说,她胜利的机会有80.8%,大约是81%。事实上,看下表,即可得知等级分差别在246-256之间的强手预期取胜可能性就是81%。不用计算。

上面看不懂?没关系!

 

当前等级分公式

Rn = Ro + K * (W - We)

Rn是赛事后的新等级分;Ro是赛事前的原等级分;K是单局得分的等级分点值,它是一个系数,取值有约定的;W是实际对局得分(胜得1、和得0.5);We是在原等级分基础上的预期对局得分。 
这个公式是用来在连续基础上(也就是每局或每个赛事之后)计算新等级分的。它把最新的表现调整考虑进先前的等级分中。它的逻辑意义是一个选手的表现高于他的预期得分以及低于他的预期失分。

系数K决定了事前等级分和本次表现分的相应权数。K值越高,那么越近的赛事表现的分量越大。反之则反之。一般K值范围从10到32。下面是一个K取值参考表:

举例三

一个队伍等级分1650,比赛中分别与等级分为2050、1270和1550的三个队伍对了阵,战胜了低分的两个,逼和了最高分的那个。问新等级分是多少?

K值取10;Ro=1650;W(胜2和1) = 1 + 1 + 0.5 = 2.5

We = P(1650 - 1270) + P(1650 - 1550) + P(1650 - 2050)
=0.910 + 0.638 + (1-0.921)
=1.627

套入第二个公式,即Rn = Ro + K * (W - We) = 1650 + 10 * (2.5 - 1.627) = 1659

也就是她在这次赛事后,等级分升了大约9分,成为1659。

举例四

某竞技场队伍等级分2350,11场比赛5胜6负,K取32;对阵11场情况如下:

已知原等级分Ro为2350,已知本次比赛实际对局得分W=5,已知K值取32,已知(总)预期对局得分We为5.24, 计算时可省略为5.2,那么根据公式(2),Rn = Ro + K * (W - We),新等级分Rn = 2350 + 32 * (5 - 5.2) = 2343.6 , 大约是2344,比之前下降了。

课后小思考


①K值如果往上取,会造成什么情况?
②K值如果往下取,会造成什么情况?
③WOW与DOTA天梯明显的不同在什么地方?
④以上的不同为WOW带来了什么漏洞?
⑤WOW里队伍积分和个人积分分别有什么用?

 

回复:

1、卡马克快速平方根

平方根倒数速算法

它让计算平方根倒数的计算速度提高了4倍,导致了3D游戏的革命。没有这个算法,恐怕到现在3D游戏里的物体仍然没影子(想想看CS的画面)。

这个算法在上世纪就出现了,但是在10年前才引起游戏圈的关注,算法的原理很清楚,但是作为算法核心的那个常数,至今没人知道它是怎么得出来的。有传言说是外星人入侵互联网时留下了这个常数。游戏业各位大牛纷纷在源代码后面写上了一句注释:what the fuck?

 

在3D图形编程中,经常要求平方根或平方根的倒数,例如:求向量的长度或将向量归一化。C数学函数库中的sqrt具有理想的精度,但对于3D游戏程式来说速度太慢。我们希望能够在保证足够的精度的同时,进一步提高速度。

 

Carmack在QUAKE3中使用了下面的算法,它第一次在公众场合出现的时候,几乎震住了所有的人。据说该算法其实并不是Carmack发明的,它真正的作者是Nvidia的Gary Tarolli(未经证实)。

 

//
// 计算参数x的平方根的倒数
//
float InvSqrt (float x)
{
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i >> 1); // 计算第一个近似根
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x); // 牛顿迭代法
return x;
} 该算法的本质其实就是牛顿迭代法(Newton-Raphson Method,简称NR),而NR的基础则是泰勒级数(Taylor Series)。NR是一种求方程的近似根的方法。首先要估计一个与方程的根比较靠近的数值,然后根据公式推算下一个更加近似的数值,不断重复直到可以获 得满意的精度。其公式如下:

 

函数:y=f(x)

 

其一阶导数为:y'=f'(x)

 

则方程:f(x)=0 的第n+1个近似根为

 

x[n+1] = x[n] - f(x[n]) / f'(x[n])NR最关键的地方在于估计第一个近似根。如果该近似根与真根足够靠近的话,那么只需要少数几次迭代,就可以得到满意的解。

 

现在回过头来看看如何利用牛顿法来解决我们的问题。求平方根的倒数,实际就是求方程1/(x^2)-a=0的解。将该方程按牛顿迭代法的公式展开为:

 

x[n+1]=1/2*x[n]*(3-a*x[n]*x[n])将1/2放到括号里面,就得到了上面那个函数的倒数第二行。

 

接着,我们要设法估计第一个近似根。这也是上面的函数最神奇的地方。它通过某种方法算出了一个与真根非常接近的近似根,因此它只需要使用一次迭代过程就获得了较满意的解。它是怎样做到的呢?所有的奥妙就在于这一行:

 

i = 0x5f3759df - (i >> 1); // 计算第一个近似根超级莫名其妙的语句,不是吗?但仔细想一下的话,还是可以理解的。我们知道,IEEE标准下,float类型的数据在32位系统上是这样 表示的(大体来说就是这样,但省略了很多细节,有兴趣可以GOOGLE):

 

bits:31 30 ... 0
31:符号位
30-23:共8位,保存指数(E)
22-0:共23位,保存尾数(M)所 以,32位的浮点数用十进制实数表示就是:M*2^E。开根然后倒数就是:M^(-1/2)*2^(-E/2)。现在就十分清晰了。语句 i>>1其工作就是将指数除以2,实现2^(E/2)的部分。而前面用一个常数减去它,目的就是得到M^(1/2)同时反转所有指数的符号。

 

至于那个0x5f3759df,呃,我只能说,的确是一个超级的Magic Number。

 

那个Magic Number是可以推导出来的,但我并不打算在这里讨论,因为实在太繁琐了。简单来说,其原理如下:因为IEEE的浮点数中,尾数M省略了最前面的1,所 以实际的尾数是1+M。如果你在大学上数学课没有打瞌睡的话,那么当你看到(1+M)^(-1/2)这样的形式时,应该会马上联想的到它的泰勒级数展开, 而该展开式的第一项就是常数。下面给出简单的推导过程:

 

对于实数R>0,假设其在IEEE的浮点表示中,
指数为E,尾数为M,则:

 

R^(-1/2)
= (1+M)^(-1/2) * 2^(-E/2)

 

将(1+M)^(-1/2)按泰勒级数展开,取第一项,得:

 

原式
= (1-M/2) * 2^(-E/2)
= 2^(-E/2) - (M/2) * 2^(-E/2)

 

如果不考虑指数的符号的话,
(M/2)*2^(E/2)正是(R>>1),
而在IEEE表示中,指数的符号只需简单地加上一个偏移即可,
而式子的前半部分刚好是个常数,所以原式可以转化为:

 

原式 = C - (M/2)*2^(E/2) = C - (R>>1),其中C为常数

 

所以只需要解方程:
R^(-1/2)
= (1+M)^(-1/2) * 2^(-E/2)
= C - (R>>1)
求出令到相对误差最小的C值就可以了上面的推导过程只是我个人的理解,并未得到证实。而Chris Lomont则在他的论文中详细讨论了最后那个方程的解法,并尝试在实际的机器上寻找最佳的常数C。有兴趣的朋友可以在文末找到他的论文的链接。

 

所以,所谓的Magic Number,并不是从N元宇宙的某个星系由于时空扭曲而掉到地球上的,而是几百年前就有的数学理论。只要熟悉NR和泰勒级数,你我同样有能力作出类似的优化。

 

上有人做过测试,该函数的相对误差约为0.177585%,速度比C标准库的sqrt提高超过20%。如果增加一次迭代过 程,相对误差可以降低到e-004的级数,但速度也会降到和sqrt差不多。据说在DOOM3中,Carmack通过查找表进一步优化了该算法,精度近乎 完美,而且速度也比原版提高了一截(正在努力弄源码,谁有发我一份)。

 

值得注意的是,在Chris Lomont的演算中,理论上最优秀的常数(精度最高)是0x5f37642f,并且在实际测试中,如果只使用一次迭代的话,其效果也是最好的。但奇怪的 是,经过两次NR后,在该常数下解的精度将降低得非常厉害(天知道是怎么回事!)。经过实际的测试,Chris Lomont认为,最优秀的常数是0x5f375a86。如果换成64位的double版本的话,算法还是一样的,而最优常数则为 0x5fe6ec85e7de30da(又一个令人冒汗的Magic Number - -b)。

 

这个算法依赖于浮点数的内部表示和字节顺序,所以是不具移植性的。如果放到Mac上跑就会挂掉。如果想具备可移植性,还是乖乖用sqrt好了。但算法思想是通用的。大家可以尝试推算一下相应的平方根算法。

2、简单公式

减法公式:受到伤害=敌人攻击力-防御力
除法公式:受到伤害=敌人攻击力*敌人攻击力/(防御力+敌人攻击力)
乘法公式:受到伤害=敌人攻击力*(1-免伤率)
经典概率算法
圆桌概率算法
属性池概念

3、双曲线公式:COC中大量粗暴的使用

4、ELO算法:貌似wow和11平台的天梯都有用到

5、超级机器人大战伤害计算公式

{(武器攻击力×(对应能力值+气力)×武器的地形适应÷200-装甲值×(防御值+气力)÷200)×攻击机体地形适应修正}÷100×{100+(武器的最大射程-和敌人的距离)×3÷100}*特殊技能修正 

1L是正解,我是来解释的。

武器攻击力,即攻击时选择武器的列表中标示的攻击力。

能力值,如果武器是格斗武器则是机师的格斗值,若是射击武器则是机师的射击值,若是跨系武器则取两者的较高值。

武器地形适性是指武器具体属性中对应的“空宇地海”的那个,S为1.2,A为1,B为0.8,C为0.6(B和C不太确定……)

装甲值,机体的装甲数值,这个不用解释吧……气力是机师的气力,防御值是机师的防御数值……机体地形适应修正指的是机体能力具体属性中的对应的“空宇地海”的那个,参数与前面相同。“射程”不用解释吧……特殊技能的修正指的是机师技能对伤害的影响,比如机师独有的某些技能,以及强攻者等等技能的影响。

具体举个例子:

我方某机体(机体的地形适性空S宇S陆A海A)用一种射程为7、攻击力3000的光束射击武器(光束武器一般都是空A宇A/S陆A海B)从水中射击距离为5的敌方1600装甲值的机体。我方机师气力150,格斗210射击190,有强攻者技能(气力130以上伤害为1.2倍);敌机师气力100,防御200。

此时我方攻击造成的伤害是{[3000×(190+150)×0.8÷200-1600×(200+100)÷200]×1}÷100×[100+(7-5)×3÷100]×1.2=2017

6、D&D中的各种检定计算

7、卡马克卷轴算法

8、仙境传说:护甲值是def,体力值vit 受到的伤害=对方〔atk*(100-def)/100〕-vit 从两方面减伤,同时强调了护甲值和加点体力的重要性

9、兰切斯特方程式

10、火炎纹章系列乱数表