تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
[مشروع] لعبة الذاكرة مبسطة ( مع الشرح )
#1
بسم الله الرحمن الرحيم

السلام عليكم ورحمة الله وبركاته اعضاء المنتدى الكرام

كان الاخ عبد الهادي بهاب بارك الله فيه وادام عليه نعمه قد طرح لعبة الذاكرة من تصميمه في هذا الموضوع

http://vb4arb.com/vb/showthread.php?tid=36191

ذلك جعلني افكر في ابسط طريقة يمكن عمل بها مثل هذه الالعاب البسيطة بدون تايمر او تكرار ، لذلك قمت بكتابتها بسرعة وانا مبهور من انها عملت فعلا  Big Grin 


   
   
   
   


اللعبة موجودة في المرفقات ، والان الى الشرح ، يمكنك المغادرة الان وان كنت افضل ان تكمل الشرح لتفهم اسلوب العمل

شرح اسلوب العمل

قبل ان نبدأ في شرح الكود نحتاج ان نفهم ما ما المشاكل التي تنطوي عليها اللعبة وكيف نقوم بحلها ، حتى نعرف الاجرائات التي سنقوم باستدعائها عند الحاجة لتلافي التكرار ولتبسيط الامر :



الخطوة الاولى : نريد طريقة امنة لتخزين الصور بحيث يمكننا الحصول عليها عند الحاجة بسهولة



اخترت ان اخزن الصور في المرفقات حيث انها تكون متضمنه في exe المشروع ولن اقلق حيال استدعائها داخل البرنامج 


   

بعد ذلك نحصل على الصور عن طريق Properties.Resources._1 هكذا ،
واذا قمنا باستيراد مجال الاسماء يصبح بامكاننا استدعاء الصور باسمائها :


كود :
using MemoryGameCS.Properties;


الان لتسهيل التعامل مع الصور طوال عمل البرنامج قمت بتعريف List<Bitmap> تحتوي الصور من الريسورسز ، تعريف عام للبرنامج كله :


كود :
       List<Bitmap> ImgList = new List<Bitmap>
   {
       Resources._1,
       Resources._2,
       Resources._3,
       Resources._4,
       Resources._5,
       Resources._6,
       Resources._7,
       Resources._8
   };


الخطوة الثانية : تصميم شكل اللعبة


نريد طريقة سريعة وعملية لرص مربعات الصور بجانب بعضهم البعض
قمت باضافة الاداه TableLayoutPanel1 لكي اضع بها الصور ، واضفت بها 4 صفوف و4 اعمدة ،
وبداخلها اضفت 16 مربع صور ، ثم جعلت الخاصية Dock لكل مربعات الصور = Fill :


   

طيب الان نريد ان نضع كل مربعات الصور في List(of PictureBox) حتى نتمكن من التعامل معها بسهولة لاحقا ، قمت بتعريف التالي في التعريفات العامة :


كود :
       List<PictureBox> PictureBoxList;


طيب الان كيف نملأ هذه ال List بمربعات الصور فعلا ؟ هل نقوم باضافتها ام ان هناك فكرة افضل ؟ 

في الحقيقة لا اطيق التكرار ، وعندي قناعة ان هناك دائما طريقة ابسط لفعل الاشياء معظم الوقت 

بما اننا وضعنا مربعات الصور داخل TableLayoutPanel1 فانه يمكننا بسهولة ملئ ال list الخاصة بالصور من الادوات التي تحتويها عن طريق الدالة OfType

الكود التالي ضعه في مشيد الفورم         public Form1() :

كود :
           PictureBoxList = TableLayoutPanel1.Controls.OfType<PictureBox>().ToList();


لاحظ ToList السحرية التي تعيد نسخة جديدة من القائمة الى قائمتنا 


الخطوة الثالثة : تغيير ترتيب الصور عشوائيا في مربعات الصور


سنقوم بتعريف الدالة RandomizePictureBox التي تقوم باسناد الصور الى مربعات صور عشوائية 

السيناريو كالتالي : نعرف List بمربعات الصور التي لم يتم اسناد لها صورة بحيث تأخد نسخة اولية من List مربعات الصور
لكل صورة في List الصور نفعل ما يلي :

  1. نختار index عشوائي من List مربعات الصور عن طريق rnd.Next
  2. نضع الصورة الحالية في هذا المربع ، وكذلك ترتيب الصورة في ال Tag حتى نتمكن من مقارنتها لاحقا
  3. نمسح مربع الصور من List بمربعات الصور الفارغة 

كود :
       public void RandomizePictureBox()
       {
           List<PictureBox> Remainig = PictureBoxList.ToList();
           Random rnd = new Random();
           int PicIndex;
           for (int i = 0; i <= ImgList.Count - 1; i++)
           {
               PicIndex = rnd.Next(0, Remainig.Count - 1);
               Remainig[PicIndex].Tag = i;
               Remainig[PicIndex].Image = ImgList[i];

               Remainig.RemoveAt(PicIndex);

               PicIndex = rnd.Next(0, Remainig.Count - 1);
               Remainig[PicIndex].Tag = i;
               Remainig[PicIndex].Image = ImgList[i];

               Remainig.RemoveAt(PicIndex);
           }
       }


نكرر العملية من 1 الى 3 مرة اخرى ( كل صورة موجودة في مربعين صور عشوائيين )





الخطوة الرابعة : تجهيز لعبة جديدة:


 نعرف اجراء لبدأ لعبة جديدة

كود :
        List<PictureBox> RemainingPictureBox;

       public void Reset()
       {
           RandomizePictureBox();
           RemainingPictureBox = PictureBoxList.ToList();
       }


تقوم بترتيب الصور عشوائيا في مربعات الصور
طيب الان نريد طريقة نعرف بها مربعات الصور التي لم يطابقها المستخدم بعد 
نعرف List للصور المتبقية دون مطابقة ونقوم بملئها من List مربعات الصور


الخطوة الخامسة : بدأ اللعبة


نضيف زر جديد لبدأ اللعبة

طيب دعنا نفكر ماذا نحتاج ان نفعله لبدأ اللعبة ؟

  1. اولا ان نعطل الزر الذي يبدأ اللعبة حتى لا يتم ضغطه مرة اخرى
  2. اخفاء جميع الصور من مربعات الصور
  3. انتظار ام يضغط المستخدم على مربعات الصور
 
نعرف الاجراء الذي يبدأ اللعبة :


كود :
       void Start()
       {
           Button1.Enabled = false;
           PictureBoxList.ForEach(x => x.Click += PictureBox_Click);
           HideAllPics();
       }


في السطر الثالث نضيف الاجراء PictureBox_Click ( لم نعرفه بعد ) الى حدث Click لكل الصور 
في السطر الثاني نستدعى الاجراء الخاص باخفاء كل الصور 


كود :
       void HideAllPics()
       {
           PictureBoxList.ForEach(x => x.Image = Resources.unknown);
       }



الخطوة السادسة : الاستجابة للضغط على مربعات الصور


نقوم بتعريف الاجراء الذي اضفناه لحدث الضغط على كل الصور PictureBox_Click وهو عصب اللعبة الاساسي :


كود :
       private async void PictureBox_Click(object sender, EventArgs e)
       { }


طيب الان المستخدم ضغط على الصورة ، نريد ان نعرف اي صورة ضغط عليها ومن ثم نعطل حدث الضغط لها حتى لايضغط عليها مرة اخرى  ( اجراء احترازي )

كود :
           PictureBox Pic = (PictureBox)sender;

           Pic.Click -= PictureBox_Click;


بعد ذلك نريد ان نظهر الصورة التي يحملها مربع الصورة ( تذكر ان ترتيبها موجود في الخاصية tag ) :


كود :
           Pic.Image = ImgList[(int)Pic.Tag];


طيب الان كيف نعرف هل الصورة التي ضغط عليها المستخدم هي اول صورة ام ثاني صورة ؟ 
نعرف متغير عام خارج الاجراء LastPic يمثل اخر صورة تم الضغط عليها 

كود :
       PictureBox LastPic;


الان  نعود للدالة ،
اذا كانت المتغير LastPic لايشير الى شيء فهذا معناه انها اول صورة يتم الضغط عليها ، وعندها كل ما علينا فعله هو ان نضع الصورة التي ارسلت حدث الضغط في LastPic ومن ثم نقوم ب Return من الاجراء :

كود :
           if (LastPic == null)
           {
               LastPic = Pic;
               return;
           }


طيب اذا اكمل الحدث الكود بعد هذه النقطة فهذا معناه ان LastPic كانت تشير لمربع الصورة الذي تم ضغطه اولا ، وحينها تقوم بعمل متغير محلى يحمل مربع الصورة التي يشير لها LastPic ، ثم نقوم بجعل LastPic لايشير الا شيء حتى تتكرر العملية مجددا مع مربع صور جديد :


كود :
           PictureBox lst = LastPic;
           LastPic = null;


الان نقارن بين مربع الصورة الذي تم ضغطه اولا ومربع الصورة الحالي 
اذا تساوي الخاصية tag لهما هذا يعني ان هناك  تطابق ، نستدعي الاجراء Match ، سنعرفه لاحقا

والا هنا محاولة خاطئة
ننتظر 500 ملي ثانية ثم نستدعي الاجراء NotMatch ، سنعرفه لاحقا ايضا


كود :
           if ((int)(Pic.Tag) == (int)(lst.Tag))
               Match(Pic, lst);
           else
           {
               await Task.Delay(500);

               NotMatch(Pic, lst);
           }


الآن اذا كانت RemainingPictureBox فارغة هذا معناه اننا انهينا اللعبة ، نستدعي الاجراء Finished 


كود :
           if (RemainingPictureBox.Count == 0)
               Finished();


الخطوة السابعة : ماذا نفعل في حالة حدوث تطابق او محاولة خاطئة او انتهاء اللعبة :



الاجراء Match : نمرر له مربعين صورة يمثل الصورة الاولى والثانية ، عند حدوث تطابق نريد ان نزيل المربعين من RemainingPictureBox ، 

وثم نضيف ما نريد من اكواد لزيادة متغير المحاولات الصحيحة مثلا او اظهار صوت او شيء نريده عند حدوث تطابق :

كود :
       public void Match(PictureBox CurrentPic, PictureBox LastPic)
       {
           RemainingPictureBox.Remove(CurrentPic);
           RemainingPictureBox.Remove(LastPic);

           //كود عرض زيادة النتيجة الصحيحة بعد هذا السطر كسلت اكتبها

       }


الاجراء NotMatch : نمرر له مربعين صورة يمثل الصورة الاولى والثانية ، عند حدوث محاولة خاطئة نريد ان نضع الصورة unknown في مربعين الصورة الاولى والثانية ، وكذلك نضيف الاجراء الى حدث الضغط لهما ليعود كل شيء كما كان

وثم ايضا نضيف مانريد فعله عند حدوث محاولة خاطئة :


كود :
       public void NotMatch(PictureBox CurrentPic, PictureBox LastPic)
       {
           CurrentPic.Image = Resources.unknown;
           LastPic.Image = Resources.unknown;
           CurrentPic.Click += PictureBox_Click;
           LastPic.Click += PictureBox_Click;

           //كود عرض النتيجة خاطئة بعد هذا السطر ولكن كسلت اكتبها ايضا

       }



الاجراء Finished : اخيرا المستخدم طابق كل الصور ، نظهر له رسالة تخبره بانه قد اتم اللعبة بنجاح ، 

ثم نبدأ لعبة جديدة عن طريق الاجراء Reset الذي عرفناه

وبعد ذلك نعيد تفعيل زر بدأ اللعبة 


كود :
       public void Finished()
       {
           MessageBox.Show("مبروك لقد اتممت اللعبة بنجاح");
           Reset();
           Button1.Enabled = true;
       }


الخطوة الأخيرة : تعريف حدث Load للفورم وحدث Click على زر بدأ اللعب:


في حدث Form1_Load : نقوم ببدأ لعبة جديدة Reset :

كود :
       private void Form1_Load(object sender, EventArgs e)
       {
           Reset();
       }


في حدث Button1_Click : نقوم ببدأ اللعب Start :



كود :
       private void Button1_Click(object sender, EventArgs e)
       {
           Start();
       }


هذا كل شيء تقريبا  Big Grin 



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


اتمنى التوفيق للجميع


الملفات المرفقة
.zip   MemoryGameCS.zip (الحجم : 294.21 ك ب / التحميلات : 12)
الرد
#2
شكرا لك خي








Tutuapp 9apps Showbox
الرد
#3
عبقرى ومتمكن ماشاء الله عليك أخى أنس.....

ربنا يحفظك
الرد
تم الشكر بواسطة: asemshahen5 , asemshahen5 , ابراهيم ايبو , Anas Mahmoud
#4
(11-10-20, 02:05 PM)ابو روضة كتب :
عبقرى ومتمكن ماشاء الله عليك أخى أنس.....

ربنا يحفظك

ولك بمثله اخي
الرد
تم الشكر بواسطة:



التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم