AIMBOT 2.0
في الحلقة الأولى من اللعبة الجديدة 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(); } } }
بعد اللقطة ، قال أوميكو ، مشيرًا إلى حلقة for ، إن سبب تعطل الكود هو وجود حلقة لا نهائية.
لا أعرف حقًا C ++ ، لذلك لست متأكدًا مما إذا كان ما تقوله صحيحًا.
من خلال ما يمكنني رؤيته ، فإن حلقة for تقوم فقط بالتكرار خلال debufs التي يمتلكها الممثل حاليًا. ما لم يكن لدى الممثل كمية لا حصر لها من debufs ، لا أعتقد أنه يمكن أن يصبح حلقة لا نهائية.
لكنني لست متأكدًا لأن السبب الوحيد لوجود لقطة للكود هو أنهم أرادوا وضع بيضة عيد الفصح هنا ، أليس كذلك؟ كنا قد حصلنا للتو على لقطة للجزء الخلفي من الكمبيوتر المحمول وسمعنا أوميكو يقول "أوه ، لديك حلقة لا نهائية هناك". حقيقة أنهم أظهروا بالفعل بعض الرموز تجعلني أعتقد أن الكود بطريقة ما عبارة عن بيضة عيد الفصح من نوع ما.
هل ستقوم الشفرة بالفعل بإنشاء حلقة لا نهائية؟
8- ربما تكون مفيدة: لقطة شاشة إضافية لأوميكو تقول "لقد كانت كذلك استدعاء نفس العملية مرارًا وتكرارًا "، والتي قد لا تظهر في الكود.
- أوه! لم أكن أعرف ذلك! AkiTanaka الغواصة التي شاهدتها تقول "حلقة لا نهائية"
- LoganM أنا لا أوافق حقًا. لا يقتصر الأمر على أن OP لديه سؤال حول بعض الكود المصدري الذي حدث في أنمي ؛ سؤال OP هو حول بيان معين تم الإدلاء به حول شفرة المصدر بواسطة شخصية في الأنمي ، وهناك إجابة متعلقة بالأنمي ، وهي "Crunchyroll فعل أحمق وأخطأ في ترجمة الخط".
- senshin أعتقد أنك تقرأ ما تريد أن يكون السؤال عنه ، بدلاً من السؤال الفعلي. يوفر السؤال بعض التعليمات البرمجية المصدر ويسأل عما إذا كان يولد حلقة لا نهائية كرمز C ++ حقيقي. لعبة جديدة! هو عمل خيالي. ليست هناك حاجة للكود المقدم فيه ليتوافق مع معايير الحياة الواقعية. ما يقوله Umiko عن الكود هو أكثر موثوقية من أي معايير C ++ أو المجمعين. لا تحتوي الإجابة العلوية (المقبولة) على أي ذكر لأي معلومات في الكون. أعتقد أنه يمكن طرح سؤال حول هذا الموضوع بإجابة جيدة ، ولكن كما هو مكتوب ، هذا ليس كذلك.
الكود ليس حلقة لا نهائية ولكنه خطأ.
هناك مشكلتان (ربما ثلاث):
- في حالة عدم وجود debuf لن يتم تطبيق أي ضرر على الإطلاق
- سيتم تطبيق ضرر جسيم إذا كان هناك أكثر من 1 debuf
- إذا قام 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 هل نحن في مراجعة التعليمات البرمجية؟ :د
- 4 عوامات رائعة للصحة إذا لم تتجاوز 16777216 حصان. يمكنك حتى ضبط الصحة على ما لا نهاية لإنشاء عدو يمكنك ضربه ولكنك لن تموت ، ولديك هجوم قاتل واحد باستخدام ضرر لا نهائي لا يزال لن يقتل شخصية HP غير محدودة (نتيجة INF-INF هي NaN) ولكن سيقتل كل شيء آخر. لذلك فهو مفيد للغاية.
- 1cat وفقًا للاتفاقية في العديد من معايير الترميز ، فإن
m_
البادئة تعني أنه متغير عضو. في هذه الحالة متغير عضو منDestructibleActor
. - 2HotelCalifornia أوافق على وجود فرصة صغيرة
ApplyToDamage
لا يعمل كما هو متوقع ولكن في حالة المثال الذي أعطيته أود أن أقولApplyToDamage
أيضا يحتاج إلى إعادة صياغة لتتطلب تمريره الأصليsourceDamage
وكذلك بحيث يمكنه حساب debuf بشكل صحيح في تلك الحالات. لكي تكون متحذلقًا مطلقًا: في هذه المرحلة ، يجب أن تكون معلومات dmg عبارة عن هيكل يتضمن dmg الأصلي و dmg الحالي وطبيعة الضرر (الأضرار) وكذلك إذا كانت debufs بها أشياء مثل "قابلية التعرض لإطلاق النار". من التجربة ، لم يمض وقت طويل قبل أن يطلب أي تصميم للعبة مع debufs هذه. - 1 @ ستيفاني هوكنهول قال جيدًا!
لا يبدو أن الكود ينشئ حلقة لا نهائية.
الطريقة الوحيدة التي ستكون بها الحلقة غير محدودة هي إذا
debuf.ApplyToDamage(resolvedDamage);
أو
DestroyMe();
كانت لإضافة عناصر جديدة إلى m_debufs
حاوية.
هذا يبدو غير مرجح. وإذا كان الأمر كذلك ، فقد يتعطل البرنامج بسبب تغيير الحاوية أثناء التكرار.
من المرجح أن يتعطل البرنامج بسبب الاتصال بـ DestroyMe();
الذي يفترض أنه يدمر الكائن الحالي الذي يقوم بتشغيل الحلقة حاليًا.
يمكننا أن نفكر في الأمر على أنه رسم كاريكاتوري حيث رأى "الشرير" فرعًا لكي يسقط "الرجل الطيب" معه ، لكنه يدرك بعد فوات الأوان أنه في الجانب الخطأ من القص. أو ثعبان Midgaard يأكل ذيله.
يجب أن أضيف أيضًا أن أكثر الأعراض شيوعًا للحلقة اللانهائية هو أنها تجمد البرنامج أو تجعله غير مستجيب. سوف يتعطل البرنامج إذا خصص الذاكرة بشكل متكرر ، أو قام بشيء ينتهي بالقسمة على صفر ، أو الإعجابات.
بناءً على تعليق أكي تاناكا ،
ربما تكون مفيدة: لقطة شاشة إضافية لأوميكو تقول "كانت تستدعي نفس العملية مرارًا وتكرارًا" ، والتي قد لا تظهر في الكود.
"كانت تستدعي نفس العملية مرارا وتكرارا" هذا هو الأرجح.
افترض أن DestroyMe();
لم يتم تصميمه ليتم استدعاؤه أكثر من مرة ، فمن المرجح أن يتسبب في حدوث عطل.
تتمثل إحدى طرق إصلاح هذه المشكلة في تغيير ملف if
لشيء مثل هذا:
if (m_currentHealth <= 0.f) { m_currentHealth = 0.f; DestroyMe(); break; }
سيؤدي ذلك إلى الخروج من الحلقة عندما يتم تدمير الفاعل المدمر ، مع التأكد من أن 1) ملف DestroyMe
يتم استدعاء الطريقة مرة واحدة فقط و 2) لا تطبق التحسينات بدون فائدة بمجرد اعتبار الكائن ميتًا بالفعل.
- 1 الخروج من الحلقة for عندما تكون الصحة <= 0 هي بالتأكيد حل أفضل من الانتظار حتى بعد الحلقة للتحقق من الصحة.
- أعتقد أنني ربما
break
خارج الحلقة و من ثم مكالمةDestroyMe()
، مجرد أن تكون آمنة
توجد عدة مشكلات في الكود:
- إذا لم يكن هناك debufs ، فلن يحدث أي ضرر.
DestroyMe()
اسم الوظيفة يبدو خطيرا. اعتمادًا على كيفية تنفيذه ، قد تكون مشكلة أو لا. إذا كانت مجرد دعوة إلى مدمر الكائن الحالي الملفوف في دالة ، فهناك مشكلة ، حيث سيتم إتلاف الكائن في منتصف تنفيذ التعليمات البرمجية. إذا كانت عبارة عن استدعاء لوظيفة تضع حدث الحذف للكائن الحالي في قائمة الانتظار ، فلا توجد مشكلة ، حيث سيتم تدمير الكائن بعد أن يكمل تنفيذه وتبدأ حلقة الحدث.- المشكلة الفعلية التي يبدو أنها مذكورة في الأنمي ، "لقد كانت تستدعي نفس العملية مرارًا وتكرارًا" - سوف تستدعي
DestroyMe()
طالماm_currentHealth <= 0.f
وهناك المزيد من debuffs المتبقية للتكرار ، مما قد يؤدي إلىDestroyMe()
يتم الاتصال بها عدة مرات ، مرارًا وتكرارًا. يجب أن تتوقف الحلقة بعد الأولىDestroyMe()
الاتصال ، لأن حذف كائن أكثر من مرة يؤدي إلى تلف الذاكرة ، والذي من المحتمل أن يؤدي إلى تعطل على المدى الطويل.
لست متأكدًا حقًا من سبب إبعاد كل debuf عن الصحة ، بدلاً من التخلص من الصحة مرة واحدة فقط ، مع تطبيق تأثيرات جميع debuff على الضرر الأولي ، لكنني سأفترض أن هذا هو منطق اللعبة الصحيح.
سيكون الرمز الصحيح
// 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 أحد الأخطاء ، ويؤدي عمومًا إلى سلوك غير محدد وتعطل. حتى إذا كان لديك مُخصص مخصص لا يسمح بطريقة ما بإعادة استخدام عنوان الذاكرة نفسه ، فإن الرمز الخالي من المضاعفة هو رمز كريه الرائحة لأنه لا معنى له وسيصيح عليك محللو الكود الثابت.
- بالتاكيد! لم أصممه لهذا الغرض. تتطلب بعض اللغات مخصصًا فقط بسبب نقص الميزات. لا لا لا. كنت أقول فقط أن وقوع حادث غير مضمون. بعض تصنيفات التصميم لا تتعطل دائمًا.