Anonim

AIMBOT 2.0

New Game 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ループは、アクターが現在持っているデバフを繰り返し処理しているだけです。アクターに無限のデバフがない限り、無限ループになる可能性はないと思います。

しかし、コードのショットがある唯一の理由は、彼らがここにイースターエッグを置きたかったからですよね?ラップトップの背面のショットを撮ったところ、海子が「そこに無限ループがある」と言っているのが聞こえたでしょう。彼らが実際にいくつかのコードを示したという事実は、どういうわけかそのコードはある種のイースターエッグであると私に思わせます。

コードは実際に無限ループを作成しますか?

8
  • おそらく役に立ちました:Umikoの追加のスクリーンショットは「 同じ操作を呼び出す 何度も何度も」、これはコードに表示されない場合があります。
  • ああ!知らなかった! @AkiTanaka私が見た潜水艦は「無限ループ」と言っています
  • @LoganM私は本当に同意しません。 OPがたまたまアニメから来たソースコードについて質問しているだけではありません。 OPの質問は、行われた特定のステートメントについてです アニメのキャラクターによるソースコードであり、アニメ関連の答え、すなわち「Crunchyrollは間抜けで行を誤訳した」があります。
  • @senshin実際に質問されていることではなく、質問の内容を読んでいると思います。質問はいくつかのソースコードを提供し、それが実際のC ++コードとして無限ループを生成するかどうかを尋ねます。 新しいゲーム! 架空の作品です。実際の標準に準拠するためにコードが提示されている必要はありません。 Umikoがコードについて言っていることは、どのC ++標準やコンパイラよりも信頼できます。一番上の(受け入れられた)答えには、宇宙内の情報についての言及はありません。これについては、良い答えで話題の質問をすることができると思いますが、言い換えれば、そうではありません。

コードは無限ループではありませんが、バグです。

2つ(おそらく3つ)の問題があります:

  • デバフが存在しない場合、ダメージはまったく適用されません。
  • デバフが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キャラクターを殺さない無限のダメージを使用して1キル攻撃を行うこともできます(INF-INFの結果はNaNです)が他のすべてを殺します。とても便利です。
  • 1 @cat慣例により、多くのコーディング標準では m_ プレフィックスは、それがメンバー変数であることを意味します。この場合、のメンバー変数 DestructibleActor.
  • 2 @ HotelCalifornia私は小さなチャンスがあることに同意します ApplyToDamage 期待どおりに機能しませんが、例の場合、私は言うでしょう ApplyToDamage また オリジナルを渡す必要があるように作り直す必要があります sourceDamage また、そのような場合にデバフを適切に計算できるようにします。絶対的な衒学者になるために:この時点で、dmg情報は、元のdmg、現在のdmg、およびデバフに「発砲に対する脆弱性」などがある場合の損傷の性質を含む構造体である必要があります。経験から、デバフを使ったゲームデザインがこれらを要求するのはそう長くはありません。
  • 1 @StephaneHockenhullよく言われました!

コードは無限ループを作成していないようです。

ループが無限になる唯一の方法は、

debuf.ApplyToDamage(resolvedDamage); 

または

DestroyMe(); 

に新しいアイテムを追加することでした m_debufs コンテナ。

これはありそうもないようです。その場合、反復中にコンテナを変更したためにプログラムがクラッシュする可能性があります。

の呼び出しが原因で、プログラムがクラッシュする可能性があります DestroyMe(); これはおそらく、現在ループを実行している現在のオブジェクトを破壊します。

それは、「悪者」が枝を見て「善人」を倒す漫画と考えることができますが、彼がカットの反対側にいることに気付くのは遅すぎます。または、ミズガルズのヘビが自分の尻尾を食べています。


また、無限ループの最も一般的な症状は、プログラムがフリーズしたり、応答しなくなったりすることです。メモリを繰り返し割り当てたり、ゼロ除算などを行ったりすると、プログラムがクラッシュします。


田中晃のコメントによると、

おそらく役立つ:「同じ操作を何度も呼び出していた」というUmikoの追加のスクリーンショット。これは、コードに表示されていない可能性があります。

「同じ操作を何度も呼び出していた」 これはより可能性が高いです。

仮定して DestroyMe(); 複数回呼び出されるように設計されていないため、クラッシュが発生する可能性が高くなります。

この問題を修正する方法は、 if このようなもののために:

 if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; } 

これにより、DestructibleActorが破棄されたときにループが終了し、1) DestroyMe メソッドは一度だけ呼び出され、2)オブジェクトがすでに死んでいると見なされたら、無駄にバフを適用しないでください。

2
  • 1ヘルス<= 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
  • 過去にメモリアロケータを作成したことがあるので、同じメモリを削除することは問題である必要はないことを指摘しておく必要があります。また、冗長になる可能性もあります。それはすべて、アロケータの動作に依存します。鉱山は低レベルのリンクリストのように機能したため、削除されたデータの「ノード」は数回空きとして設定されるか、数回再削除されます(これは冗長なポインタリダイレクトに対応します)。でもいいキャッチ。
  • ダブルフリーはバグであり、通常、未定義の動作やクラッシュにつながります。どういうわけか同じメモリアドレスの再利用を禁止するカスタムアロケータがある場合でも、ダブルフリーは意味がなく、静的コードアナライザに怒鳴られるので臭いコードです。
  • もちろん!私はその目的のためにそれを設計しませんでした。一部の言語では、機能が不足しているため、アロケーターが必要です。ダメダメダメ。クラッシュが保証されていないことを単に述べていました。特定のデザイン分類が常にクラッシュするとは限りません。