24-08-20, 09:33 PM
(آخر تعديل لهذه المشاركة : 24-08-20, 10:08 PM {2} بواسطة Anas Mahmoud.)
بسم الله الرحمن الرحيم
السلام عليكم ورحمة الله وبركاته اعضاء المنتدى الكرام
كان الاخ عبد الهادي بهاب بارك الله فيه وادام عليه نعمه قد طرح لعبة الذاكرة من تصميمه في هذا الموضوع
http://vb4arb.com/vb/showthread.php?tid=36191
ذلك جعلني افكر في ابسط طريقة يمكن عمل بها مثل هذه الالعاب البسيطة بدون تايمر او تكرار ، لذلك قمت بكتابتها بسرعة وانا مبهور من انها عملت فعلا
اللعبة موجودة في المرفقات ، والان الى الشرح ، يمكنك المغادرة الان وان كنت افضل ان تكمل الشرح لتفهم اسلوب العمل
شرح اسلوب العمل
قبل ان نبدأ في شرح الكود نحتاج ان نفهم ما ما المشاكل التي تنطوي عليها اللعبة وكيف نقوم بحلها ، حتى نعرف الاجرائات التي سنقوم باستدعائها عند الحاجة لتلافي التكرار ولتبسيط الامر :
الخطوة الاولى : نريد طريقة امنة لتخزين الصور بحيث يمكننا الحصول عليها عند الحاجة بسهولة
اخترت ان اخزن الصور في المرفقات حيث انها تكون متضمنه في exe المشروع ولن اقلق حيال استدعائها داخل البرنامج
بعد ذلك نحصل على الصور عن طريق My.Resources._1 هكذا ،
واذا قمنا باستيراد مجال الاسماء يصبح بامكاننا استدعاء الصور باسمائها مباشرة :
الان لتسهيل التعامل مع الصور طوال عمل البرنامج قمت بتعريف List(Of Image) تحتوي الصور من الريسورسز ، تعريف عام للبرنامج كله :
الخطوة الثانية : تصميم شكل اللعبة
نريد طريقة سريعة وعملية لرص مربعات الصور بجانب بعضهم البعض
قمت باضافة الاداه TableLayoutPanel1 لكي اضع بها الصور ، واضفت بها 4 صفوف و4 اعمدة ،
وبداخلها اضفت 16 مربع صور ، ثم جعلت الخاصية Dock لكل مربعات الصور = Fill :
طيب الان نريد ان نضع كل مربعات الصور في List(of PictureBox) حتى نتمكن من التعامل معها بسهولة لاحقا ، قمت بتعريف التالي في التعريفات العامة :
طيب الان كيف نملأ هذه ال List بمربعات الصور فعلا ؟ هل نقوم باضافتها ام ان هناك فكرة افضل ؟
في الحقيقة لا اطيق التكرار ، وعندي قناعة ان هناك دائما طريقة ابسط لفعل الاشياء معظم الوقت
بما اننا وضعنا مربعات الصور داخل TableLayoutPanel1 فانه يمكننا بسهولة ملئ ال list الخاصة بالصور من الادوات التي تحتويها عن طريق الدالة OfType
الكود التالي ضعه في مشيد الفورم Sub New () :
لاحظ ToList السحرية التي تعيد نسخة جديدة من القائمة الى قائمتنا
الخطوة الثالثة : تغيير ترتيب الصور عشوائيا في مربعات الصور
سنقوم بتعريف الدالة RandomizePictureBox التي تقوم باسناد الصور الى مربعات صور عشوائية
السيناريو كالتالي : نعرف List بمربعات الصور التي لم يتم اسناد لها صورة بحيث تأخد نسخة اولية من List مربعات الصور
لكل صورة في List الصور نفعل ما يلي :
نكرر العملية من 1 الى 3 مرة اخرى ( كل صورة موجودة في مربعين صور عشوائيين )
الخطوة الرابعة : تجهيز لعبة جديدة:
نعرف اجراء لبدأ لعبة جديدة
تقوم بترتيب الصور عشوائيا في مربعات الصور
طيب الان نريد طريقة نعرف بها مربعات الصور التي لم يطابقها المستخدم بعد
نعرف List للصور المتبقية دون مطابقة ونقوم بملئها من List مربعات الصور
الخطوة الخامسة : بدأ اللعبة
نضيف زر جديد لبدأ اللعبة
طيب دعنا نفكر ماذا نحتاج ان نفعله لبدأ اللعبة ؟
نعرف الاجراء الذي يبدأ اللعبة :
في السطر الثالث نضيف الاجراء PictureBox_Click ( لم نعرفه بعد ) الى حدث Click لكل الصور
في السطر الثاني نستدعى الاجراء الخاص باخفاء كل الصور
الخطوة السادسة : الاستجابة للضغط على مربعات الصور
نقوم بتعريف الاجراء الذي اضفناه لحدث الضغط على كل الصور PictureBox_Click وهو عصب اللعبة الاساسي :
طيب الان المستخدم ضغط على الصورة ، نريد ان نعرف اي صورة ضغط عليها ومن ثم نعطل حدث الضغط لها حتى لايضغط عليها مرة اخرى ( اجراء احترازي )
بعد ذلك نريد ان نظهر الصورة التي يحملها مربع الصورة ( تذكر ان ترتيبها موجود في الخاصية tag ) :
طيب الان كيف نعرف هل الصورة التي ضغط عليها المستخدم هي اول صورة ام ثاني صورة ؟
نعرف متغير عام خارج الاجراء LastPic يمثل اخر صورة تم الضغط عليها
الان نعود للدالة ،
اذا كانت المتغير LastPic لايشير الى شيء فهذا معناه انها اول صورة يتم الضغط عليها ، وعندها كل ما علينا فعله هو ان نضع الصورة التي ارسلت حدث الضغط في LastPic ومن ثم نقوم ب Return من الاجراء :
طيب اذا اكمل الحدث الكود بعد هذه النقطة فهذا معناه ان LastPic كانت تشير لمربع الصورة الذي تم ضغطه اولا ، وحينها تقوم بعمل متغير محلى يحمل مربع الصورة التي يشير لها LastPic ، ثم نقوم بجعل LastPic لايشير الا شيء حتى تتكرر العملية مجددا مع مربع صور جديد :
الان نقارن بين مربع الصورة الذي تم ضغطه اولا ومربع الصورة الحالي
اذا تساوي الخاصية tag لهما هذا يعني ان هناك تطابق ، نستدعي الاجراء Match ، سنعرفه لاحقا
والا هنا محاولة خاطئة
ننتظر 500 ملي ثانية ثم نستدعي الاجراء NotMatch ، سنعرفه لاحقا ايضا
الآن اذا كانت RemainingPictureBox فارغة هذا معناه اننا انهينا اللعبة ، نستدعي الاجراء Finished
الخطوة السابعة : ماذا نفعل في حالة حدوث تطابق او محاولة خاطئة او انتهاء اللعبة :
الاجراء Match : نمرر له مربعين صورة يمثل الصورة الاولى والثانية ، عند حدوث تطابق نريد ان نزيل المربعين من RemainingPictureBox ،
وثم نضيف ما نريد من اكواد لزيادة متغير المحاولات الصحيحة مثلا او اظهار صوت او شيء نريده عند حدوث تطابق :
الاجراء NotMatch : نمرر له مربعين صورة يمثل الصورة الاولى والثانية ، عند حدوث محاولة خاطئة نريد ان نضع الصورة unknown في مربعين الصورة الاولى والثانية ، وكذلك نضيف الاجراء الى حدث الضغط لهما ليعود كل شيء كما كان
وثم ايضا نضيف مانريد فعله عند حدوث محاولة خاطئة :
الاجراء Finished : اخيرا المستخدم طابق كل الصور ، نظهر له رسالة تخبره بانه قد اتم اللعبة بنجاح ،
ثم نبدأ لعبة جديدة عن طريق الاجراء Reset الذي عرفناه
وبعد ذلك نعيد تفعيل زر بدأ اللعبة
الخطوة الأخيرة : تعريف حدث Load للفورم وحدث Click على زر بدأ اللعب:
في حدث Form1_Load : نقوم ببدأ لعبة جديدة Reset :
في حدث Button1_Click : نقوم ببدأ اللعب Start :
هذا كل شيء تقريبا
اتمنى اكون قد وصلت الفكرة والهدف وراء كل ذلك هو التفكير المنطقى وتقسيم المشكلة الكبيرة الى مشاكل صغيرة وحل كل جزئية على حده ومن ثم تجميع الاجزاء لبناء برنامج قوي، والاهم هو عدم كتابة نفس الكود اكثر من مرة في اكثر من مكان ، وذلك لتسهيل تتبع الكود بعد ذلك وتسهيل التعديل عليه
اتمنى التوفيق للجميع
السلام عليكم ورحمة الله وبركاته اعضاء المنتدى الكرام
كان الاخ عبد الهادي بهاب بارك الله فيه وادام عليه نعمه قد طرح لعبة الذاكرة من تصميمه في هذا الموضوع
http://vb4arb.com/vb/showthread.php?tid=36191
ذلك جعلني افكر في ابسط طريقة يمكن عمل بها مثل هذه الالعاب البسيطة بدون تايمر او تكرار ، لذلك قمت بكتابتها بسرعة وانا مبهور من انها عملت فعلا
اللعبة موجودة في المرفقات ، والان الى الشرح ، يمكنك المغادرة الان وان كنت افضل ان تكمل الشرح لتفهم اسلوب العمل
شرح اسلوب العمل
قبل ان نبدأ في شرح الكود نحتاج ان نفهم ما ما المشاكل التي تنطوي عليها اللعبة وكيف نقوم بحلها ، حتى نعرف الاجرائات التي سنقوم باستدعائها عند الحاجة لتلافي التكرار ولتبسيط الامر :
الخطوة الاولى : نريد طريقة امنة لتخزين الصور بحيث يمكننا الحصول عليها عند الحاجة بسهولة
اخترت ان اخزن الصور في المرفقات حيث انها تكون متضمنه في exe المشروع ولن اقلق حيال استدعائها داخل البرنامج
بعد ذلك نحصل على الصور عن طريق My.Resources._1 هكذا ،
واذا قمنا باستيراد مجال الاسماء يصبح بامكاننا استدعاء الصور باسمائها مباشرة :
كود :
Imports MemoryGame.My.Resources
الان لتسهيل التعامل مع الصور طوال عمل البرنامج قمت بتعريف List(Of Image) تحتوي الصور من الريسورسز ، تعريف عام للبرنامج كله :
كود :
Dim ImgList As New List(Of Image)({_1, _2, _3, _4, _5, _6, _7, _8})
الخطوة الثانية : تصميم شكل اللعبة
نريد طريقة سريعة وعملية لرص مربعات الصور بجانب بعضهم البعض
قمت باضافة الاداه TableLayoutPanel1 لكي اضع بها الصور ، واضفت بها 4 صفوف و4 اعمدة ،
وبداخلها اضفت 16 مربع صور ، ثم جعلت الخاصية Dock لكل مربعات الصور = Fill :
طيب الان نريد ان نضع كل مربعات الصور في List(of PictureBox) حتى نتمكن من التعامل معها بسهولة لاحقا ، قمت بتعريف التالي في التعريفات العامة :
كود :
Dim PictureBoxList As List(Of PictureBox)
طيب الان كيف نملأ هذه ال List بمربعات الصور فعلا ؟ هل نقوم باضافتها ام ان هناك فكرة افضل ؟
في الحقيقة لا اطيق التكرار ، وعندي قناعة ان هناك دائما طريقة ابسط لفعل الاشياء معظم الوقت
بما اننا وضعنا مربعات الصور داخل TableLayoutPanel1 فانه يمكننا بسهولة ملئ ال list الخاصة بالصور من الادوات التي تحتويها عن طريق الدالة OfType
الكود التالي ضعه في مشيد الفورم Sub New () :
كود :
PictureBoxList = Me.TableLayoutPanel1.Controls.OfType(Of PictureBox).ToList
الخطوة الثالثة : تغيير ترتيب الصور عشوائيا في مربعات الصور
سنقوم بتعريف الدالة RandomizePictureBox التي تقوم باسناد الصور الى مربعات صور عشوائية
السيناريو كالتالي : نعرف List بمربعات الصور التي لم يتم اسناد لها صورة بحيث تأخد نسخة اولية من List مربعات الصور
لكل صورة في List الصور نفعل ما يلي :
- نختار index عشوائي من List مربعات الصور عن طريق rnd.Next
- نضع الصورة الحالية في هذا المربع ، وكذلك ترتيب الصورة في ال Tag حتى نتمكن من مقارنتها لاحقا
- نمسح مربع الصور من List بمربعات الصور الفارغة
كود :
Sub RandomizePictureBox()
Dim Remainig As List(Of PictureBox) = PictureBoxList.ToList
Dim rnd As New Random()
Dim PicIndex As Integer
For i As Integer = 0 To ImgList.Count - 1
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)
Next
End Sub
نكرر العملية من 1 الى 3 مرة اخرى ( كل صورة موجودة في مربعين صور عشوائيين )
الخطوة الرابعة : تجهيز لعبة جديدة:
نعرف اجراء لبدأ لعبة جديدة
كود :
Dim RemainingPictureBox As List(Of PictureBox)
Sub Reset()
RandomizePictureBox()
RemainingPictureBox = PictureBoxList.ToList
End Sub
تقوم بترتيب الصور عشوائيا في مربعات الصور
طيب الان نريد طريقة نعرف بها مربعات الصور التي لم يطابقها المستخدم بعد
نعرف List للصور المتبقية دون مطابقة ونقوم بملئها من List مربعات الصور
الخطوة الخامسة : بدأ اللعبة
نضيف زر جديد لبدأ اللعبة
طيب دعنا نفكر ماذا نحتاج ان نفعله لبدأ اللعبة ؟
- اولا ان نعطل الزر الذي يبدأ اللعبة حتى لا يتم ضغطه مرة اخرى
- اخفاء جميع الصور من مربعات الصور
- انتظار ام يضغط المستخدم على مربعات الصور
نعرف الاجراء الذي يبدأ اللعبة :
كود :
Sub Start()
Button1.Enabled = False
HideAllPics()
PictureBoxList.ForEach(Sub(x) AddHandler x.Click, AddressOf PictureBox_Click)
End Sub
في السطر الثالث نضيف الاجراء PictureBox_Click ( لم نعرفه بعد ) الى حدث Click لكل الصور
في السطر الثاني نستدعى الاجراء الخاص باخفاء كل الصور
كود :
Sub HideAllPics()
PictureBoxList.ForEach(Sub(x) x.Image = unknown)
End Sub
الخطوة السادسة : الاستجابة للضغط على مربعات الصور
نقوم بتعريف الاجراء الذي اضفناه لحدث الضغط على كل الصور PictureBox_Click وهو عصب اللعبة الاساسي :
كود :
Private Async Sub PictureBox_Click(sender As Object, e As EventArgs)
End Sub
طيب الان المستخدم ضغط على الصورة ، نريد ان نعرف اي صورة ضغط عليها ومن ثم نعطل حدث الضغط لها حتى لايضغط عليها مرة اخرى ( اجراء احترازي )
كود :
Dim Pic As PictureBox = DirectCast(sender, PictureBox)
RemoveHandler Pic.Click, AddressOf PictureBox_Click
بعد ذلك نريد ان نظهر الصورة التي يحملها مربع الصورة ( تذكر ان ترتيبها موجود في الخاصية tag ) :
كود :
Pic.Image = ImgList(Pic.Tag)
طيب الان كيف نعرف هل الصورة التي ضغط عليها المستخدم هي اول صورة ام ثاني صورة ؟
نعرف متغير عام خارج الاجراء LastPic يمثل اخر صورة تم الضغط عليها
كود :
Dim LastPic As PictureBox
الان نعود للدالة ،
اذا كانت المتغير LastPic لايشير الى شيء فهذا معناه انها اول صورة يتم الضغط عليها ، وعندها كل ما علينا فعله هو ان نضع الصورة التي ارسلت حدث الضغط في LastPic ومن ثم نقوم ب Return من الاجراء :
كود :
If LastPic Is Nothing Then
LastPic = Pic
Return
End If
طيب اذا اكمل الحدث الكود بعد هذه النقطة فهذا معناه ان LastPic كانت تشير لمربع الصورة الذي تم ضغطه اولا ، وحينها تقوم بعمل متغير محلى يحمل مربع الصورة التي يشير لها LastPic ، ثم نقوم بجعل LastPic لايشير الا شيء حتى تتكرر العملية مجددا مع مربع صور جديد :
كود :
Dim lst As PictureBox = LastPic
LastPic = Nothing
الان نقارن بين مربع الصورة الذي تم ضغطه اولا ومربع الصورة الحالي
اذا تساوي الخاصية tag لهما هذا يعني ان هناك تطابق ، نستدعي الاجراء Match ، سنعرفه لاحقا
والا هنا محاولة خاطئة
ننتظر 500 ملي ثانية ثم نستدعي الاجراء NotMatch ، سنعرفه لاحقا ايضا
كود :
If Pic.Tag = lst.Tag Then
Match(Pic, lst)
Else
Await Task.Delay(500)
NotMatch(Pic, lst)
End If
الآن اذا كانت RemainingPictureBox فارغة هذا معناه اننا انهينا اللعبة ، نستدعي الاجراء Finished
كود :
If RemainingPictureBox.Count = 0 Then
Finished()
End If
الخطوة السابعة : ماذا نفعل في حالة حدوث تطابق او محاولة خاطئة او انتهاء اللعبة :
الاجراء Match : نمرر له مربعين صورة يمثل الصورة الاولى والثانية ، عند حدوث تطابق نريد ان نزيل المربعين من RemainingPictureBox ،
وثم نضيف ما نريد من اكواد لزيادة متغير المحاولات الصحيحة مثلا او اظهار صوت او شيء نريده عند حدوث تطابق :
كود :
Sub Match(CurrentPic As PictureBox, LastPic As PictureBox)
RemainingPictureBox.Remove(CurrentPic)
RemainingPictureBox.Remove(LastPic)
'كود عرض زيادة النتيجة الصحيحة بعد هذا السطر كسلت اكتبها
End Sub
الاجراء NotMatch : نمرر له مربعين صورة يمثل الصورة الاولى والثانية ، عند حدوث محاولة خاطئة نريد ان نضع الصورة unknown في مربعين الصورة الاولى والثانية ، وكذلك نضيف الاجراء الى حدث الضغط لهما ليعود كل شيء كما كان
وثم ايضا نضيف مانريد فعله عند حدوث محاولة خاطئة :
كود :
Sub NotMatch(CurrentPic As PictureBox, LastPic As PictureBox)
CurrentPic.Image = unknown
LastPic.Image = unknown
AddHandler CurrentPic.Click, AddressOf PictureBox_Click
AddHandler LastPic.Click, AddressOf PictureBox_Click
'كود عرض النتيجة خاطئة بعد هذا السطر ولكن كسلت اكتبها ايضا
End Sub
الاجراء Finished : اخيرا المستخدم طابق كل الصور ، نظهر له رسالة تخبره بانه قد اتم اللعبة بنجاح ،
ثم نبدأ لعبة جديدة عن طريق الاجراء Reset الذي عرفناه
وبعد ذلك نعيد تفعيل زر بدأ اللعبة
كود :
Sub Finished()
MsgBox("مبروك لقد اتممت اللعبة بنجاح")
Reset()
Button1.Enabled = True
End Sub
الخطوة الأخيرة : تعريف حدث Load للفورم وحدث Click على زر بدأ اللعب:
في حدث Form1_Load : نقوم ببدأ لعبة جديدة Reset :
كود :
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Reset()
End Sub
في حدث Button1_Click : نقوم ببدأ اللعب Start :
كود :
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Start()
End Sub
هذا كل شيء تقريبا
اتمنى اكون قد وصلت الفكرة والهدف وراء كل ذلك هو التفكير المنطقى وتقسيم المشكلة الكبيرة الى مشاكل صغيرة وحل كل جزئية على حده ومن ثم تجميع الاجزاء لبناء برنامج قوي، والاهم هو عدم كتابة نفس الكود اكثر من مرة في اكثر من مكان ، وذلك لتسهيل تتبع الكود بعد ذلك وتسهيل التعديل عليه
اتمنى التوفيق للجميع