
设计多人游戏时,几乎最重要的组成部分是平衡。 游戏设计师在这方面的工作与情报分析师的工作类似:如果他工作得好,没有人注意到。 这是值得绊脚的,玩家可以毫不留情地利用错误。 但是,最有趣的事情发生在除了游戏设计师之外,程序员还被误认为...
在本文中,我们将考虑“ 哥萨克3”战略的一个要素。 该游戏包含17世纪和18世纪的各种火枪手和其他射手,以及探索减少火枪装弹时间的技术的机会。 根据游戏界面,总共有两项改进,每项改进都使射速提高30%。
但是,即使从视觉上看,很明显有些战斗部队在研究了改进措施后,不仅射击率高达60%,而且射击频率甚至提高了几倍。 当直接使用内置游戏计时器测量射速时,完全奇怪的数字出来了,与规定的百分比无关。
在“哥萨克人”的引擎盖下
幸运的是,该游戏的修改方式非常友好,因此我们需要的所有脚本都可以在数据/脚本/文件夹中以文本文件的形式获得。 根据语法判断,这些脚本是用Delphi或非常相似的语言编写的。 让我们看看计算镜头间隔的机制。
注意事项- 在游戏“ Cossacks 3” 2.1.4版上进行了分析。
- 以下所有脚本节均包含简化的伪代码。
游戏开始时,将初始化所有战斗单元。 该程序指出每种类型的生命力,成本和武器的价值。 对于小型武器,将传递一个参数,该参数指示游戏帧中两次射击之间的间隔:
//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;
另外,在游戏开始时,将初始化游戏国家的数据,包括可用的改进。 对于每个变量,都会指示并存储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范围内的浮点数,因此舍弃重新计算结果的决定看起来很奇怪,即使不重要。
在每次射击后,指示下一次射击之前的延迟。 idividual.attackrate修饰符应用于塔式结构,在我们的情况下始终为1。
因此,除了计算中的数学误差外,还可以在下面的扰流器下读取其详细信息,并且对浮点数舍入不当。 我想知道乍看之下这种轻微的疏忽会对游戏的机制产生什么影响?
一点数学射速与射击间隔的大小成反比。 如果对玩家来说重要的是每分钟的回合数,那么游戏引擎通常会使用间隔来计算暂停时间。 这里要注意的是,“将间隔减少30%”和“将射速增加30%”是完全不同的事情。 间隔t和射击次数n之间的比率r用一个简单的公式描述:
例如,如果我们采用6秒的间隔(每分钟10发)并将其减少30%,则每分钟将不会获得13发:
要获得所需的值,应将当前间隔除以新的发射率与旧的发射率的期望比率:
测量方式若要获取游戏引擎使用的值,可以使用日志记录功能。 为此,您首先需要启用日志记录:
//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 | +2 | 0 | +1 | +2 | +1 | +2 |
我 | 5.00 | 4.0 | 3.0 | 12.0 | 15 | 20 | + 25% | + 67% |
II | 6.88 | 5,0 | 4.0 | 8.7 | 12 | 15 | + 38% | + 72% |
三级 | 5.31 | 4.0 | 3.0 | 11.3 | 15 | 20 | + 33% | + 77% |
IV | 5.63 | 4.0 | 3.0 | 10.7 | 15 | 20 | + 41% | + 88% |
V | 3.75 | 3.0 | 2.0 | 16.0 | 20 | 30 | + 25% | + 88% |
六 | 5.94 | 4.0 | 3.0 | 10.1 | 15 | 20 | + 48% | + 98% |
七 | 4.06 | 3.0 | 2.0 | 14.8 | 20 | 30 | + 35% | + 103% |
八 | 4.38 | 3.0 | 2.0 | 13.7 | 20 | 30 | + 46% | + 119% |
九 | 4.69 | 3.0 | 2.0 | 12.8 | 20 | 30 | + 56% | + 134% |
X | 3.13 | 2.0 | 1,0 | 19,2 | 30 | 60 | + 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 |