خلق roguelike في الوحدة من الصفر: مولد المحصنة

صورة

هذه المرة سوف نغرق في تنفيذ خوارزمية مولد المحصنة. في المقالة الأخيرة ، أنشأنا الغرفة الأولى ، والآن سننشئ ما تبقى من مستوى المحصنة.

ولكن قبل أن نبدأ ، أود إصلاح خطأ من مشاركة سابقة. في الواقع ، تعلمت في الأسابيع الأخيرة شيئًا جديدًا ، وهذا هو السبب في أن بعض العمل الذي قمت به أصبح قديمًا ، وأريد التحدث عنه.

هل تتذكر فئة المركز التي أنشأناها؟ في الواقع ، تحتوي Unity بالفعل على فئة مدمجة تؤدي نفس الوظائف تمامًا ، ولكن مع تحكم أفضل قليلاً - من السهل الإعلان والمعالجة. تسمى هذه الفئة Vector2Int. لذلك ، قبل البدء ، سنقوم بإزالة فئة Position من MapManager.cs واستبدال كل متغير Position بمتغير Vector2Int.


يجب القيام بنفس الشيء في عدة أماكن في البرنامج النصي DungeonGenerator.cs. الآن دعنا ننزل إلى بقية الخوارزمية.

المرحلة 7 - توليد الغرفة / القاعة


سنبدأ بتغيير بسيط على الوظيفة FirstRoom () التي تم إنشاؤها في المرة الأخيرة. بدلاً من إنشاء وظيفة أخرى لإنشاء جميع العناصر الأخرى للخريطة وتكرار مجموعة من التعليمات البرمجية ، نقوم ببساطة بتحويل هذه الوظيفة ، وتحويلها إلى GenerateFeature () عام. لذلك ، قم بتغيير الاسم من FirstRoom إلى GenerateFeature.

الآن سنحتاج لتمرير المعلمات لهذه الوظيفة. بادئ ذي بدء ، تحتاج إلى معرفة الوظيفة التي تولدها - غرفة أو ممر. يمكننا فقط تمرير سلسلة تسمى النوع. بعد ذلك ، تحتاج الوظيفة إلى معرفة نقطة البداية للعنصر ، أي من أي جدار يأتي منه (لأننا دائمًا ننشئ عنصرًا جديدًا من جدار العنصر الأقدم) ، ولهذا السبب ، فإن حجة الجدار كافية. أخيرًا ، تحتوي الغرفة الأولى التي يتم إنشاؤها على خصائص خاصة ، لذلك نحتاج إلى متغير منطقي اختياري يخبرنا ما إذا كان العنصر هو الغرفة الأولى. بشكل افتراضي ، فإنه خطأ: bool isFirst = false. لذا سيتغير عنوان الوظيفة من هذا:


على هذا:


غرامة. والخطوة التالية هي تغيير طريقة حساب عرض العنصر وارتفاعه. بينما نحسبها ، نحصل على قيمة عشوائية بين القيم الدنيا والقصوى لارتفاع وعرض الغرف - هذا مثالي للغرف ، لكنه لن يعمل في الممرات. حتى الآن لدينا ما يلي:


لكن الممرات سيكون لها حجم ثابت يبلغ 3 في العرض أو الارتفاع ، اعتمادًا على الاتجاه. لذلك ، نحتاج إلى التحقق من العنصر - غرفة أو ممر ، ثم إجراء الحسابات المناسبة.


وبالتالي. نتحقق مما إذا كان العنصر عبارة عن غرفة. إذا كانت الإجابة بنعم ، فإننا نفعل نفس الشيء كما كان من قبل
- نحصل على رقم عشوائي في الفترة الفاصلة بين الحد الأدنى والحد الأقصى للارتفاع والعرض. ولكن الآن في مكان آخر من نفس الشيء إذا كنت بحاجة إلى القيام بشيء مختلف قليلاً. نحتاج إلى التحقق من اتجاه الممر. لحسن الحظ ، عند إنشاء جدار ، نقوم بحفظ المعلومات حول الاتجاه الذي يتم توجيهه إليه ، لذلك نستخدمه للحصول على اتجاه الممر.


ولكننا لم نعلن بعد minCorridorLength المتغير. تحتاج إلى العودة إلى الإعلانات المتغيرة والإعلان عنها ، مباشرة فوق maxCorridorLength.


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


رائع. وهنا انتهى الأمر بتغيير حجم العنصر الجديد. الآن عليك أن تقرر أين تضعه. وضعنا الغرفة الأولى في مكان عشوائي ضمن قيم العتبة بالنسبة لمركز الخريطة.


ولكن بالنسبة لجميع العناصر الأخرى ، لن ينجح ذلك. يجب أن تبدأ بجانب النقطة العشوائية على الحائط التي يتم إنشاء العنصر منها. لذا دعنا نغير الكود. أولاً ، نحتاج إلى التحقق مما إذا كان العنصر هو الغرفة الأولى. إذا كانت هذه هي الغرفة الأولى ، فإننا نحدد نقاط البداية بنفس الطريقة السابقة - نصف عرض الخريطة وارتفاعها.


في حالة أخرى ، إذا لم يكن العنصر هو الغرفة الأولى ، فإننا نحصل على نقطة عشوائية على الجدار الذي يتم إنشاء العنصر منه. أولاً ، نحتاج إلى التحقق مما إذا كان للجدار حجم 3 (وهذا يعني أنه نقطة النهاية للممر) ، وإذا كان الأمر كذلك ، فسيتم تحديد النقطة الوسطى دائمًا ، أي المؤشر 1 من مجموعة الجدار (مع 3 عناصر ، فإن المصفوفة لديها الفهارس 0 ، 1 ، 2). ولكن إذا كان الحجم لا يساوي 3 (الجدار ليس نقطة النهاية للممر) ، فإننا نأخذ نقطة عشوائية في الفترة الفاصلة بين النقطة 1 وطول الجدار ناقص 2. هذا ضروري لتجنب الممرات التي تم إنشاؤها في الزاوية. أي ، على سبيل المثال ، على جدار بطول 6 ، نستبعد الفهارس 0 و 5 (الأول والأخير) ، ونختار نقطة عشوائية بين النقاط 1 و 2 و 3 و 4.


الآن لدينا موقع النقطة على الحائط حيث سيتم إنشاء عنصر جديد. ولكن لا يمكننا فقط البدء في توليد عنصر من هناك ، لأنه بهذه الطريقة سيتم حظره بواسطة الجدران الموضوعة بالفعل. من المهم أيضًا ملاحظة أن العنصر يبدأ في توليده من الزاوية السفلية اليسرى ، ثم يتم تنفيذ الزيادة إلى اليمين وأعلى ، لذلك يجب علينا ضبط موضع البدء في أماكن مختلفة ، اعتمادًا على الاتجاه الذي ينظر فيه الجدار. بالإضافة إلى ذلك ، سيكون العمود الأول x والصف الأول y عبارة عن جدران ، وإذا بدأنا عنصرًا جديدًا بجوار نقطة على الحائط مباشرة ، فيمكننا إنشاء ممر ينتهي في زاوية الغرفة ، وليس في مكان مناسب على الحائط.

لذلك ، إذا تم توجيه الجدار إلى الشمال ، فمن الضروري أن يبدأ العنصر في موضع واحد إلى الشمال على طول المحور y ، ولكن في عدد عشوائي من المواضع إلى الغرب على طول المحور x ، في النطاق من 1 إلى عرض الغرفة -2. في الاتجاه الجنوبي ، يعمل المحور x بنفس الطريقة ، ولكن موضع البدء على المحور y هو موضع النقطة على الحائط ناقص ارتفاع الغرفة. تتبع الجدران الغربية والشرقية نفس المنطق ، فقط مع محاور مقلوبة.

ولكن قبل القيام بكل هذا ، نحتاج إلى حفظ موضع نقطة الجدار في متغير Vector2Int حتى نتمكن من معالجتها لاحقًا.


عظيم. لنفعل ذلك.


لذا ، أنشأنا عنصرًا بالحجم والموضع ، والخطوة التالية هي وضع العنصر على الخريطة. ولكن أولاً ، نحتاج إلى معرفة ما إذا كان هناك بالفعل مساحة على الخريطة لهذا العنصر في هذا الموضع. في الوقت الحالي ، نسمي فقط دالة CheckIfHasSpace (). سيتم تمييزه باللون الأحمر ، لأننا لم ننفذه بعد. سنفعل ذلك مباشرة بعد الانتهاء من ما يجب القيام به هنا في دالة GenerateFeature (). لذلك ، تجاهل التسطير الأحمر واستمر.


في الجزء التالي ، يتم إنشاء الجدران. حتى ونحن على اتصال به، باستثناء جزء في الثانية لل حلقة .


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

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


مدهش ، لقد انتهينا تقريبا هنا. الآن لدينا عنصر جديد تمامًا ، تم إنشاؤه في المكان الصحيح ، ولكنه هو نفسه مثل الغرفة الأولى: وهو محاط بالكامل بالجدران. هذا يعني أن اللاعب لن يتمكن من الوصول إلى هذا المكان الجديد. أي أننا بحاجة إلى تحويل نقطة على الحائط (والتي ، كما نذكر ، يتم تخزينها في متغير من نوع Vector2Int) والنقطة المقابلة على الحائط لعنصر جديد في Floor. ولكن فقط عندما لا يكون العنصر هو الغرفة الأولى.


يتحقق هذا الجزء من التعليمات البرمجية لمعرفة ما إذا كان العنصر الجديد هو الغرفة الأولى. إذا لم يكن كذلك ، فإنه يحول الموضع الأخير للجدار إلى الأرض ، ثم يتحقق من الاتجاه الذي يتطلع إليه الجدار للتحقق من بلاط العنصر الجديد الذي يجب أن يتحول إلى الأرض.

لقد وصلنا إلى الجزء الأخير من دالة GenerateFeature (). لديها بالفعل خطوط تضيف معلومات حول العنصر الذي تنشئه الوظيفة.


هنا نحتاج إلى تغيير شيء ما. أولاً ، نوع العنصر لا يساوي دائمًا الغرفة. لحسن الحظ ، يتم تمرير المتغير المطلوب إلى الوظيفة كمعلمة ، وهي سلسلة النوع. لذا دعنا نستبدل "Room" هنا بالنوع.


حسن. الآن ، لكي تعمل الخوارزمية التي تولد جميع عناصر اللعبة بشكل صحيح ، نحتاج إلى إضافة بيانات جديدة هنا. وهي int تحسب عدد العناصر التي تم إنشاؤها وقائمة بجميع العناصر التي تم إنشاؤها. ننتقل إلى المكان الذي نعلن فيه جميع المتغيرات ونعلن int مع اسم countFeatures ، بالإضافة إلى قائمة بالعناصر التي تحمل اسم allFeatures. يجب أن تكون قائمة جميع العناصر عامة ، ويمكن أن يكون عداد int خاصًا.


عد الآن إلى دالة GenerateFeature () وأضف بضعة أسطر إلى النهاية: زيادة متغير countFeatures وإضافة عنصر جديد إلى قائمة allFeatures.


لذا ، اكتمل إنشاء GenerateFeature (). سنحتاج لاحقًا إلى الرجوع إليها لملء وظيفة CheckIfHasSpace الفارغة ، ولكننا نحتاج أولاً إلى إنشائها. هذا ما سنفعله الآن.

المرحلة 8 - تحقق مما إذا كان هناك مكان


الآن دعنا ننشئ وظيفة جديدة مباشرة بعد اكتمال وظيفة GenerateFeature (). إنها تحتاج إلى حجتين: الموضع الذي يبدأ فيه العنصر ، والموضع الذي ينتهي عنده. يمكنك استخدام متغيرين Vector2Int كلاهما. يجب أن تُرجع الدالة قيمة منطقية حتى يمكن استخدامها في حالة التحقق من وجود مساحة.


تم تسطيره باللون الأحمر ، لأنه لم يرد أي شيء حتى الآن. سنقوم بإصلاحه قريبًا ، ولكن في الوقت الحالي لن نهتم. في هذه الوظيفة ، سننتقل عبر جميع المواضع بين بداية العنصر ونهايته ، ونتحقق مما إذا كان الموضع الحالي في MapManager.map خاليًا أو هناك شيء موجود بالفعل. إذا كان هناك شيء هناك ، فإننا نوقف الوظيفة ونرجع خطأ. إذا لم يكن كذلك ، فتابع. إذا وصلت الوظيفة إلى نهاية الحلقة دون مقابلة الأماكن المملوءة ، فعندئذٍ ترجع true.

بالإضافة إلى ذلك ، قبل التحقق من الموضع للصفر ، نحتاج إلى خط للتحقق مما إذا كان الموضع داخل الخريطة. لأنه بخلاف ذلك ، قد نحصل على خطأ في فهرس المصفوفة وتعطل اللعبة.


غرامة. نعود الآن إلى المكان الذي ندخل فيه هذه الوظيفة داخل دالة GenerateFeature (). نحتاج إلى إصلاح هذه المكالمة لأنها لا تجتاز الحجج اللازمة.

هنا نريد إدراج عبارة if للتحقق مما إذا كانت هناك مساحة كافية للعنصر. إذا كانت النتيجة خاطئة ، فإننا ننهي الوظيفة دون إدراج عنصر جديد في MapManager.map.


نحن بحاجة إلى تمرير الوسيطات المطلوبة ، أي متغيرين Vector2Int. مع الأول ، كل شيء بسيط ، هذا هو الموقف مع إحداثيات س وص لنقطة بداية العنصر.


والثاني أصعب ، ولكن ليس بالكثير. هذه هي نقطة البداية بالإضافة إلى الارتفاع لـ y وعرض x ، وطرح 1 من كليهما (لأن البداية قد تم أخذها في الاعتبار بالفعل).


الآن دعنا ننتقل إلى الخطوة التالية - إنشاء خوارزمية لاستدعاء وظيفة GenerateFeature ().

المرحلة 9 - العناصر التي تم إنشاؤها استدعاء


العودة إلى دالة GenerateDungeon () التي تم إنشاؤها في الجزء السابق من المقالة. الآن يجب أن يبدو مثل هذا:


تم تمييز استدعاء FirstRoom () باللون الأحمر لأننا قمنا بتغيير اسم هذه الوظيفة. لذا دعنا نسمي الجيل الأول من الغرف.


لقد مررنا الحجج اللازمة: "الغرفة" كنوع ، لأن الغرفة الأولى ستكون دائمًا الغرفة ، والجدار الجديد () ، لأن الغرفة الأولى لن يتم إنشاؤها من أي غرفة أخرى ، لذا فإننا نمرر فارغة ، وهذا أمر طبيعي تمامًا. بدلاً من Wall () الجديد ، يمكنك استبدال null ، فهذه مسألة تفضيل شخصي. تحدد الوسيطة الأخيرة ما إذا كان العنصر الجديد هو الغرفة الأولى، وذلك في حالتنا نحن نمر صحيحا .

الآن نأتي إلى النقطة الرئيسية. نستخدم حلقة للتشغيل 500 مرة - نعم ، سنحاول إضافة العناصر 500 مرة. ولكن إذا كان عدد العناصر التي تم إنشاؤها (متغير countFeatures) يساوي الحد الأقصى لعدد العناصر المحدد (متغير maxFeatures) ، فإننا نقاطع هذه الدورة.


الخطوة الأولى في هذه الحلقة هي إعلان العنصر الذي سيتم إنشاء العنصر الجديد منه. إذا أنشأنا عنصرًا واحدًا فقط (الغرفة الأولى) ، فسيكون العنصر الأصلي. بخلاف ذلك ، نختار عشوائيًا أحد العناصر التي تم إنشاؤها بالفعل.


الآن سنختار أي جدار من هذا العنصر سيتم استخدامه لإنشاء العنصر الجديد.


يرجى ملاحظة أنه ليس لدينا هذه الوظيفة ChoseWall () حتى الآن. لنكتبها بسرعة. انتقل إلى نهاية الوظيفة وقم بإنشائها. يجب أن يُرجع جدارًا ، ويستخدم عنصرًا كوسيطة ، بحيث يمكن للوظيفة تحديد جدار هذا العنصر.


قمت بإنشائه بين وظائف CheckIfHasSpace () و DrawMap (). لاحظ أنه إذا كنت تعمل في Visual Studio ، والذي تم تثبيته مع Unity ، فيمكنك استخدام - / + الحقول الموجودة على اليسار لطي / توسيع أجزاء من التعليمات البرمجية لتبسيط العمل.

في هذه الوظيفة ، سنجد الجدار الذي لم يتم إنشاء العنصر منه بعد. في بعض الأحيان ، سنحصل على عناصر بجدار واحد أو أكثر تم إرفاق عناصر أخرى به بالفعل ، لذلك نحتاج إلى التحقق مرة أخرى ومرة ​​أخرى مما إذا كانت أي من الجدران العشوائية مجانية. للقيام بذلك ، نستخدم حلقة متكررة عشر مرات - إذا لم يتم العثور على جدار حر بعد هذه العشر مرات ، فإن الدالة ترجع فارغة.


الآن عد إلى الدالة GenerateDungeon () وقم بتمرير العنصر الأصلي كمعلمة إلى دالة ChoseWall ().


if (wall == null) continue;يعني السطر أنه إذا أعادت وظيفة البحث عن الجدار خطأ ، فلن يتمكن العنصر الأصلي من إنشاء عنصر جديد ، وبالتالي ستستمر الوظيفة في الدورة ، أي أنه لا يمكن إنشاء عنصر جديد والانتقال إلى التكرار التالي للدورة.

الآن نحن بحاجة إلى تحديد نوع العنصر التالي. إذا كان العنصر المصدر عبارة عن غرفة ، فيجب أن يكون العنصر التالي ممرًا (لا نريد أن تؤدي الغرفة مباشرة إلى غرفة أخرى بدون ممر بينهما). ولكن إذا كان هذا ممرًا ، فنحن بحاجة إلى خلق احتمالية أن يكون هناك ممر أو غرفة أخرى بعد ذلك.


غرامة. الآن نحن بحاجة فقط إلى استدعاء دالة GenerateFeature () ، لتمريرها على الجدار والكتابة كمعلمات.


أخيرًا ، انتقل إلى مفتش الوحدة ، حدد كائن GameManager وقم بتغيير القيم إلى ما يلي:


إذا نقرت الآن على زر التشغيل ، فسترى النتائج بالفعل!


كما قلت ، هذه ليست أفضل زنزانة. لدينا الكثير من الطرق المسدودة. ولكنها تعمل بكامل طاقتها ، وتضمن أنه لن يكون لديك غرفة غير متصلة بأي غرفة أخرى.

اتمنى ان تكون قد استمتعت به! في المنشور التالي ، سننشئ لاعبًا سينتقل عبر الزنزانة ، ثم سنحول الخريطة من ASCII إلى نقش متحرك.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Post3 : MonoBehaviour {
    public int mapWidth;
    public int mapHeight;

    public int widthMinRoom;
    public int widthMaxRoom;
    public int heightMinRoom;
    public int heightMaxRoom;

    public int minCorridorLength;
    public int maxCorridorLength;
    public int maxFeatures;
    int countFeatures;

    public bool isASCII;

    public List<Feature> allFeatures;

    public void InitializeDungeon() {
        MapManager.map = new Tile[mapWidth, mapHeight];
    }

    public void GenerateDungeon() {
        GenerateFeature("Room", new Wall(), true);

        for (int i = 0; i < 500; i++) {
            Feature originFeature;

            if (allFeatures.Count == 1) {
                originFeature = allFeatures[0];
            }
            else {
                originFeature = allFeatures[Random.Range(0, allFeatures.Count - 1)];
            }

            Wall wall = ChoseWall(originFeature);
            if (wall == null) continue;

            string type;

            if (originFeature.type == "Room") {
                type = "Corridor";
            }
            else {
                if (Random.Range(0, 100) < 90) {
                    type = "Room";
                }
                else {
                    type = "Corridor";
                }
            }

            GenerateFeature(type, wall);

            if (countFeatures >= maxFeatures) break;
        }

        DrawMap(isASCII);
    }

    void GenerateFeature(string type, Wall wall, bool isFirst = false) {
        Feature room = new Feature();
        room.positions = new List<Vector2Int>();

        int roomWidth = 0;
        int roomHeight = 0;

        if (type == "Room") {
            roomWidth = Random.Range(widthMinRoom, widthMaxRoom);
            roomHeight = Random.Range(heightMinRoom, heightMaxRoom);
        }
        else {
            switch (wall.direction) {
                case "South":
                    roomWidth = 3;
                    roomHeight = Random.Range(minCorridorLength, maxCorridorLength);
                    break;
                case "North":
                    roomWidth = 3;
                    roomHeight = Random.Range(minCorridorLength, maxCorridorLength);
                    break;
                case "West":
                    roomWidth = Random.Range(minCorridorLength, maxCorridorLength);
                    roomHeight = 3;
                    break;
                case "East":
                    roomWidth = Random.Range(minCorridorLength, maxCorridorLength);
                    roomHeight = 3;
                    break;

            }
        }

        int xStartingPoint;
        int yStartingPoint;

        if (isFirst) {
            xStartingPoint = mapWidth / 2;
            yStartingPoint = mapHeight / 2;
        }
        else {
            int id;
            if (wall.positions.Count == 3) id = 1;
            else id = Random.Range(1, wall.positions.Count - 2);

            xStartingPoint = wall.positions[id].x;
            yStartingPoint = wall.positions[id].y;
        }

        Vector2Int lastWallPosition = new Vector2Int(xStartingPoint, yStartingPoint);

        if (isFirst) {
            xStartingPoint -= Random.Range(1, roomWidth);
            yStartingPoint -= Random.Range(1, roomHeight);
        }
        else {
            switch (wall.direction) {
                case "South":
                    if (type == "Room") xStartingPoint -= Random.Range(1, roomWidth - 2);
                    else xStartingPoint--;
                    yStartingPoint -= Random.Range(1, roomHeight - 2);
                    break;
                case "North":
                    if (type == "Room") xStartingPoint -= Random.Range(1, roomWidth - 2);
                    else xStartingPoint--;
                    yStartingPoint ++;
                    break;
                case "West":
                    xStartingPoint -= roomWidth;
                    if (type == "Room") yStartingPoint -= Random.Range(1, roomHeight - 2);
                    else yStartingPoint--;
                    break;
                case "East":
                    xStartingPoint++;
                    if (type == "Room") yStartingPoint -= Random.Range(1, roomHeight - 2);
                    else yStartingPoint--;
                    break;
            }
        }

         if (!CheckIfHasSpace(new Vector2Int(xStartingPoint, yStartingPoint), new Vector2Int(xStartingPoint + roomWidth - 1, yStartingPoint + roomHeight - 1))) {
            return;
        }

        room.walls = new Wall[4];

        for (int i = 0; i < room.walls.Length; i++) {
            room.walls[i] = new Wall();
            room.walls[i].positions = new List<Vector2Int>();
            room.walls[i].length = 0;

            switch (i) {
                case 0:
                    room.walls[i].direction = "South";
                    break;
                case 1:
                    room.walls[i].direction = "North";
                    break;
                case 2:
                    room.walls[i].direction = "West";
                    break;
                case 3:
                    room.walls[i].direction = "East";
                    break;
            }
        }

        for (int y = 0; y < roomHeight; y++) {
            for (int x = 0; x < roomWidth; x++) {
                Vector2Int position = new Vector2Int();
                position.x = xStartingPoint + x;
                position.y = yStartingPoint + y;

                room.positions.Add(position);

                MapManager.map[position.x, position.y] = new Tile();
                MapManager.map[position.x, position.y].xPosition = position.x;
                MapManager.map[position.x, position.y].yPosition = position.y;

                if (y == 0) {
                    room.walls[0].positions.Add(position);
                    room.walls[0].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (y == (roomHeight - 1)) {
                    room.walls[1].positions.Add(position);
                    room.walls[1].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (x == 0) {
                    room.walls[2].positions.Add(position);
                    room.walls[2].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (x == (roomWidth - 1)) {
                    room.walls[3].positions.Add(position);
                    room.walls[3].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (MapManager.map[position.x, position.y].type != "Wall") {
                    MapManager.map[position.x, position.y].type = "Floor";
                }
            }
        }

        if (!isFirst) {
            MapManager.map[lastWallPosition.x, lastWallPosition.y].type = "Floor";
            switch (wall.direction) {
                case "South":
                    MapManager.map[lastWallPosition.x, lastWallPosition.y - 1].type = "Floor";
                    break;
                case "North":
                    MapManager.map[lastWallPosition.x, lastWallPosition.y + 1].type = "Floor";
                    break;
                case "West":
                    MapManager.map[lastWallPosition.x - 1, lastWallPosition.y].type = "Floor";
                    break;
                case "East":
                    MapManager.map[lastWallPosition.x + 1, lastWallPosition.y].type = "Floor";
                    break;
            }
        }

        room.width = roomWidth;
        room.height = roomHeight;
        room.type = type;
        allFeatures.Add(room);
        countFeatures++;
    }

    bool CheckIfHasSpace(Vector2Int start, Vector2Int end) {
        for (int y = start.y; y <= end.y; y++) {
            for (int x = start.x; x <= end.x; x++) {
                if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) return false;
                if (MapManager.map != null) return false;
            }
        }

        return true;
    }

    Wall ChoseWall(Feature feature) {
        for (int i = 0; i < 10; i++) {
            int id = Random.Range(0, 100) / 25;
            if (!feature.walls[id].hasFeature) {
                return feature.walls[id];
            }
        }
        return null;
    }

    void DrawMap(bool isASCII) {
        if (isASCII) {
            Text screen = GameObject.Find("ASCIITest").GetComponent<Text>();

            string asciiMap = "";

            for (int y = (mapHeight - 1); y >= 0; y--) {
                for (int x = 0; x < mapWidth; x++) {
                    if (MapManager.map[x, y] != null) {
                        switch (MapManager.map[x, y].type) {
                            case "Wall":
                                asciiMap += "#";
                                break;
                            case "Floor":
                                asciiMap += ".";
                                break;
                        }
                    }
                    else {
                        asciiMap += " ";
                    }

                    if (x == (mapWidth - 1)) {
                        asciiMap += "\n";
                    }
                }
            }

            screen.text = asciiMap;
        }
    }
}

All Articles