欢迎回到这个关于如何开发格斗游戏的教程系列!今天,我们将为这类游戏的初学者介绍另一个大障碍:输入缓存,以及为什么对于流畅的玩家体验是很必要的。
但是,首先,一个非常重要的免责声明:就像我的文章格斗游戏中的确定性,本文也是相当技术向的。这仍然是本系列的一部分因为这是过去许多开发人员问过我的问题,也是我个人在构建引擎时不得不为之斗争的问题。虽然我写这篇文章是因为我希望有更多的可用资源,这绝不是一个可以容易处理的话题。我对完全初学者的建议是只要浏览一下这篇文章和上一篇文章,就能对这个话题有个大概的了解然后在获得更多编程方面的经验后再回来。
(资料图)
我的下一篇文章将会是关于更多的“初学者级”主题(比如受击硬直和连段,格挡和招架,抓取…),所以,如果你认为你还没有准备好这个主题,你可以直接跳过到它们!
在大多数格斗游戏中,当角色在闲置状态时按下一个按钮,通常会导致立即执行一个动作。无论是轻拳、重拳还是回旋踢,按下按钮这个简单的动作可以1:1地转化为激活一个动作。如果我们把角色状态考虑在内这看起来很好,也不太难处理蹲伏和站立的标准动作:如果一个攻击按钮被按下,检查角色的状态并执行相应的动作。
如果我们觉得更有勇气,我们也可以开始使用所谓的“指令普通技”或方向输入。当然,按下重拳按钮是好的,但如果按下向前加重拳我得到了不同的攻击呢?这种情况也很容易处理:我们检查角色状态是否正确,我们检查是否按下了正确的方向,我们检查是否按下了攻击按钮,然后—嘣——指令中断普通技!
当你必须处理比上面更复杂的事情时这种方法完全失败了:假设你想识别一个火球招式摇杆——通常是一个四分之一圆下前,然后按下按钮。由于总共有四个不同的输入(下、下前、前、拳),您不能再只检查一帧重叠的按钮。你需要保存至少在最后四帧中按钮被按下的记录。此外,如果你的玩家输入了下,下前,前,下前,拳,会发生什么?还是会接受输入吗?如果在同一帧按前和拳,将字符串减少到三个输入,而不是您预期的四个输入,会怎么样?
为了处理这种情况以及更多的稀少的情况,您需要所谓的输入缓存。如果你今天才听说这个,系上你的安全带,准备参加这个主题的速成班吧!
在继续之前,我恳求你考虑一个普遍的、可靠的建议:不要把你的游戏动作映射到物理游戏手柄按钮上。即使你正在开发一个X-Box控制器,并且到2021年每台电脑都支持它们,不要陷入在代码中直接使用X、Y、A、B按钮的舒适陷阱。这将使得实现按钮重映射、接收新的控制器甚至使你的系统对其他外设(如键盘)更加灵活变得极其困难。
你需要在“物理”按钮和游戏接收的输入之间创建一个层。您的游戏不需要知道×键被按下:您的游戏需要知道轻拳按钮被按下。Y
创建一组代表您需要的功能的通用按钮,并然后处理哪个物理按钮意味着分别激活哪个功能。
作为C++代码的一个例子,您可以使用枚举类型它将每个“逻辑按钮”映射到一个值,以便于操作。
用流程图的术语来说,发生的情况如下:
· 您的输入层检测控制器上Y键的按下;
· 您的输入层搜索其内部查找表,以查看哪个操作被映射到Y键;它发现,根据当前的设置、选项和插入的控制器,Y按钮被映射到重拳逻辑输入;
· 您的游戏引擎现在得到通知重拳键被按下并将其添加到要处理的输入列表中。
从长远来看,没有输入映射层将会损害您的利益,降低灵活性并使事情更难改变。所以,请不要低估这一步的重要性并提前计划!
既然我们已经就如何处理原始输入达成了共识,那么让我们更深入地探讨一下如何访问和读取它们。一个免责声明:有多种方法可以达到同样的效果。我在这里展示的是一个可行的方法,它被证明是有效的,但是不要害怕做自己的研究,看看周围还有什么方法可以被使用!
如前所述,当您需要处理较长的输入时,您需要有适当的措施来“及时返回”并查看什么时候按下了什么。
这个概念的一个可能的实现方法是使用容器(list,vector,deque)在您选择的编程语言/引擎中。理想情况下,您应该为每个角色保留一个这样的容器。当每一帧,你更新游戏逻辑时,你读取每个设备的物理输入,把它映射到逻辑输入,并把它添加到正确的容器中。
我建议只存储之前没有按过的输入,而不是连续的,这样在读取时更容易。还有其他策略,但在本文的其余部分,我们将假设只要按钮被按住,就不会再被记录。
以这种方式存储输入应该不需要很多内存:如果你正在为一个现代的主机或PC开发,在你的设备的内存中保存完整的输入历史是完全可行的。然而,在性能非常严格的地方,有一些解决方案,如环形缓冲器http://wiki.c2.com/?CircularBuffer,这有助于减轻硬件负担。
现在我们已经存储了所有的输入,是时候按顺序读取它们,让你的角色执行正确的动作了!为此,我们仍然需要一些额外的技巧:
· 给每个动作一个单独的优先级;
· 给每个动作一个一组可选输入/快捷键;
· 给每个动作一个特定输入时间窗口;
· 给每个动作一个一组条件这使得它可用,减少了在任何给定时间要检查的条目数量。
优先级相对简单:在优先级较低的移动之前,将根据缓冲区检查优先级较高的移动。这确保了输入例如序列[前、下、下前、前、拳]导致升龙拳而不是波动拳。
替代输入(翻译注:可理解为模糊输入)是一种简化输入检测并接受“不干净”或不完整输入的方法,以获得更好的用户体验。例子可能包括在按钮被按下之前允许一个额外的方向,或者检查方向和按钮是否同时被按下,并且仍然接受输入为有效。比如街霸系列,所谓的360输入(以顺时针或逆时针方式方向按下)都可被接受,即使半圆输入随后输入向上对角线方向也可以。这个游戏常见问题帖子https://gamefaqs.gamespot.com/boards/975212-super-street-fighter-iv/55389548包含了超级街霸4接受的一个有趣的快捷键列表,并且可能有助于了解如何读取特定运动的缓冲区。
第三点可以描述为当搜索输入时,你的游戏会看多远的过去。这可以根据帧来设置,并且有助于缓冲区的整体宽松度。建议对一般移动也给予一些宽松度,这允许玩家在他们的角色动作之前稍微输入它们(例如,当防御时),并让移动在第一个可能的发动帧前出现。
最后,关于“检查条件”,我指的是像“超级必杀应该只在足够的超杀能量可触发”或“这个移动只能在蹲下时执行”。在遍历完整的移动列表之前执行一个检查,你将会发现只有少数“合法的招式”会真正遍历你的输入缓冲区。
为了更好地解释这是如何工作的,假设我们有以下招式:
· 一个超杀需要两格超杀能量并需要输入[下,前,下,前,脚],优先级为5而输入窗口为15帧;
· 一个必杀技不需要能量并需要输入[下,下前,前,脚],优先级为3而输入窗口为5帧;
· 一个普通技输入只是简单一个[脚],优先级为1而输入窗口为3帧;
我们将看到这三者在各种情况下是如何相互作用的。
情况1:玩家的输入是[下、前、下、前、脚],具有满的超杀能量,并在第8帧中执行
这里有两个可行的选择:超级必杀和普通技,作为我们的必杀技也需要一个缺失的下前输入。在这两者中,超级必杀由于具有更高的优先级而被首先检查。所有条件都满足,所以这是由缓冲区读取器返回的招式。
情况2: 玩家的输入是[下,前,下,前,脚],具有满的超杀能量,并在第17帧执行
满足能量条件,但输入太慢。因此,解析器将跳过超级必杀。必杀技与缺少的下前方向的输入不匹配。现在唯一可行的动作是标准的正常踢腿,然后被选中
情况3: 玩家的输入是[下,前,下,下前,前,脚],具有满的超杀能量,并在第10帧执行
在这种特定情况下,所有三个招式都是可行的,并且必杀技与输入的第二部分有完美的重叠。但是,由于我们首先检查超级必杀,由于它的优先级更高,并且超级必杀的所有必需输入都以正确的顺序存储在列表中,所以超级必杀是被选中的招式。请注意,如果不允许有效输入之间的异常值,将会选择必杀技。这是一个有效的设计选择,尽管从玩家的角度来看有点令人沮丧,但你绝对可以考虑它。
情况4: 玩家的输入是[下,前,下,下前,前,脚],没有超杀能量,并在第10帧执行
在这种情况下,将选择必杀技,因为超必杀由于缺少激活条件而不在选项中。普通技也与输入匹配,但优先级低于必杀技。
情况5:玩家的输入是[下,前,下,前,脚],没有超杀能量,并在第10帧执行
在这种情况下,将选择普通技,如情况2,因为超级必杀的激活条件之一没有满足,并且我们缺少有效必杀技的输入。
我注意到大多数卡普空游戏在整个招式过程中不使用扁平化输入缓冲窗口,而是每次按下按钮后有一个更短的缓冲窗口。让我们考虑一个火球招式[下,下前,向,拳]:当玩家按下[下],引擎等待固定数量的帧到[下前],接下来引擎等待对随后的[前]的固定数量的帧有效,这又对同时引擎等待(猜猜是什么?)的[拳]固定帧数有效。这允许在如何处理输入方面有更好的微调、精度和间隔,但肯定更难实现。有关社区如何在M.U.G.E.N中实现这一点的更多详细信息,请访问这里https://mugen-net.work/wiki/index.php?title=Deep_Buffering(特别感谢Kamekaze教会我这种可能性)。
虽然系统的基架如前几段所述是完全可用的,但仍有一些特定的输入类型是常见的,如果我们想构建一个街头霸王的复刻,就需要考虑这些输入类型。
蓄力输入是一个突出的例子:要执行带有蓄力输入的动作,你需要在释放按钮并执行其余动作之前,保持按住按钮(通常是向下或向后)一段特定的时间。常见的蓄力操作有【下蓄力,上,攻击】和【后蓄力,前,攻击】,但是也还有太多其他风格的数不过来。蓄力操作通常最适合防守型角色,或者是你不想让玩家随便使用的非常好用的必杀技。
我们如何处理输入缓冲区中的“蓄力”?一个可能的解决方案是用一个单独的容器来存储每个按钮被按下的帧数:每一帧,你为每个被按下的按钮增加1,如果按钮被释放,你就把它归零。当这种情况发生在连续保持按压N帧之后时,您可以将[N帧后释放按钮]事件添加到输入缓存,就好像它只是另一个逻辑输入,并使用与前面讨论的相同的检测模式。将蓄力事件视为“逻辑按钮”的优势在于,不必仅为它们更改招式检测逻辑。
巴洛克的转身拳[啪]是“按下,松开”输入的一个臭名昭著的例子。
同样的方法也可以应用于巴洛克的转身拳和科迪的转身猛击:这两个特殊技都需要玩家至少在特定的帧量内保持按下一些按钮,然后按钮发动攻击。通过跟踪按钮被保持按下的时间量,可以将相同类型的[N帧后释放按钮]事件添加到队列中,并根据它匹配招式。
顺便说一下,将“松开按键”事件添加到输入缓冲区也可以用于实现所谓的“Negative Edge離し入力 ”:一些格斗游戏允许在松开按钮时像按下按钮一样触发必杀技。这些“释放”输入事件对于普通技被忽略,但是对于必杀技和超级必杀都被接受为有效,从而提高执行这些招式的宽松度。
我们必须处理的最后一件事是如何处理同时按下按钮。许多游戏使用同时输入来触发必杀技、超必杀或特殊角色状态(例如,一次按两个按钮,所有脚键……)。同时按下, 不过,这可能会很难,而且自相矛盾,特别是因为,如果游戏逻辑以60 fps的速度更新,这意味着两个按钮需要在16.6毫秒的时间内按下。虽然这可以被视为“git gud scrub”(翻译注:在线上电子游戏中,用来质问没有经验的玩家或新手的一种表达方式)的一种情况,但我支持简化玩家的操作,至少在执行基本游戏操作时。
一种可行处理方式是接受按钮稍微“异步”的交替输入(例如,接受[出拳+踢腿],[出拳,踢腿在-1帧后]和[踢腿,出拳-1在帧后]作为激活招式的等效输入),并使其在最初几帧期间,所有可能错误出现的移动可以被取消为“正确的”同时按下式招式。
这也是所谓的“挥空取消“实现方法。
为了更好地解释这是如何工作的,假设我们有以下招式:
· 一个超必杀通过输入[下,下前,前,轻拳+重拳]执行,有最高优先级并有1帧宽容度的同时按下的输入窗口。
· 一个必杀技通过时任[下,下前,前,轻拳]执行。
· 优先级最低的轻拳和重拳的普通技。
我们将看到这三者在各种情况下是如何相互作用的。
情况 1: 玩家的输入是[下,下前,前,轻拳+重拳]
自然是超必杀会发出来。
情况 2: 玩家的输入是[下,下前,前,轻拳,1f之后重拳]
对于第一帧,角色执行必杀技,然后在第二帧一旦[重拳]输入被记录。取消为正确的超级版。
情况 3: 玩家的输入是[下,下前,前,重拳拳,1f之后轻拳]
对于第一帧,角色执行普通技[重拳],然后在第二帧一旦[轻拳拳]输入被记录。取消为正确的超级版。
情况 4: 玩家的输入是[下,下前,前,重拳拳,2f之后轻拳]
由于[轻拳]输入是在[重拳]输入之后2帧记录的,在宽容度窗口之外,轻拳招式被执行,没有取消发生。
挥空取消仍然存在并活跃,在某些情况下甚至是有意的。
作为这篇文章的最后一点,我想简短地谈谈如何处理烈火招式(翻译注:指类似街霸中飞龙的烈火拳那样的分段式招式)和取消招式。在上一节中,我们讨论了每一步应该如何给予招式优先级、宽容度处理和一系列条件扳机。我们还可以做些什么来让事情变得更简单允许每个招式都有一个可以取消其他招式的列表。这意味着,如果玩家角色已经在招式中,当我们输入新的命令时,只有此列表中的移动将被优先化,并根据输入进行检查。
如果您需要输入缓冲——用来实现任何类型的运动输入——您需要一个容器来存储每一帧的输入。合理的优先级、时间窗口和激活条件的平衡允许非常强大的灵活的输入读取系统。“释放”事件的使用使得实现蓄力招式相对简单。总的来说,有很多需要注意的地方,但是一旦你明确了基本的概念,你的游戏就会感觉反应灵敏使你所有的有所努力回报
本教程到此为止——下次见!
[1] 游戏:超级龙珠z (PS2), 截图来自 sleepmode
[2]来自 Zinac关于我们正在考虑的相同主题的堆栈交换答案: https://gamedev.stackexchange.com/a/68134/106008
[3] Infil的格斗游戏术语表: https://glossary.infil.net/
[4]m . u . g . e . n .如何处理输入: https://mugen-net.work/wiki/index.php?title=Command (特别感谢Kamekaze提供该资源链接)
[5] M.U.G.E.N.的(坏掉的)解释器解释程序是如何被替换的: https://mugen-net.work/wiki/index.php?title=Deep_Buffering
[6] 超级街霸4中的输入快捷键:https://gamefaqs.gamespot.com/boards/975212-super-street-fighter-iv/55389548 (特别感谢 Joeldaho提供该资源链接)
标签:
输入缓存——如何读取并使一连串按键列表合理欢迎回到这个关于如何开发格斗游戏的教程系列!今天,我们...
5月28日,“东方创世神话《山海经》主题皮影艺术展”在海淀上庄镇举办。《山海经》皮影艺术研学成为京城...
2023年05月31日06时40分卢布 人民币汇率最新报价
通过努力,确保在8月31日前,脱贫家庭、低保家庭高校毕业生去向落实率高于全省平均水平;有就业意愿和就业
北京时间5月31日,曼联官方宣布马夏尔因伤将缺席足总杯决赛!对于红魔来说,这是重大打击。马夏尔原本是曼