AIMBOT 2.0
在新游戏2的第1集中,大约9:40,Nene编写了以下代码:
这是文本形式,带注释的翻译:
// the calculation of damage when attacked void DestructibleActor::ReceiveDamage(float sourceDamage) { // apply debuffs auto resolvedDamage = sourceDamage; for (const auto& debuf:m_debufs) { resolvedDamage = debuf.ApplyToDamage(resolvedDamage); m_currentHealth -= resolvedDamage if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); } } }
射击后,Umiko指向for循环,他说代码崩溃的原因是存在无限循环。
我不太了解C ++,所以不知道她在说什么。
从我可以看到,for循环只是迭代Actor当前具有的debuf。除非Actor具有无限数量的debuf,否则我认为它不可能成为无限循环。
但是我不确定,因为只有一小段代码是他们想在这里放一个复活节彩蛋,对吧?我们本来可以从笔记本电脑的背面拍摄一下,然后听到Umiko说:“哦,那儿有无限循环了。”他们实际上显示了一些代码,这一事实使我认为代码在某种程度上是某种复活节彩蛋。
代码实际上会创建一个无限循环吗?
8- 可能有帮助:Umiko的其他屏幕截图说:“ 调用相同的操作 一遍又一遍”,该代码可能不会显示。
- 哦!我不知道!我看过的@AkiTanaka子说“无限循环”
- @LoganM我不太同意。不仅仅是OP对某个源于动漫的源代码有疑问; OP的问题是关于特定声明 关于 源代码由动漫中的角色完成,并且有一个与动漫相关的答案,即“ Crunchyroll弄糟并误翻译了行”。
- @senshin我认为您正在阅读的是您想要的问题而不是实际询问的内容。该问题提供了一些源代码,并询问它是否像现实生活中的C ++代码一样生成无限循环。 新游戏! 是一部虚构的作品;不需要其中的代码来符合现实生活中的标准。 Umiko关于代码的说法比任何C ++标准或编译器都更具权威性。最上面的(可接受的)答案没有提及任何宇宙信息。我认为可以通过一个很好的答案对此问题进行提问,但是不是这样。
代码不是无限循环,而是错误。
有两个(可能是三个)问题:
- 如果没有气泡,则完全不会造成任何损坏
- 如果debuf超过1个,将造成过多的损害
- 如果DestroyMe()立即删除该对象,并且仍然有m_debufs要处理,则循环将在已删除的对象上执行并浪费内存。大多数游戏引擎都有一个销毁队列来解决此问题,甚至更多,因此这可能不是问题。
损害的施加应在环路之外。
这是更正后的函数:
// the calculation of damage when attacked void DestructibleActor::ReceiveDamage(float sourceDamage) { // apply debuffs auto resolvedDamage = sourceDamage; for (const auto& debuf:m_debufs) { resolvedDamage = debuf.ApplyToDamage(resolvedDamage); } m_currentHealth -= resolvedDamage if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); } }
12 - 15我们正在进行代码审查吗? :D
- 如果您没有超过16777216 HP,那么4个浮球对健康非常有用。您甚至可以将生命值设置为无限,以创建可以击中但不会死的敌人,并使用无限伤害进行一击攻击,但仍然无法杀死无限HP角色(INF-INF的结果为NaN),但是会杀死其他一切。因此,它非常有用。
- 1 @cat按照许多编码标准的约定
m_
前缀意味着它是一个成员变量。在这种情况下,成员变量DestructibleActor
. - 2 @HotelCalifornia我同意这是一个很小的机会
ApplyToDamage
不能按预期工作,但是在示例中,我会说ApplyToDamage
还 需要重新设计以要求通过原始文件sourceDamage
以便在这种情况下可以正确计算出气泡。绝对是个狂徒:此时,dmg信息应为包含原始dmg,当前dmg以及损坏性质的结构,如果debufs具有诸如“易燃性”之类的东西。根据经验,任何带有debuf的游戏设计都需要这些。 - 1 @StephaneHockenhull说得好!
该代码似乎并未创建无限循环。
循环无限循环的唯一方法是
debuf.ApplyToDamage(resolvedDamage);
或者
DestroyMe();
将新项目添加到 m_debufs
容器。
这似乎不太可能。如果是这种情况,由于在迭代时更改容器,程序可能会崩溃。
该程序很可能会由于调用而崩溃 DestroyMe();
大概会破坏当前正在运行循环的当前对象。
我们可以将其视为动画片,其中“坏人”看到一个分支使“好人”跌倒,但为时已晚,他发现自己错了。或是Midgaard Snake吃着自己的尾巴。
我还应该补充一点,无限循环的最常见症状是冻结程序或使其无响应。如果它重复分配内存,或者执行某些操作最终导致被零除等操作,它将使程序崩溃。
根据田中亚希(Aki Tanaka)的评论,
可能有帮助:Umiko的其他屏幕截图说:“它一次又一次地调用同一操作”,但可能不会在代码中显示。
“它一遍又一遍地调用相同的操作” 这更有可能。
假如说 DestroyMe();
不能被多次调用,更可能导致崩溃。
解决此问题的一种方法是更改 if
对于这样的事情:
if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; }
当DestructibleActor被销毁时,这将退出循环,请确保1) DestroyMe
方法仅被调用一次,并且2)一旦对象已被视为死亡,就不要无用地施加增益。
- 1当health <= 0时退出for循环绝对是比等到循环之后检查运行状况更好的解决方案。
- 我想我可能会
break
跳出循环 然后 称呼DestroyMe()
,只是为了安全起见
该代码有几个问题:
- 如果没有气泡,则不会造成任何损坏。
DestroyMe()
函数名称听起来很危险。取决于其实施方式,它可能会或可能不会成为问题。如果这只是对包装在函数中的当前对象的析构函数的调用,那么就会出现问题,因为该对象将在执行代码的中间被破坏。如果是对将当前对象的删除事件排队的函数的调用,那么就没有问题,因为对象在完成执行并触发事件循环后将被销毁。- 在动画中似乎提到的实际问题是“它一遍又一遍地调用相同的操作”,它将调用
DestroyMe()
只要m_currentHealth <= 0.f
而且还有更多的减益效果需要迭代DestroyMe()
一遍又一遍地被多次调用。循环应该在第一个之后停止DestroyMe()
调用,因为多次删除对象会导致内存损坏,从长远来看,很可能会导致崩溃。
我不太确定为什么每个减荷动作都会夺走生命值,而不是只将生命值减掉一次,而所有减益效果都会对初始伤害造成影响,但是我认为这是正确的游戏逻辑。
正确的代码是
// the calculation of damage when attacked void DestructibleActor::ReceiveDamage(float sourceDamage) { // apply debuffs auto resolvedDamage = sourceDamage; for (const auto& debuf:m_debufs) { resolvedDamage = debuf.ApplyToDamage(resolvedDamage); m_currentHealth -= resolvedDamage if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; } } }
3 - 我应该指出,由于我过去已经编写了内存分配器,因此删除相同的内存不一定是问题。这也可能是多余的。这完全取决于分配器的行为。我的行为就像一个低级链接列表,因此删除数据的“节点”要么被设置为空闲几次,要么被重新删除了几次(这仅与冗余指针重定向相对应)。虽然好捕获。
- Double-free是一个错误,通常会导致未定义的行为和崩溃。即使您有一个自定义的分配器,以某种方式不允许重复使用相同的内存地址,double-free也是一个令人讨厌的代码,因为它毫无意义,并且会被静态代码分析器大喊大叫。
- 当然!我没有为此设计它。由于缺乏功能,某些语言仅需要分配器。不不不。我只是说不能保证发生崩溃。某些设计分类并不总是会崩溃。