游戏开发中,程序如何计算巨大的伤害数字,如超过long int ...
现在游戏中的数字往往非常大,几亿,几兆屡见不鲜。而游戏的伤害压缩往往仅在显示层面,如一万亿的数字显示为一亿,那么底层仍然要计算如此庞大的数。据我所知,一些游戏已经出现过数字溢出的问题,如DNF曾经在90版本出现过附加伤害溢出问题(21e bug):当附加伤害伤害超过约21e时,总伤害反而大幅减少。推测原因是附加伤害使用int32类型存储,数据范围是-2,147,483,648 ~ +2,147,483,647。
据我所知,大多数编程语言提供的标准数据类型,最大整型是int64与unsigned int64,uint64最大表示整数是3.402823669209385e+38。而linux环境gcc提供了int128等,c#,rust等也提供了int128。而int256,或是BigInteger的实现往往依赖于高精度算法,处理速度较慢。
我想知道对于那些使用天文数字的游戏,数据类型是如何选择的。 有一类英文名为idle的数值膨胀游戏,名叫“懒人游戏”/“放置游戏”
那里面爆double(大于等于1.7976931348623159e308)只是开始
在那里,数字会以另外的形式进行存储
比如一般情况下那里会直接存储lg 数字,而不是数字本身。
当你有两个小数字,比如13和5,你实际上只储存了lg(13)~1.1139433523068367204444939488894306123256683349609375
和
lg(5)~0.69897000433601885749368420874816365540027618408203125
当你试图计算它们的加减法时,你应当将它们恢复成普通数字,然后相加减
恢复时候,可以将两个数字分别减去它们的最大者的整数部分,比如这里可以使用
lg(13)-1~0.1139433523068367204444939488894306123256683349609375
和
lg(5)-1~-0.30102999566398114250631579125183634459972381591796875
进行恢复,恢复得到的两个数字大致为1.2999999999999998和0.5000000000000001,相加得
1.79999999999999982236431605997495353221893310546875
之后再将这个数字求lg,结果加上之前减掉的整数部分,就得到了最终结果,1.255272505103306013296560195158235728740692138671875
这个结果大概对应17.999999999999996
fn add(a:f64,b:f64)->f64{
let c=a.fract();
let d=b-a.trunc();
let e=10f64.powf(c)+10f64.powf(d);
let f=e.log10()+a.trunc();
println!("{:102.99}\n{:102.99}\n{:102.99}\n{:102.99}\n{:102.99}\n{:102.99}\n\nadd1={},add2={},ans={}",a,b,c,d,e,f,10f64.powf(a),10f64.powf(b),10f64.powf(f));
f // ans
}
fn main(){
add(13f64.log10(),5f64.log10());
}
使用这样的算法可以表示类似3.2e9999999这样的天文数字
类似的数字,真的是,显示问题比程序问题更离谱。
<hr/>当然,直接存储lg(x)并不能带来除了便于理解之外的其他好处
直接上ln,exp,expm1,log1p之类的神奇函数也是一个可行的选择。 大整数处理要考虑数值的存储和运算,常用的运算可能会包括:
[*]大整数加法
[*]大整数减法
[*]大整数乘法
[*]大整数除法
[*]大整数取模
[*]大整数的幂
[*]大整数的平方根
[*]比较大整数的大小
[*]求大整数的位数
[*]将整数转换为大整数
这里有一个大整数的C++示例代码,应该能满足你对大整数的各种需求。
https://www.geeksforgeeks.org/bigint-big-integers-in-c-with-example/更多关于游戏开发的技术上那些事,还可以关注下面这个开源Github仓库,可以为你(们)开发游戏节省一些时间。
https://github.com/gonglei007/GameDevMind
64位的伤害还能不够用…?
38位的十进制数打在屏幕上都费劲,我没见过哪个游戏用科学计数法来表示伤害的
一个很适合的例子就是Roguelike游戏《noita》,在33orbs的情况下最终boss的血量略高于1e12,你配置的武器需要足够离谱打出1e8到1e9的DPS才能以相对合理的时间把boss打败;再说noita如何处理金钱数值的爆炸,超过int的表示范围就直接以无限表示了。
然而初期noita的武器伤害都以几十为正常,每个怪物掉落的金钱也就从一到十几不等
如此程度的数值膨胀,到最后伤害一秒跳九位数已经够离谱了,你跟我说38位十进制伤害不够用,那我觉得更应该怀疑游戏的数值设计是不是出问题了……
<hr/>感谢评论区的指正,题目描述和我的答案都犯了一个基本的错误。64位整型的表示范围仅1.8e19,题目描述中的3.402823669209385e+38指的是128位整型的范围。
理论上64位依旧已经十分够用了。 首先,游戏里几兆的数字并不常见。
而这类数字里,仍然需要保持高精度的,更加不常见。
所以基本double就可以覆盖了。
真要极端情况下,是有可能使用大数库的,但是,大数库的性能并不差。无需考虑性能。
纵然游戏种类上万,但是形如exponential idle这样以指数为卖点的依然不多。
图中最大的数在左上角,用ee表示,ee(x)=10^(10^x)。
<hr/>顺带一提,无论游戏数值看起来有多膨胀,如果它还能玩,其背后的数值机制必然还是合理的。我有写过相关的文章,不过对于一般读者来言,太重了,这里不列出。
简单来说,哪怕是指数膨胀的游戏,在双指数ee的世界里还是收敛的。只是人类不太好理解而已。 有没有可能你看到的伤害数值有一半都是一次性的随机数?
页:
[1]