TDD和游戏物理
我正在玩一个小型的游戏项目,因为我在TDD方面不是很有经验,所以我很乐意就几件事情得到一些专家意见。
首先,我很早就意识到TDD对于游戏开发并不理想。 看来这个主题的观点差异很大。 我最初没有受过教育的意见是TDD好像对所有的游戏逻辑都能很好地工作。 我认为,任何能够处理视频输出和声音的东西都会被抽象成可视化测试的类。
事情开始顺利。 目标是创建一个2D天体飞行游戏(对于那些关心的小行星)。 我为Ship类创建了一系列单元测试。 诸如初始化,旋转之类的东西可以很容易地在一系列测试中使用,比如:GetRotation(),TurnRotateRightOn(),Update(1),GetRotation(),Expect_NE(rotation1,rotation2)。 然后我遇到了第一个问题。
我对TDD的理解是你应该写测试你认为你应该如何使用这个类。 我希望这艘船能够移动,所以我写了一个基本上讲过的课。 GetCoordinates(),ThrustOn(),Update(1),GetCoordinates()。 确保船舶移动到某个地方很好。 但是,我很快意识到我必须确保船舶以正确的速度向正确的方向移动。 接下来是75线单元测试,我基本上必须初始化旋转,检查坐标,初始化推力,更新船舶,获取新坐标,检查新的旋转。 更重要的是,我可以看到没有必要在游戏中获得船的速度(只是坐标,船应该更新自己)。 正因为如此,我没有直接的方法来获得速度。 所以测试基本上必须重新计算速度应该是什么,所以我可以确保它与更新后得到的坐标相匹配。 总而言之这是非常混乱,非常耗时,但工作。 测试失败,通过测试通过等。
这很好,直到后来我意识到我想将船的更新代码重构为抽象的“Actor”类。 我意识到,尽管Actor的每个子类都需要能够正确计算新的位置,但并非每个子类都必须以相同的方式更新其速度(有些碰撞,有些不会,有些具有静态速度)。 现在,我基本上面临着复制和改变巨大而庞大的测试代码的前景,我不禁想到应该有更好的方法。
有没有人有处理单元测试这种复杂的黑匣子类型的工作经验? 看起来我基本上不得不在测试中编写完全相同的物理代码,所以我知道结果应该是什么。 这似乎是自我挫败,我确信我错过了这一切的一切。 我非常感谢任何人可以提供的帮助或建议。
我建议你首先创建一个计算位置和方向的组件,给出一系列控制输入。 那个部分就是为了测试而构成的“单位”。 该组件的测试用例可以行使您可以想到的所有场景:零加速度,恒定的非零加速度,脉冲加速度命令等。如果应用程序不需要速度,那么该组件不会公开任何功能与速度有关。
在产生预期产出以包含在测试中时,对于那些预期结果是否正确有很高的自信是很重要的。 出于这个原因,需要最小化生成预期结果所需的代码量。 尤其是,如果您发现自己编写的测试脚手架几乎与测试中的组件一样复杂,那么出现测试本身的bug的前景就成为一个严重问题。
在这种情况下,我会直接根据运动方程生成测试数据。 我使用Mathematica来达到这个目的,因为我可以直接输入方程,解决它们,然后生成结果的图表和表格。 这些图表让我可以看到结果,从而确信它们是可信的。 Excel / OpenOffice / Google Apps可以用于相同的目的,也可以用于像Sage这样的Mathematica的开源替代品。 无论选择什么,关键的问题是能够解决运动方程而不必编写非平凡的代码。
一旦我们有了一组好的测试用例以及预期的结果,我们就可以对单元测试进行编码。 请注意,测试代码非常简单,本身不执行任何计算。 它只是比较组件的输出和我们之前获得的硬编码结果。 通过测试用例,我们可以编写组件本身,添加代码直到测试全部通过。 当然,在严格的TDD中,这些操作正是按照这个顺序发生的。 我承认,我不亲自坚持瀑布,并倾向于在创建测试数据,编写测试和编写组件代码之间来回反弹。
Mathematica或Excel文档本身具有超出测试初始创建的有用寿命。 当添加新功能或者(天堂禁止)稍后会发现错误时,可以再次使用它们。 我会主张处理源代码等文档。
在本练习结束时,结果是一个“防爆炸”组件,我们相信自己会在任何给定的控制输入集合下计算对象的正确位置和方向。 从这个基础上,我们可以构建更多使用该功能的组件,如船只,小行星,碟子和镜头。 为了避免每个组件的测试用例出现组合式的爆炸,我将采用严格的黑盒测试方法。 因此,例如,如果我们正在测试“ship”组件,我们会编写测试,知道它使用位置/方向组件。 使用这种白盒知识,我们可以避免重新测试与运动有关的所有角落案例。 船舶单元测试可以执行“烟雾测试”来验证船舶实际上是否对控制输入作出响应,但主要关注的是测试船舶部件本身的任何独特功能。
所以,总结一下:
你可能会贬低你的测试。 与其检查正确的矢量和加速度,您可以简单地检查被测物体是否完全移动。 在大多数游戏中,无论如何你都会在物理模型中引入一些小的随机性来保持有趣。
我认为如果你采取更多的“偶发”方法,这可能会有所帮助。 不是跟踪你似乎正在做的连续体的坐标和旋转,你可以简单地指定你的太空飞船应该在哪里,以及在30个游戏步骤之后旋转什么。 如果这证明是正确的,那么你的船可能在中间做了所有正确的事情。 您将以这种方式编写更少的测试代码。
相同的想法通过针对碰撞的“情节”起作用。 如果一个台球打算从两面墙壁弹开并击中另一个球,那么在最后一次碰撞发生时可以提起一个事件,并检查碰撞的“入射角”。 再一次,你没有检查其间的所有步骤。 如果入射角度是正确的,那么在击中最后的球之前,球可能正确地从两个墙壁反弹。
当然,你必须为没有发生碰撞的情况做好准备。 您的测试可以计算每单位时间的游戏点击次数以达到最终碰撞。 您将进行必要次数的游戏点击以实现碰撞。 如果碰撞没有在规定的点击次数内发生,则测试可以正确地失败。
所有这些都是通过点击游戏而不是实时完成的,因此测试可以立即发生,而不是等待预期结果(如果您真的在玩游戏,通常会这样做)。
链接地址: http://www.djcxy.com/p/95559.html上一篇: TDD And Game Physics