Anonim

AIMBOT 2.0

New Game 2'nin 1. bölümünde, saat 9:40 civarında, Nene'nin yazdığı kodun bir görüntüsü var:

İşte, çevrilmiş yorumlarla birlikte metin biçiminde:

// 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(); } } } 

Atıştan sonra Umiko, for döngüsünü işaret ederek, kodun çökmesinin sebebinin sonsuz bir döngü olduğunu söyledi.

C ++ 'ı gerçekten bilmiyorum, bu yüzden söylediklerinin doğru olup olmadığından emin değilim.

Gördüğüm kadarıyla, for döngüsü sadece Aktörün şu anda sahip olduğu debuf'ları yineliyor. Aktörde sonsuz miktarda debuf olmadığı sürece, bunun sonsuz bir döngü haline gelebileceğini sanmıyorum.

Ama emin değilim çünkü kodun bir atışının olmasının tek nedeni, buraya bir paskalya yumurtası koymak istemeleri, değil mi? Sadece dizüstü bilgisayarın arkasının bir görüntüsünü alırdık ve Umiko'nun "Orada sonsuz bir döngünün var" dediğini duyardık. Aslında bazı kodlar göstermiş olmaları, bana bir şekilde kodun bir çeşit paskalya yumurtası olduğunu düşündürüyor.

Kod gerçekten sonsuz bir döngü oluşturacak mı?

8
  • Muhtemelen yardımcı oldu: Umiko'nun "Öyleydi aynı operasyonu çağırmak tekrar tekrar ", bu kodda gösterilmeyebilir.
  • Oh! Bunu bilmiyordum! İzlediğim denizaltı @AkiTanaka "sonsuz döngü" diyor
  • @LoganM Gerçekten katılmıyorum. Sadece OP'nin bir animeden gelen bazı kaynak kodlarıyla ilgili bir sorusu olması değil; OP'nin sorusu, yapılan belirli bir açıklama hakkındadır hakkında kaynak kodu animede bir karaktere göre ve anime ile ilgili bir cevap var, yani "Crunchyroll şaka yaptı ve satırı yanlış tercüme etti".
  • @senshin Sanırım aslında sorulandan çok sorunun ne hakkında olmasını istediğinizi okuyorsunuz. Soru, bazı kaynak kodları sağlar ve gerçek hayat C ++ kodu olarak sonsuz bir döngü oluşturup oluşturmadığını sorar. Yeni oyun! kurgusal bir çalışmadır; gerçek yaşam standartlarına uyması için içinde sunulan koda gerek yoktur. Umiko'nun kod hakkında söyledikleri, herhangi bir C ++ standardı veya derleyicisinden daha güvenilirdir. En üstteki (kabul edilen) cevap, herhangi bir evren içi bilgiden bahsetmez. Bence bu konu hakkında iyi bir cevapla sorulabilir, ancak ifade edildiği gibi bu değil.

Kod sonsuz bir döngü değil ama bir hatadır.

İki (muhtemelen üç) sorun var:

  • Hiçbir debuf yoksa hiçbir hasar uygulanmaz
  • 1'den fazla debuf varsa aşırı hasar uygulanacaktır.
  • DestroyMe () nesneyi hemen silerse ve hala işlenecek m_debuf'lar varsa, döngü silinmiş bir nesne üzerinde yürütülür ve belleği çöpe atar. Çoğu oyun motorunun bu sorunu çözmek için bir yok etme kuyruğu vardır ve daha fazlası, bu bir sorun olmayabilir.

Hasar uygulaması döngünün dışında olmalıdır.

İşte düzeltilmiş işlev:

// 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 Kod İncelemesinde miyiz? : D
  • 16777216 HP'nin üzerine çıkmazsanız 4 şamandıra sağlık için harikadır. Vurabileceğiniz ancak ölmeyeceğiniz bir düşman oluşturmak için sağlığı sonsuza bile ayarlayabilirsiniz ve sonsuz bir HP karakterini öldürmeyecek olan sonsuz hasar kullanarak tek öldürme saldırısı yapabilirsiniz (INF-INF'nin sonucu NaN'dir) ancak diğer her şeyi öldürecek. Bu yüzden çok kullanışlı.
  • 1 @ cat Birçok kodlama standardında geleneksel olarak m_ önek, bunun bir üye değişken olduğu anlamına gelir. Bu durumda bir üye değişkeni DestructibleActor.
  • 2 @HotelCalifornia Küçük bir şans olduğuna katılıyorum ApplyToDamage beklendiği gibi çalışmıyor ama verdiğiniz örnek durumda şunu söyleyebilirim ApplyToDamage Ayrıca orijinali geçirmeyi gerektirecek şekilde yeniden çalışılması gerekiyor sourceDamage bu gibi durumlarda debuf'u doğru bir şekilde hesaplayabilmesi için. Mutlak bir bilgiç olmak için: bu noktada, dmg bilgisi orijinal dmg'yi, mevcut dmg'yi ve hasar (lar) ın doğasını ve ayrıca debufların "ateşe karşı savunmasızlık" gibi şeyleri içeren bir yapı olmalıdır. Deneyimlere göre, debuf içeren herhangi bir oyun tasarımının bunları talep etmesi çok uzun sürmez.
  • 1 @StephaneHockenhull iyi dedi!

Kod sonsuz bir döngü yaratmıyor gibi görünüyor.

Döngünün sonsuz olmasının tek yolu,

debuf.ApplyToDamage(resolvedDamage); 

veya

DestroyMe(); 

yeni öğeler eklemek için m_debufs konteyner.

Bu pek olası görünmüyor. Ve durum böyle olsaydı, yinelenirken kabı değiştirdiği için program çökebilirdi.

Program, çağrı nedeniyle büyük olasılıkla çökecektir. DestroyMe(); Muhtemelen şu anda döngüyü çalıştıran mevcut nesneyi yok eder.

Bunu, 'kötü adam'ın' iyi adam'ın düşmesi için bir dalı gördüğü, ancak kesimin yanlış tarafında olduğunu çok geç fark ettiği bir çizgi film olarak düşünebiliriz. Ya da Midgaard Yılanı kendi kuyruğunu yiyor.


Sonsuz bir döngünün en yaygın semptomunun, programı dondurması veya yanıt vermemesine neden olması olduğunu da eklemeliyim. Belleği tekrar tekrar ayırırsa veya sıfıra bölünen bir şey veya benzeri şeyler yaparsa programı çökertecektir.


Aki Tanaka'nın yorumuna göre,

Muhtemelen faydalı: Umiko'nun kodda gösterilmeyen "Aynı işlemi defalarca çağırıyordu" diyen ek ekran görüntüsü.

"Aynı operasyonu defalarca çağırıyordu" Bu daha muhtemeldir.

Varsayalım ki DestroyMe(); birden fazla çağrılmak üzere tasarlanmadıysa, çökmeye neden olma olasılığı daha yüksektir.

Bu sorunu çözmenin bir yolu, if bunun gibi bir şey için:

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

Bu, DestructibleActor yok edildiğinde döngüden çıkar ve 1) DestroyMe yöntem yalnızca bir kez çağrılır ve 2) nesne zaten ölü olarak kabul edildikten sonra buffları gereksiz yere uygulamayın.

2
  • 1 Sağlık <= 0 olduğunda for döngüsünden çıkmak, durumu kontrol etmek için döngü bitene kadar beklemekten kesinlikle daha iyi bir çözümdür.
  • Sanırım muhtemelen break döngünün dışında ve sonra telefon etmek DestroyMe(), sadece güvenli olmak için

Kodla ilgili birkaç sorun var:

  1. Debuf yoksa hasar alınmaz.
  2. DestroyMe() işlev adı tehlikeli geliyor. Nasıl uygulandığına bağlı olarak, bir sorun olabilir veya olmayabilir. Sadece bir fonksiyona sarılmış mevcut nesnenin yıkıcısına yapılan bir çağrı ise, o zaman bir sorun vardır, çünkü nesne kodu yürütürken ortasında yok edilir. Mevcut nesnenin silme olayını sıraya alan bir işleve yapılan bir çağrı ise, nesne yürütmeyi tamamladıktan ve olay döngüsü devreye girdikten sonra yok olacağından herhangi bir sorun yoktur.
  3. Anime'de bahsedildiği gibi görünen asıl mesele, "Aynı operasyonu defalarca çağırıyordu" - onu arayacak DestroyMe() olduğu sürece m_currentHealth <= 0.f ve yinelenecek daha fazla debuff vardır, bu da DestroyMe() defalarca, defalarca çağrılıyor. Döngü ilkinden sonra durmalıdır DestroyMe() call, çünkü bir nesneyi birden fazla kez silmek bellek bozulmasına neden olur ve bu da uzun vadede büyük olasılıkla bir çökmeye neden olur.

Tüm debuff'ların alınan ilk hasara uygulanan etkileri ile, her debuf'un sağlığın yalnızca bir kez alınması yerine sağlığı almasının nedenini gerçekten bilmiyorum, ancak bunun doğru oyun mantığı olduğunu varsayacağım.

Doğru kod

// 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
  • Geçmişte hafıza ayırıcıları yazdığım için aynı hafızayı silmenin sorun olmak zorunda olmadığını belirtmeliyim. Gereksiz de olabilir. Hepsi ayırıcının davranışına bağlı. Benimki düşük seviyeli bağlantılı bir liste gibi davrandı, bu nedenle silinen veriler için "düğüm" ya birkaç kez serbest olarak ayarlanır ya da birkaç kez yeniden silinir (bu sadece gereksiz işaretçi yeniden yönlendirmelerine karşılık gelir). Yine de iyi yakaladın.
  • Double-free bir hatadır ve genellikle tanımlanmamış davranışlara ve çökmelere yol açar. Aynı bellek adresinin yeniden kullanılmasına bir şekilde izin vermeyen özel bir ayırıcınız olsa bile, double-free kötü kokulu bir koddur, çünkü hiçbir anlam ifade etmez ve statik kod çözümleyicileri tarafından size bağırılır.
  • Elbette! Onu bu amaç için tasarlamadım. Bazı diller, özelliklerin eksikliğinden dolayı yalnızca ayırıcı gerektirir. Hayır hayır hayır. Ben sadece kazanın garanti edilmediğini söylüyordum. Belirli tasarım sınıflandırmaları her zaman çökmez.