الشبكة العربية لمطوري الألعاب

موهوب  ياسر أبوبكر مشاركة 1

التعريف :
     وحدة تنقل بين أجزاء اللعبة :  
       - تعريف واجهة تنحدر منها أجزاء اللعبة المختلفة (IGameState) تقدم إجراءات لحوسبة اللقطة القادمة ورسمها.
       - تحديد تدفق الانتقال بين الأجزاء المختلفة.
 

ماهي تفاصيل هذه المهمة؟

خبير مدير وسام البهنسي مشاركة 2

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

public class Game
{
  bool InMenu;   // هل البرنامج في القائمة الرئيسة؟
  int MenuSelection; // الخيار الحالي في القائمة الرئيسة
  int PlayerX;   // إحداثي البطل على المحور السيني
  int PlayerY;   // إحداثي البطل على المحور الصادي
  int PlayerLife;  // طاقة البطل المتبقية
 
  // هذا الإجراء يتم نداؤه في كل لقطة لمعالجة اللعبة
  public void Game_Update()
  {
     if (InMenu)
     {
        if (KeyDown(Keys.Up))
           MenuSelection = MenuSelection + 1;
        if (KeyDown(Keys.Down))
           MenuSelection = MenuSelection - 1;
 
        if (KeyDown(Keys.Space))
        {
           if (MenuSelection == 0)
              ExitGame();   // الخروج من البرنامج
           else InMenu = false; // توجه إلى اللعبة
        }

     }



     else // ليس في القائمة الرئيسية، إذن نحن في اللعبة
     {
        if (KeyDown(Keys.Left))
           PlayerX = PlayerX - 1;
        if (KeyDown(Keys.Right))
           PlayerX = PlayerX + 1;
        .
        .
        .
        // كود كشف التصادم وتجميع الجواهر
        .
        .
        // في النهاية
        if (PlayerLife == 0)
           InMenu = true; // مات البطل. عد إلى القائمة الرئيسية
     }

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



public enum States
{
   ContinueSameState,
   MainMenu,
   Gameplay,
   Exit,
}
 
// هذا الصنف يمثل القائمة الرئيسية
public class MenuState : State
{
   int MenuSelection; // الخيار الحالي في القائمة الرئيسة
 
   public override States GameUpdate()
   {
      if (KeyDown(Keys.Up))
         MenuSelection = MenuSelection + 1;
      if (KeyDown(Keys.Down))
         MenuSelection = MenuSelection - 1;
 
      if (KeyDown(Keys.Space)) // ضغط زر المسافة، انتقل للحالة المناسبة
      {
         if (MenuSelection == 0)
            return States.Exit;   // الخروج من البرنامج
         else return States.Gameplay; // توجه إلى اللعبة
      }
      else return States.ContinueSameState; // وإلا استمر في نفس الحالة



   }
}
 
 
// هذا الصنف يمثل لعب مرحلة
public class GameplayState
{
   int PlayerX;   // إحداثي البطل على المحور السيني
   int PlayerY;   // إحداثي البطل على المحور الصادي
   int PlayerLife;  // طاقة البطل المتبقية
 
   public override States GameUpdate()
   {
      if (KeyDown(Keys.Left))
         PlayerX = PlayerX - 1;
      if (KeyDown(Keys.Right))
         PlayerX = PlayerX + 1;
      .
      .
      .
      // كود كشف التصادم وتجميع الجواهر
      .
      .
      // في النهاية
      if (PlayerLife == 0)
         return States.MainMenu; // مات البطل. عد إلى القائمة الرئيسية

      else return States.ContinueSameState; // وإلا استمر في نفس الحالة
   }
}
 
 
لاحظ كيف أن الكود أصبح منظماً. فالمتغيرات الخاصة بالقائمة الرئيسية انتقلت إلى الصنف الخاص بالقائمة الرئيسية. ومتغيرات اللعبة والبطل نقلت إلى صنف اللعبة. ثم أن منطق اللعبة لم يعد كله مجتمعاً في إجراء واحد كبير، وإنما تم تقسيمه في أصناف مستقلة.
 
هذا هو النظام الذي علينا أن نبنيه هنا في عبر السدم. بعد أن نبنيه، سنستطيع كتابة الكود لكل حالة من حالات اللعبة دون أن تتداخل فيما بينها مما يبسط المسألة ويتيح لأكثر من مبرمج أن يعملوا في المشروع بنفس الوقت.
 
 
لاحظ أن الكود أعلاه به بعض النواقص. فصحيح أننا نقلنا الكود إلى أصناف مختلفة، لكن من يشغلها؟ من يستدعيها ومن ينشئها؟ هذه هي وحدة التنقل التي عليك كتابتها يا ياسر.
 
كمثال محلول (لكنه مكتوب بلغة سي++)، انظر كود وادي الملوك:
 
http://www.agdn-online.com/source/kvalley2_src.zip
 
وبالتحديد الملفين المسميين StateFlow.h و StateFlow.cpp
 
 
أرجو أن يكون الشرح واضحاً، ولا تتردد بنقاش هذه الأفكار وطلب المزيد من التوضيح إن احتجت.
 
بالتوفيق

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

موهوب  ياسر أبوبكر مشاركة 3

أعتذر كثيرا عن التأخر في الرد لحدوث بعض الأمور لدي شغلتني عن هذا المنتدى الحبيب ...

بعد الإطلاع على مشاركة الأستاذ وسام ودراسة كود وادي الملوك توضحت الفكرة كثيرا وعرفت الغاية والمغزى من هذه الوحدة

وأريد أن أفتح باب النقاش والمزيد من التوضيح هنا

كان أو مافكرت فيه بعد قراءة كود وادي الملوك هو أن أحدد الحالات التي ستمر بها لعبتنا هذه وكانت هذه هي النتيجة :

1 - المقدمة
2 - القائمة الرئيسية
3 - قائمة الإعدادات
4 - قائمة أفضل اللعيبة
5 - فلم البداية
6 - اللعبة نفسها (حالة اللعب)
7 - إيقاف اللعبة
8 - حالة الـ ( Game Over )
9 - الإنتقال بين المراحل (ربما يكون فلم؟)
9.1 - إختيار المراحل ( إمكانية للمناقشة )
9.2 - قائمة لشراء الأسلحة (للمناقشة أيضا)
10 - قائمة الأسماء
11 - فلم النهاية

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

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

خبير مدير وسام البهنسي مشاركة 4

رائع. القائمة التي ذكرتها يا ياسر قريبة جداً من القائمة النهائية التي سنشهدها عند نهاية العمل إن شاء الله. كما تلاحظ، هناك العديد من الحالات في اللعبة، ولستَ أنت مكلفاً بكتابتها كلها بالطبع، وإنما مهمتك فقط كتابة الكود الذي سيتولى تشغيل هذه الحالات.
 
لهذه المهمة، أقترح أن تفعل الآتي كي تصبح الأمور واضحة وجلية وغير هلامية:
 
* أكتب أول محاولة لكلاس الـ StateFlow بشكل يقابل ذلك في كود وادي الملوك. دع المزايا المتقدمة مبدئياً (كتأثير الانتقال transition). فقط ركز على التنقل.
 
* اكتب ثلاثة حالات تجريبية: الحالة الأولى تظهر شاشة حمراء حتى يضغط المستخدم زر المسافة. الحالة الثانية تفعل نفس الشيء لكن الشاشة زرقاء. والثالثة خضراء.
 
* اجعل سير التنقل كالآتي: تشغيل اللعبة > شاشة حمراء > شاشة خضراء > شاشة زرقاء > إنهاء البرنامج
 
لهذه العملية، سيحتوي إجراء الـ Update الخاص بكل حالة فقط على كود التحقق من ضغط زر المسافة، وإن كان مضغوطاً فإن الحالة تطلب الانتقال للحالة التي تليها. أما كود إجراء الـ Render فسيحوي فقط أمر وحيد، وهو مسح محتويات الشاشة باللون الذي تحدده:


GraphicsDevice.Clear(Color.Blue);
 
لا تقلق نفسك بالحالات الأخرى. فقط اصنع الحالات الثلاث التجريبية.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير  أحمد عزالدين مشاركة 5

السلام عليكم

كيف حال الجميع 😄
أخيراً عدت بعد انقطاع لعدة أسابيع بسبب ظروف خاصة

ولقد قمت بانهاء مهمة "وحدة التنقل بين أجزاء اللعبة" بشكل مبدئي كما شرح الاخ وسام
وهذا بالطبع بعد اذن الاخ ياسر ، فلعله في انشغال هذه الايام

يمكنكم الان سحب اخر التعديلات من مخزن الكود مباشرة كما ذكر الاخ وسام في مدونته
-------- يبدو أن المشروع بدأ يدخل في العميق --------
سنكون دوماً في انتظار المهمات الجديدة☺ 😄

وفي انتظار مشاركات الجميع
والسلام عليكم

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

موهوب  ياسر أبوبكر مشاركة 6

وعليكم السلام أخ أحمد وأهلا وسهلا بعودتك

وفي 29 يونيو 2010 12:08 ص، أعرب ahmed ezz عن رأيه بالموقف كالآتي:

ولقد قمت بانهاء مهمة "وحدة التنقل بين أجزاء اللعبة" بشكل مبدئي كما شرح الاخ وساموهذا بالطبع بعد اذن الاخ ياسر ، فلعله في انشغال هذه الايام

الحقيقة شكرا جزيلا لك ولكن لكي أكون صريحا فلم أذن لك بذلك ...

ولكن لن ألومك فبالفعل تأخرت كثيرا في تنفيذ هذه المهمة ولإدارة المنتدى الحق فإتخاذ أي إجراء مناسب ... ولكن البرمجة بإستخدام مكتبة Xna لم تكن بهذه السهولة التي توقعتها ربما بعض الأمور التي وقفت عندها قد تكون بديهة (أو ربما سخيفة حتى) عند البعض أو سهلة عند البعض الأخر ... ولكن بالنسبة لي فقد كانت كالجبال فهناك الكثير من الإجراءات التي لم أفهم سبب وجودها ناهيك عن الطريقة التي يعمل بها برنامج اللعبة ككل ... فتوقفت عن هذه المهمة وبدأت لدي مهمة جديدة في التعرف على أبجديات مكتبة الـ XNA أولا فهذه هي المرة الأولى التي أعمل عليها ... ولكن ذلك أخذ وقت أطول من اللازم وهذا خطأي بالطبع لأني لم أستغل وقتي بشكل فعال ...

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

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

شكراً

خبير  أحمد عزالدين مشاركة 7

السلام عليكم

كيف حالك أخي ياسر؟
شكراً لك ، وأعتذر عن قيامي بتنفيذ المهمة المسندة اليك 🙁
وأود أنوه أنني قمت بذلك بعد أخذ الاذن من الادارة

بخصوص بعض المشاكل التي تواجهك في XNA فأرجوا أن تطرح ما يصعب عليك التعامل معه فيها ، وسنكون سعيدين لان نتعلم جميعاً ونساعد بعضنا البعض

وفي 29 يونيو 2010 08:27 م، قال ياسر أبوبكر متحمساً:

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

أوافق معك أن تقوم الادارة بتحديد مجموعة من القواعد للمشاركين ، بحيث مثلا أنه ان تم اسناد مهمة لشخص ما ، وظهر فيما بعد انشغاله لاي سبب اخر ، فعليه أن يقوم بابلاغ الادارة بحالته ان كان سيستطيع اكمال المهمة وأنه قد يتأخر قليلا ، أم أنه مشغول ويجب على الادارة أن تقوم بتحويل المهمة لشخص اخر

وأعتقد أن الادارة ستقوم بالاعلان عن مثل هذه القواعد ، وسنكون في انتظار اقتراحاتك بخصوص المشروع ان شاء الله 😄

والسلام عليكم

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

خبير مدير وسام البهنسي مشاركة 8

أحمد، هل تستطيع إعادة تسمية المتغيرات الخاصة بوحدة التنقل لتصبح تتبع نظام تسمية camelCase المتبع في كل المشروع بدلاً من النظام الهنجاري المتبع في وادي الملوك؟
 
شكراً

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

خبير  أحمد عزالدين مشاركة 9

السلام عليكم

تم تعديل أسماء المتغيرات كما اقترح الاخ وسام
- أيضا قمت بتعديل وتنظيم بعض كود وحدة التنقل بين حالات اللعبة لتقوم بتحميل نموذج الانبوب عند الحاجة وليس في بداية عمل اللعبة كمان كان مسبقاً

في انتظار الخطوة القادمة ان شاء الله☺ 😄

أحمد عزالدين
طالب دراسات عليا
جامعة كالجري

خبير مدير وسام البهنسي مشاركة 10

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

وسام البهنسي
مبرمج في إنفيديا وإنفريمز