试玩,但要检查:引擎如何使设计师短路



设计多人游戏时,几乎最重要的组成部分是平衡。 游戏设计师在这方面的工作与情报分析师的工作类似:如果他工作得好,没有人注意到。 这是值得绊脚的,玩家可以毫不留情地利用错误。 但是,最有趣的事情发生在除了游戏设计师之外,程序员还被误认为...


在本文中,我们将考虑“ 哥萨克3”战略的一个要素。 该游戏包含17世纪和18世纪的各种火枪手和其他射手,以及探索减少火枪装弹时间的技术的机会。 根据游戏界面,总共有两项改进,每项改进都使射速提高30%。


但是,即使从视觉上看,很明显有些战斗部队在研究了改进措施后,不仅射击率高达60%,而且射击频率甚至提高了几倍。 当直接使用内置游戏计时器测量射速时,完全奇怪的数字出来了,与规定的百分比无关。


在“哥萨克人”的引擎盖下


幸运的是,该游戏的修改方式非常友好,因此我们需要的所有脚本都可以在数据/脚本/文件夹中以文本文件的形式获得。 根据语法判断,这些脚本是用Delphi或非常相似的语言编写的。 让我们看看计算镜头间隔的机制。


注意事项
  • 在游戏“ Cossacks 3” 2.1.4版上进行了分析。
  • 以下所有脚本节均包含简化的伪代码。

  1. 游戏开始时,将初始化所有战斗单元。 该程序指出每种类型的生命力,成本和武器的价值。 对于小型武器,将传递一个参数,该参数指示游戏帧中两次射击之间的间隔:


    //lib/unit.script procedure _unit_InitBase() 'musketeer' : maxhp := 70; SetObjBaseWeapon( x,x,x,x, 150, ... ); SetObjBasePrice( ... ); //lib/unit.script procedure SetObjBaseWeapon( x,x,x,x, pause, ... ) weapon.pause := _misc_FramesToTime( pause ); 

    从评论中可以看出,时间单位“游戏框架”是第一个“哥萨克人”的虚假行为,在创建第三部分时复制了游戏过程。 但是,这些帧会立即在游戏秒中以1:32的比例重新计数,我们不再遇到它们:


     //lib/misc.script function _misc_FramesToTime( val ) Result := ( val * gc_frames_to_time ); //dmscript.global gc_frames_to_time := 0.03125; gc_time_to_frames := 32; 

  2. 另外,在游戏开始时,将初始化游戏国家的数据,包括可用的改进。 对于每个变量,都会指示并存储value变量,当研究此改进时,它将影响游戏必要参数的重新计算:


     //lib/country.script procedure _country_Init() _country_AddUpgrade( x,x,x,x, type_attpauseperc, -30, ... ); procedure _country_AddUpgrade( x,x,x,x, upgrade_type, value, ... ); 

    在我们的情况下,这意味着每次改进后的军事单位间隔乘以0.7,然后将……四舍五入?!


     //lib/player.script procedure _player_ApplyUpgrade() type_attpauseperc : weapon.pause := Round( weapon.pause * (1 + value/100) ); 

    考虑到射手的时间间隔最初是3.125到5.0范围内的浮点数,因此舍弃重新计算结果的决定看起来很奇怪,即使不重要。


  3. 在每次射击后,指示下一次射击之前的延迟。 idividual.attackrate修饰符应用于塔式结构,在我们的情况下始终为1。


     //lib/unit.script procedure _unit_ApplyAttackPause() attackdelay := weapon.pause * idividual.attackrate; 


因此,除了计算中的数学误差外,还可以在下面的扰流器下读取其详细信息,并且对浮点数舍入不当。 我想知道乍看之下这种轻微的疏忽会对游戏的机制产生什么影响?


一点数学

射速与射击间隔的大小成反比。 如果对玩家来说重要的是每分钟的回合数,那么游戏引擎通常会使用间隔来计算暂停时间。 这里要注意的是,“将间隔减少30%”和“将射速增加30%”是完全不同的事情。 间隔t和射击次数n之间的比率r用一个简单的公式描述:

 fract1t2=r= fracn2n1


例如,如果我们采用6秒的间隔(每分钟10发)并将其减少30%,则每分钟将不会获得13发:

6 space mathrms cdot0.7=4.2 space mathrms; quad frac6 space mathrms4.2 space mathrms\约1.43 neq frac1310


要获得所需的值,应将当前间隔除以新的发射率与旧的发射率的期望比率:

t2= fract1r= frac6 space mathrms1.3 about4.62 space mathrms



测量方式

若要获取游戏引擎使用的值,可以使用日志记录功能。 为此,您首先需要启用日志记录:


  //cossacks.ini & editor.ini LogFileEnabled = true LogFileRoot = true 

然后在_unit_ApplyAttackPause()过程的末尾添加对Log()函数的调用:


  //data/scripts/lib/unit.script procedure _unit_ApplyAttackPause(const goHnd, weapind : Integer); begin //... if (attpause<>0) then Log(TObjProp(pobjprop).sid+' '+FloatToStr(attpause)); end; 

现在,您可以在地图编辑器中玩转各种箭头和进行改进(要启用攻击模式,请按Ctrl + W )。 该协议将被写入/日志文件夹中的文本文件。 每次射击后,将记录战斗单位的标识符及其当前间隔的值。


谁是谁


最初,游戏脚本会区分35种类型的射击者(不包括不受改进影响的雇佣兵)。 如果我们按照间隔的大小对它们进行分组,那么我们可以区分出十个类别。 我决定按照射速的相对增加对它们进行排序,以挑选出那些从改进中受益最大的射手。 因此,分析结果为:


攻击间隔射/分钟射速
类别分类 \反增强功能0+1+20+1+2+1+2
5.004.03.012.01520+ 25%+ 67%
II6.885,04.08.71215+ 38%+ 72%
三级5.314.03.011.31520+ 33%+ 77%
IV5.634.03.010.71520+ 41%+ 88%
V3.753.02.016.02030+ 25%+ 88%
5.944.03.010.11520+ 48%+ 98%
4.063.02.014.82030+ 35%+ 103%
4.383.02.013.72030+ 46%+ 119%
4.693.02.012.82030+ 56%+ 134%
X3.132.01,019,23060+ 56%+ 213%

在下图中,这些列从左到右对应于类别IX。 图表的最后一个虚线列对应于游戏界面中声明的增长率。 左边的一组列显示出改进后的射速增加,右边的列-两者均显示。


类别和单位清单

该游戏有多个国家/地区-17个欧洲国家和4个独特国家(乌克兰,土耳其,阿尔及利亚和苏格兰)。 欧洲派系从​​一开始就非常相似,并有17、18世纪的火枪手和龙骑兵以及掷弹兵。 但是有时某些国家的箭头与模板不同,或者被唯一的箭头完全取代。


类别分类作战单位
火枪手17世纪 (奥地利)
塞克(匈牙利)
苏格兰射手(英格兰)
波兰立陶宛联邦(波兰)
德拉贡18世纪 (荷兰和皮埃蒙特)
II亨斯迈(瑞士)
皇家火枪手(法国)
三级掷弹兵(除丹麦和普鲁士以外的欧洲)
德拉贡18世纪 (欧洲,法国,荷兰和皮埃蒙特除外)
轻骑兵(不同国家)
IV德拉贡17世纪 (欧洲)
V火枪手17世纪 (荷兰)
火枪手17世纪 (西班牙)
火枪手18世纪 (巴伐利亚和丹麦)
掷弹兵(丹麦)
义工(葡萄牙)
亨斯迈(法国)
Serdyuk(乌克兰)
火枪手18世纪 (萨克森)
掷弹兵(普鲁士)
火枪手17世纪 (欧洲,奥地利,波兰,荷兰和西班牙除外)
火枪手圣约(苏格兰)
射手座(俄罗斯)
贾尼萨(土耳其)
火枪手18世纪 (欧洲,丹麦,巴伐利亚和萨克森除外)
潘杜尔(奥地利)
德拉贡18世纪 (法国)
X火枪手17世纪 (波兰)
哈伊杜克(匈牙利)

注意事项:


  • 军事单位的名称从游戏的俄语界面复制而来。
  • 18世纪的斜体箭头用斜体表示
  • 马箭头以粗体突出显示

事实证明,17世纪的波兰火枪手和匈牙利劫机者从射击率的提高中受益最大:他们的射击次数比承诺的高60%,而不是承诺的+ 60%。 由于间隔的初始值较低,因此他们最终比其他所有射击者的射击速度快两倍,三倍甚至四倍。


在骑兵中,最适合定居于18世纪的法国龙骑兵:他们的射速超过一倍。 结果,它们每分钟的射击次数比其他欧洲国家的射击次数多50%。


自然地,这里没有考虑射击的伤害或每秒的伤害,但是即使没有这些数据,很明显军事单位的表现也不如预期。


如何修复

解决该问题最快,最无创的方法是重写公式以应用改进。 除了拒绝舍入外,不要将间隔乘以0.3,而应将其除以1.3。 为此,只需将gc_upg_type_attpauseperc改进过程替换为


  //lib/player.script Round(weapon.pause*(1+value/100)); 


  weapon.pause/(1+(-value)/100); 

由于改进是一致地应用的,最后,不是声明的+ 60%,而是+ 69%。 但这仍然优于213%。


后记


为了在这种情况下可靠地确定平衡中的错误计算,应该分析游戏机制的另外两个方面-射手的伤害和经济价值以及建立战斗部队所需的时间。 但是,常识告诉您先等待下一个更新...


我从视频“ 为什么AoE2的攻击率经常出错 ”中获得了研究的想法,该视频解决了《帝国时代II》策略中的一个类似问题。


UPD:错误已部分纠正


自从文章发布以来,甚至还没有一周,因为更新2.2.1中的开发人员已通过舍入修复了该错误。 同时,配方本身保持不变-每次升级的射速提高了43%。 由于计算是递增的,因此在检查了两种改进后,所有箭头的工作速度均提高了104%。


桌子

在研究了两种改进之后,每分钟射击的射击单位速率,按升序排列:


作战单位射门
亨斯迈(瑞士)
皇家火枪手(法国)
17.8
火枪手17世纪 (西班牙)
火枪手18世纪 (巴伐利亚和丹麦)
掷弹兵(丹麦)
义工(葡萄牙)
亨斯迈(法国)
20.6
德拉贡17世纪 (欧洲)21.8
掷弹兵(除丹麦和普鲁士以外的欧洲)
德拉贡18世纪 (欧洲,法国,荷兰和皮埃蒙特除外)
轻骑兵(不同国家)
23.0
火枪手17世纪 (奥地利)
塞克(匈牙利)
苏格兰射手(英格兰)
波兰-立陶宛联邦(波兰)
德拉贡18世纪 (荷兰和皮埃蒙特)
24.5
火枪手17世纪 (欧洲,奥地利,波兰,荷兰和西班牙除外)
火枪手圣约(苏格兰)
射手座(俄罗斯)
贾尼萨(土耳其)
火枪手18世纪 (欧洲,丹麦,巴伐利亚和萨克森除外)
潘杜尔(奥地利)
德拉贡18世纪 (法国)
26.1
火枪手18世纪 (萨克森)
掷弹兵(普鲁士)
28.0
Serdyuk (乌克兰)30.1
火枪手17世纪 (荷兰)32,7
火枪手17世纪 (波兰)
哈伊杜克(匈牙利)
39,2

Source: https://habr.com/ru/post/zh-CN414853/


All Articles