Anonim

АИМБОТ 2.0

У епизоди 1 Нове игре 2, око 9:40, налази се снимак кода који је написала Нене:

Ево га у текстуалном облику са преведеним коментарима:

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

После пуцњаве, Умико, показујући на петљу фор, рекао је да је разлог зашто се код срушио тај што постоји бесконачна петља.

Заправо не знам Ц ++ па нисам сигуран да ли је истина оно што она говори.

Колико видим, петља фор само се понавља кроз дебуфове које глумац тренутно има. Ако глумац нема бесконачну количину отклона, не мислим да би могао постати бесконачна петља.

Али нисам сигуран јер је једини разлог што постоји шифра кода тај што су овде желели да ставе ускршње јаје, зар не? Управо бисмо добили снимак задњег дела лаптопа и чули како Умико говори „Ох, тамо имате бесконачну петљу“. Чињеница да су заправо показали неки код наводи ме на размишљање да је некако нека врста ускршњег јајета.

Да ли ће код заиста створити бесконачну петљу?

8
  • Вероватно корисно: додатни снимак екрана Умика који каже „Било је позивајући исту операцију изнова и изнова ", што можда неће бити приказано у коду.
  • Ох! Нисам то знао! @АкиТанака у подграђу који сам гледао каже „бесконачна петља“
  • @ЛоганМ Заправо се не слажем. Не ради се само о томе да ОП има питање о неком изворном коду који је случајно произашао из анимеа; ОП-ово питање односи се на одређену изјаву О томе изворни код знаком у анимеу, а ту је и одговор на аниме, наиме „Црунцхиролл је гоофед и погрешно превео линију“.
  • @сенсхин Мислим да читате оно о чему желите да се односи питање, а не оно што се заправо поставља. Питање пружа изворни код и пита га да ли генерише бесконачну петљу као стварни Ц ++ код. Нова игра! је измишљено дело; нема потребе да код представљен у њему буде у складу са стварним стандардима. Оно што Умико каже о коду је меродавније од било којих Ц ++ стандарда или компајлера. Врх (прихваћен) одговор не помиње никакве информације из свемира. Мислим да би се могло поставити питање на тему о овоме са добрим одговором, али како је срочено, то није то.

Код није бесконачна петља, али је грешка.

Постоје два (могуће три) проблема:

  • Ако нема отклоњених грешака, никаква штета неће бити примењена
  • Прекомерна штета ће се применити ако постоји више од 1 отклона
  • Ако ДестроиМе () одмах избрише објекат, а и даље има м_дебуфс за обраду, петља ће се извршити над избрисаним објектом и разметати меморију. Већина покретача игара има ред уништавања да би то заобишао, па и више, што можда и није проблем.

Наношење штете треба да буде ван петље.

Ево исправљене функције:

// 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 Да ли смо на прегледу кода? : Д
  • 4 пловка су одлична за здравље ако не идете изнад 16777216 ХП. Можете чак поставити здравље на бесконачно да бисте створили непријатеља којег можете погодити, али неће умрети, и извршити напад једним убиством користећи бесконачну штету која и даље неће убити бесконачни ХП карактер (резултат ИНФ-ИНФ је НаН), али убиће све остало. Дакле, врло је корисно.
  • 1 @цат Према конвенцији у многим стандардима кодирања, m_ префикс значи да је променљива члана. У овом случају променљива члана DestructibleActor.
  • 2 @ХотелЦалифорниа Слажем се да постоје мале шансе ApplyToDamage не ради како се очекивало, али рекао бих у примеру који наведете ApplyToDamage такође потребно је прерадити да би се захтевало прослеђивање оригинала sourceDamage као и да би у тим случајевима могао правилно да израчуна дебуф. Да будемо апсолутни педант: у овом тренутку дмг информације треба да буду структура која укључује оригинални дмг, тренутни дмг и природу штете, као и ако дебуфи имају ствари попут „рањивости на пожар“. Из искуства, недуго затим било који дизајн игре са дебуфовима захтева ово.
  • 1 @СтепханеХоцкенхулл добро речено!

Чини се да код не ствара бесконачну петљу.

Једини начин на који би петља била бесконачна био би ако

debuf.ApplyToDamage(resolvedDamage); 

или

DestroyMe(); 

су додали нове ставке у m_debufs контејнер.

Ово изгледа мало вероватно. А да је то случај, програм би могао да се сруши због промене контејнера током понављања.

Програм би се највероватније срушио због позива на DestroyMe(); који претпоставља да уништава тренутни објекат који тренутно покреће петљу.

Можемо то замислити као цртани филм у којем 'негативац' види грану да би и 'добри момак пао с њим', али прекасно схвата да је на погрешној страни реза. Или Мидгаард Снаке која једе свој реп.


Такође бих требало да додам да је најчешћи симптом бесконачне петље да замрзава програм или не реагује. Програм ће срушити ако више пута додељује меморију или ако нешто заврши дељењем са нулом или слично.


На основу коментара Акија Танаке,

Вероватно корисно: додатни снимак екрана Умико-а који каже да „Позивао је исту операцију изнова и изнова“, што можда неће бити приказано у коду.

„Позивало се у исту операцију изнова и изнова“ Ово је вероватније.

Претпостављајући да DestroyMe(); није дизајниран за позивање више пута, већа је вероватноћа да ће изазвати пад.

Начин да се поправи овај проблем био би промена if за овако нешто:

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

Ово би изашло из петље када се ДеструцтиблеАцтор уништи, водећи рачуна да 1) DestroyMe метода се позива само једном и 2) немојте бескорисно примењивати баффове када се објекат већ сматра мртвим.

2
  • 1 Прекидање петље фор када је здравље <= 0 дефинитивно је бољи поправак од чекања да се провјери стање након петље.
  • Мислим да бих вероватно 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
  • Требало би да нагласим да, као што сам раније написао алокаторе меморије, брисање исте меморије не мора представљати проблем. Такође би могао бити сувишан. Све зависи од понашања додељивача. Моја се понашала као повезана листа на ниском нивоу, тако да се „чвор“ за избрисане податке неколико пута ослобађа или поново брише неколико пута (што би само одговарало сувишним преусмеравањима показивача). Добар улов ипак.
  • Доубле-фрее је грешка и обично доводи до недефинисаног понашања и рушења. Чак и ако имате прилагођени алокатор који некако онемогућава поновну употребу исте меморијске адресе, доубле-фрее је смрдљив код јер нема смисла и на вас ће викати статички анализатори кода.
  • Наравно! Нисам га дизајнирао у ту сврху. Неки језици само захтевају алокатор због недостатка карактеристика. Не не не. Само сам изјавио да пад није загарантован. Неке класификације дизајна не падају увек.