نواصل الحديث عن مطلق النار 3D خلال عطلة نهاية الأسبوع. إذا كان أي شيء ، فأنا أذكرك أن هذا هو النصف الثاني:
كما قلت ، أبذل قصارى جهدي لدعم رغبة الطلاب في فعل شيء بأيديهم. على وجه الخصوص ، عندما أقدم محاضرة حول مقدمة البرمجة ، ثم كتدريبات عملية ، أترك لهم الحرية الكاملة تقريبًا. هناك اثنين فقط من القيود: لغة البرمجة (C ++) وموضوع المشروع ، يجب أن تكون هذه لعبة فيديو. فيما يلي مثال على واحدة من مئات الألعاب التي قام بها طلابي الجدد:
لسوء الحظ ، يختار معظم الطلاب الألعاب البسيطة مثل منصات ثنائية الأبعاد. أكتب إليكم هذا المقال لإظهار أن خلق وهم لعالم ثلاثي الأبعاد ليس أكثر صعوبة من استنساخ ماريو بروز.
أذكرك أننا توقفنا في مرحلة تسمح لك بتركيب الجدران:

المرحلة 13: رسم الوحوش على الخريطة
ما هو الوحش في لعبتنا؟ هذه هي الإحداثيات ورقم الملمس:
struct Sprite { float x, y; size_t tex_id; }; [..] std::vector<Sprite> sprites{ {1.834, 8.765, 0}, {5.323, 5.365, 1}, {4.123, 10.265, 1} };
بعد تحديد العديد من الوحوش ، لبداية نرسمها ببساطة على الخريطة:

التغييرات
التي يمكنك رؤيتها هنا .

المرحلة 14: المربعات السوداء بدلاً من الوحوش ثلاثية الأبعاد
الآن سوف نرسم العفاريت في نافذة 3D. للقيام بذلك ، نحتاج إلى تحديد شيئين: موضع العفريت على الشاشة وحجمها. فيما يلي الوظيفة التي ترسم مربعًا أسود بدلاً من كل عبّارة:
void draw_sprite(Sprite &sprite, FrameBuffer &fb, Player &player, Texture &tex_sprites) {
دعونا معرفة كيف يعمل. هنا هو الرسم البياني:

في السطر الأول ، نعتبر الزاوية المطلقة sprite_dir (الزاوية بين الاتجاه من المشغِّل إلى العفريت والمفارقة). من الواضح أن الزاوية النسبية بين العفريت واتجاه النظرة يتم الحصول عليها ببساطة بطرح زاويتين مطلقتين: sprite_dir - player.a. المسافة من المشغل إلى العفريت تافهة لحسابها ، وحجم العفريت هو تقسيم بسيط لحجم الشاشة من خلال المسافة. حسنًا ، في هذه الحالة ، قمت بقطع ألفين من الأعلى حتى لا تحصل على مربعات عملاقة (بالمناسبة ، يمكن تقسيم هذه الشفرة بسهولة على الصفر). يعطي h_offset و v_offset إحداثيات الزاوية العليا اليسرى من العفريت على الشاشة ؛ ثم حلقة مزدوجة بسيطة تملأ مربع لدينا مع الأسود. تحقق باستخدام قلم وقطعة من الورق تُحسب بشكل صحيح h_offset و v_offset ، في التزامي يوجد خطأ (غير حرج) ، صدق الرمز في المقالة :) حسنًا ، تم أيضًا إصلاح الكود الأكثر حداثة في المستودع.

التغييرات
التي يمكنك رؤيتها هنا .

الخطوة 15: خريطة العمق
المربعات لدينا جيدة بأعجوبة ، ولكن هناك مشكلة واحدة فقط: الوحوش البعيدة تتجول في الزاوية ، والمربع مرسوم بالكامل. كيف تكون بسيط جدا نرسم العفاريت
بعد رسم الجدران. لذلك ، لكل عمود من شاشتنا ، نعرف المسافة إلى أقرب جدار. نحفظ هذه المسافات في صفيف من 512 قيمة ، ونمرر الصفيف إلى وظيفة عرض العفريت. يتم رسم العفاريت أيضًا عمودًا تلو الآخر ، لذلك سنقوم بمقارنة المسافة معها بالقيمة من مجموعة العمق لكل عمود في العفريت.

التغييرات
التي يمكنك رؤيتها هنا .

المرحلة 16: مشكلة العفاريت
لقد تحولوا إلى وحوش عظيمة ، أليس كذلك؟ لكن في هذه المرحلة لن أضيف أي وظيفة ، على العكس من ذلك ، سأفشل في كل شيء بإضافة وحش آخر:

التغييرات
التي يمكنك رؤيتها هنا .

المرحلة 17: فرز العفاريت
ما هي المشكلة؟ المشكلة هي أنه يمكنني الحصول على ترتيب تعسفي لرسم العفاريت ، ولكل منهما أقارن المسافة بينها وبين الجدران ، ولكن ليس مع العفاريت الأخرى ، لذلك الزحف مخلوق بعيد عبر أقرب واحد. هل من الممكن تكييف حل مع خريطة عمق لرسم العفاريت؟
النص المخفيالجواب الصحيح هو "يمكنك". لكن كيف؟ اكتب في التعليقات.
سأذهب في الاتجاه الآخر ، وحل المشكلة بغباء في الجبهة. سأوجه فقط كل العفاريت من أبعدها إلى أبعدها. بمعنى ، سأفرز العفاريت بترتيب تنازلي للمسافة ، وسأرسمها بهذا الترتيب.

التغييرات
التي يمكنك رؤيتها هنا .

الخطوة 18: وقت SDL
لقد حان الوقت ل SDL. هناك الكثير من مكتبات النوافذ المختلفة للنظام الأساسي ، ولا أفهمها على الإطلاق. أنا شخصياً أحب
imgui ، لكن لسبب ما يفضل طلابي SDL ، لذا فأنا
أرتبط به. مهمة هذه المرحلة بسيطة للغاية: قم بإنشاء نافذة وعرض الصورة من المرحلة السابقة:

التغييرات
التي يمكنك رؤيتها هنا . لم أعد أقدم رابطًا إلى gitpod ، لأن لم يتعلم SDL في المتصفح حتى الآن لبدء :(
تحديث: تعلمت! يمكنك تشغيل الكود في نقرة واحدة في المتصفح!
الخطوة 19: معالجة الأحداث والتنظيف
إضافة رد فعل على ضربات المفاتيح ليست مضحكة ، وأنا لن أصف. عند إضافة SDL ، أزلت التبعية على stb_image.h. إنه جميل ، لكنه يستغرق وقتًا طويلاً للتجميع.
بالنسبة لأولئك الذين لا يفهمون ، فإن مصادر المرحلة التاسعة عشرة
موجودة هنا . حسنًا ، إليك أداء نموذجي:
الخاتمة
تحتوي الكود الخاص بي في الوقت الحالي على 486 سطرًا ، وفي الوقت نفسه لم أحفظها على الإطلاق:
haqreu@daffodil:~/tinyraycaster$ cat *.cpp *.h | wc -l 486
لم ألعق الكود الخاص بي ، وأتعمد غسل الملابس القذرة. نعم ، أنا أكتب مثل هذا (وليس لي فقط). صباح أحد أيام السبت فقط جلست وكتبت هذا :)
لم أجعل اللعبة النهائية ، مهمتي هي فقط إعطاء قوة دفع أولية لرحلة خيالك. اكتب الكود الخاص بك ، ربما سيكون أفضل من الكود الخاص بي. مشاركة الرمز الخاص بك ، وتبادل أفكارك ، وإرسال طلبات السحب.