Anonim

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以及損壞的性質(如果debuf具有類似“易燃性”之類的東西)。根據經驗,任何帶有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)一旦對像已被視為死亡,就不要無用地施加增益。

2
  • 1當health <= 0時退出for循環絕對是比等到循環之後檢查運行狀況更好的解決方案。
  • 我想我可能會 break 跳出循環 然後 稱呼 DestroyMe(),只是為了安全起見

該代碼有幾個問題:

  1. 如果沒有氣泡,則不會造成任何損壞。
  2. DestroyMe() 函數名稱聽起來很危險。取決於其實施方式,它可能會或可能不會成為問題。如果這只是對包裝在函數中的當前對象的析構函數的調用,則會出現問題,因為該對象將在執行代碼的中間被破壞。如果是對將當前對象的刪除事件排隊的函數的調用,那麼就沒有問題,因為對像在完成執行並觸發事件循環後將被銷毀。
  3. 在動畫中似乎提到的實際問題是“它一遍又一遍地調用相同的操作”,它將調用 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也是一個令人討厭的代碼,因為它毫無意義,並且會被靜態代碼分析器大喊大叫。
  • 當然!我沒有為此設計它。由於缺乏功能,某些語言僅需要分配器。不不不。我只是說不能保證發生崩潰。某些設計分類並不總是會崩潰。