صنع لعبة ماتش- 3 في المبنى 2: حركة الكتلة
Arabic (العربية/عربي) translation by Maryam Abbas (you can also view the original English article)
صنع لعبة ماتش- 3 في المبنى 2: حركة الكتلة
في الجزء السابق من هذه السلسلة، قدمنا بعض التغييرات الصغيرة ولكن الهامة على العديد من النظم التي أنشأناها لدينا من أجل اللعبة ماتش- 3. مع تنفيذ هذه التحسينات، فإننا الآن على وشك العودة إلى المسار الصحيح وتنفيذ أحد آخر اثنين من النظم الرئيسية للعبة: نظام حركة الكتلة.
سوف يأخذك هذا البرنامج التعليمي عبر تطوير كامل للنظام الذي يسمح للكتل بالارتفاع إلى أعلى الشاشة، وسوف يغطي أيضا إنشاء جميع النظم الصغيرة التي سوف تحتاج إلى تنفيذ لدعم نظام الحركة. وفي حين أن الموضوعات الأولى التي أغطيها في هذا البرنامج التعليمي ليست معقدة جدا، فهناك الكثير للمضي خلاله - لذلك دعونا نحصل عليه.
تجريب اللعبة النهائية
هنا تجريب للعبة التي نعمل من أجلها عبر هذه السلسلة:
1. تحريك الكتل للاعلى
قبل أن نبدأ بتحريك الكتل، علينا صنع تغيير طفيف على الأحداث التي تنتج الكتل. انتقل إلى النظام> على أبدأ بالتخطيط للحدث وغير Y ل
لحلقة للانتقال من 0
إلى 3
، بدلا من 0
إلى 7
كما هي أصلا.
يجب أن يكون الحدث الآن كما يلي:



السبب الذي جعلنا نقوم بهذا التغيير هو أننا نريد أن تبدأ اللعبة بكتل أقل على الشاشة، بحيث لا تنتهي بسرعة عندما نضيف نهاية اللعبة في البرنامج التعليمي المقبل.
بعد ذلك، سنقوم بإنشاء متغير يمثل سرعة الكتل:
Global Variable: Name = CurrentSpeed Type = Number Value = 0.2
الآن سنقوم بإنشاء الحدث الذي يحرك الكتل في الواقع:
Event: Condition: System>Every X Seconds Interval (seconds) = CurrentSpeed Action: Block>Move at Angle Angle = -90 Distance = 1
يجب أن يكون الحدث كما يلي:



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

كما ترون في جيف، كتل كشف المبادلة لا تتحرك، على الرغم من أنها نفس الكتل.
لحل كل من هاتين المشكلتين، سنضيف بعض الإجراءات الأخرى إلى الحدث الذي أنشأناه للتو:
Action: BottomBlock>Move at Angle Angle = -90 Distance = 1 Action: LeftBlock>Move at Angle Angle = -90 Distance = 1 Action: RightBlock>Move at Angle Angle = -90 Distance = 1 Action: TopBlock>Move at Angle Angle = -90 Distance = 1 Action: BlockPositions>Set at XY X = 0 Y = 1 Value = BlockPositions.At(0,1) - 1 Action: BlockPositions>Set at XY X = 1 Y = 1 Value = BlockPositions.At(1,1) - 1
يجب أن يبدو الحدث الآن كما يلي:



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



إذا فعلت ذلك بشكل صحيح، يجب أن ترى أن شيئا لم يحدث وأن الكتل لم تبادل. إذا انتظرت وقتا طويلا، قد يتم تبديل الكتل لأنها انتقلت فوق حدود مجال اللعبة مرة أخرى، لذلك إذا حدث هذا حاول مرة أخرى بمجرد سقوطها ويجب أن تشاهد هذه المشكلة تحدث.
حل وفهم هذه المسألة بسيط
إلى حد ما. إذا نظرتم إلى التعليمات البرمجية للمبادلات الهبوطية، يجب أن تجد
الحدث الذي أضفناه في البرنامج التعليمي السابق الذي يمنع اللاعب من إجراء مبادلات
الهبوط التي تسبب سقوط الكتلة أسفل مجال اللعبة. بما أن هذه العبارة تمنع اللاعب من إجراء مبادلات هبوطية عندما يكون كائن الكتلة السفلية
أدنى من الموضع Y المبدئي للكتلة، فإنه يمنع الكتل من أن يتم تبديلها بمجرد سقوطها، ويسمح لك فقط بإجراء المبادلات مرة أخرى بمجرد انتقالها إلى موضعها الأصلي مرة أخرى.
لتثبيت هذه العبارة سنقوم بإجراء تغيير واحد صغير في الحالة:
Condition: BottomBlock>Compare Y Comparison = Less or equal Y co-ordinate = SPAWNY + ((Block.Width + 2)/2)
يجب أن تبدو الحالة كما يلي:



هذا التعديل يعني أنه يمكن إجراء مبادلة هبوطية فقط عندما يكون كائن الكتلة السفلية
هوعلى الأكثر نصف الكتلة تحت الموقع Y الذي تبدأ الكتل فيه. وهذا يعني أيضا أنه بمجرد أن نبدأ بمبادلة صفوف جديدة من الكتل ودفعها على الشاشة من الاسفل، فهذه الكتل سوف تكون فقط قادرة على المبادلة بهذه الطريقة حالما يكون على الأقل نصف الكتلة مرئية.
سنقوم أيضا بوضع قيود مماثلة في جميع أحداث مبادلاتنا لضمان أن تصبح جميعها صالحة للاستعمال في نفس الوقت، وأنه لا يمكن تبديل كتلة على الإطلاق حتى يكون نصفها على الأقل مرئياً. مرة أخرى، هذا سوف يساعد أيضا عندما ندمج النظام الذي يؤدي إلى صفوف جديدة من الكتل. لإجراء ذلك، سنضيف شرطا جديدا إلى كل من المبادلات الثلاثة المتبقية.
الشروط التي نضيفها ستكون بالضبط نفس التي قمنا بتعديلها فقط في حدث الكتلة السفلية
، إلا أنها سوف تشير إلى كائنات الكتلة العلوية
، والكتلة اليمينية
، و الكتلة اليسارية
بدلا من الكائن الكتلة السفلية
، اعتمادا على الحدث الذي هي فيه.
يجب أن يكون الشرط الجديد لحدث الكتلة العلوية
:
Condition: TopBlock>Compare Y Comparison = Less or equal Y co-ordinate = SPAWNY + ((Block.Width + 2)/2)
يجب أن يكون الشرط الجديد لحدث الكتلة اليسارية
:
Condition: LeftBlock>Compare Y Comparison = Less or equal Y co-ordinate = SPAWNY + ((Block.Width + 2)/2)
يجب أن يكون الشرط الجديد لحدث الكتلة اليمينية
:
Condition: RightBlock>Compare Y Comparison = Less or equal Y co-ordinate = SPAWNY + ((Block.Width + 2)/2)
حدث السقوط الكامل لديك السحب والافلات يجب أن يبدو الآن مثل هذا:



مع هذه الحالات الجديدة في مكانها، قمنا بتثبيت آلية المبادلة لدينا وبدأنا في إعداد النظم القائمة للنظام التالي الذي أضفناه: النظام الذي سوف ينتج صفوف جديدة من الكتل.
3. انتاج المزيد من الكتل
الآن بعد أن أصبح لدينا كتل تتحرك صعودا بمعدل ثابت، فإننا بحاجة لجعل الصفوف الجديدة من الكتل تنتج في الوقت الصحيح، والسماح للاعب بمواصلة اللعب طالما يريدون ذلك. سوف نستخدم وظيفة لتنتج الصفوف الجديدة من الكتل، وسوف نستخدم الحدث الذي يكشف عندما تكون الكتل منسجمة مع الانتاجية
لإطلاق هذه الوظيفة.
لذلك أولا، دعونا نصنع الوظيفة نفسها.
Event: Condition: Function>On function Name = "SpawnNewBlocks" Condition: System>For Name = "X" Start Index = 0 End Index = 7 Action: System>Create object Object = Block Layer = 1 X = SPAWNX + (loopIndex("X"))*(Block.Width+2) Y = SPAWNY + (Block.Width+2) Action: Block>Set value Instance Variable = Color Value = floor(Random(1,7)) Action: System>Add to Variable = NumBlocks Value = 1
يجب أن يبدوحدثك الجديد كما يلي:



عند استخدامها، فإن هذه الوظيفة سوف تنشئ صف من الكتل تحت الصف السفلي من الكتل في مجال اللعبة. كما هو عليه الآن، على الرغم من أننا، فعليا لم نستخدم هذه الوظيفة في أي لحظة، لذلك دعونا نجعل الحدث يفعل ذلك:
Event: Condition: System>Every X seconds Interval(seconds) = CurrentSpeed Condition: Block>Compare Y Comparison = Equal to Y = SPAWNY Condition: Invert: Block>Is Dragging Action: Function>Call function Name = "SpawnNewBlocks"
يجب أن يبدو حدثك الجديد كما يلي:



الحدث الذي أنشأناه فقط يتحقق من موقع Y للكتل في كل مرة يتم نقلها. إذا وجد أي كتل منسجمة مع الانتاجية
، فإنه يطلق وظيفة انتاج كتل جديدة ()
كما ناقشنا في وقت سابق. إنه يتحقق أيضا للتأكد من أن الكتلة التي يجدها ليست تلك التي يجري جرها من قبل اللاعب.
إذا اختبرت اللعبة في هذه المرحلة، سوف تعمل، ولكن يجب أن تلاحظ قضية واحدة غريبة. لحظة بدء اللعبة، سوف تسقط كتلك كما لو لم يكن هناك كتل تحتها، ولكن بعد هذه النقطة كل شيء يعمل بشكل تام، وتنتج كتل جديدة كلما كانت هناك حاجة إليها.
يحدث ذلك لأنه عندما تبدأ اللعبة لأول مرة، فإنها تعالج رمز الجاذبية قبل الرمز الذي يولد صفوف جديدة من الكتل. لإصلاح هذا سوف نقوم بإجراء تعديل صغيرعلى التعليمات البرمجية التي تولد المجموعة الأولية من الكتل بحيث تولد تحت النقطة حيث سيكون هناك حاجة لصف جديد. وهذا يسمح لها تجنب تشغيل رمز الجاذبية على الفور، ويسمح لها بإنشاء صف جديد من الكتل حالما تكون الكتل الموجودة في الموقع الصحيح.
انتقل إلى الحدث الذي يولد المجموعة الأولية من الكتل وعدل الإجراء الذي يخلق في الواقع الكتلة.
Action: System>Create object Object = Block Layer = 1 X = SPAWNX + (loopIndex("X"))*(Block.Width+2) Y = SPAWNY - (loopIndex("Y"))*(Block.Width+2) + 5
يجب أن يبدو الحدث الآن كما يلي:



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

الحدث الذي نقوم بإنشائه سوف يتضمن أيضا حالة ثانية والتي يتحقق منها للتأكد من أن الكتلة التي ينظر اليها لا يتم سحبها. هذا الحالة تسمح لنا أن نتأكد من أنه عندما يسحب لاعب كتلة تحت النقطة التي تصبح عندها الكتل صالحة للاستعمال، فإنه لن تتغير صورتها كما لو كانت رمادية، وسوف تبقى باللون الذي من المفترض أن يكون.
لجعل هذه الرسوم المتحركة تعمل نحتاج أولا إلى إضافة حدث جديد:
Event: Condition: Block>Compare Y Comparison = Greater than Y = SPAWNY + ((Block.Width + 2)/2) Condition: Invert: Block>Is Dragging Action: Block>Set frame Frame number = 0
يجب أن يكون الحدث الجديد كما يلي:



يجب أن تكون الآن قادرا على اختبار اللعبة الخاصة بك ويجب أن نرى أن الكتل التي تستخدم الصورة الرمادية عندما تكون تحت النقطة تصبح صالحة للاستعمال.
5. تمكين وتعطيل السحب / الاسقاط
إذا قمت بتشغيل اللعبة الآن، ستلاحظ أنه على الرغم من الكتل لا يمكن تبديلها مع بعضها البعض عندما تكون رمادية، فإنه لا يزال ممكناً سحب الكتل الرمادية حولها والتلاعب بها. هذا لأننا لم نعطل قدرات السحب / الإسقاط للكتلة عندما منعنا اللاعب من مبادلة معها.
لمنع إمكانية نقل الكتل الرمادية، سنعدل الحدث الذي أنشأناه في القسم السابق. أولا سنقوم بإضافة إجراء جديد الذي يوقف تشغيل السحب عندما تكون الكتلة تحت نقطة قابلية الاستخدام.
أضف هذا الإجراء إلى الحدث الذي أنشأناه في وقت سابق:
Action: Block (DragDrop)>Set enabled State = Disabled
نحن أيضا بصدد إضافة بيان آخر لهذا الحدث الذي يتيح سحب الكتلة مرة أخرى عندما تكون فوق النقطة حيث تصبح كتلة صالحة للاستعمال:
Event: Condition: Else Action: Block (DragDrop)>Set enabled State = Enabled
مع كلا هذين التغييرين، يجب أن يبدو الحدث كالتالي:



إذا اختبرت اللعبة في هذه المرحلة، فيجب أن تكون الكتل ليست قابلة للاستعمال بعد عندما تكون رمادية، وينبغي أن تعمل بنفس الطريقة التي تعمل بها دائما عندما لا تكون قابلة للاستخدام.
6. تغييرات السرعة
الشيء الأخير الذي أريد أن أغطيه في هذه المقالة هو النظام الذي سيسمح لنا بتغيير سرعة اللعبة مع مرور الوقت. على وجه التحديد، هذا هو النظام الذي من شأنه أن يجعل الكتل تتحرك بشكل أسرع كلما ألغى اللاعب المزيد منها.
النظام الذي سننشئه بسيط نسبيا: في كل مرة يحصل فيها اللاعب على عدد من النقاط، ستزيد سرعة اللعبة بناء على معدل سنقوم بإنشائه وعدد النقاط التي يحتاج اللاعب للحصول على زيادة السرعة التالية سوف يتغير استنادا إلى معدل ثان.
قبل أن نبدأ فعلا في إنشاء أحداث لهذا النظام، سنقوم بإنشاء متغيرين عالميين للتعامل مع الميزات الجديدة بالنسبة لنا:
Global Variable: SPEEDMOD Type = Number Initial value = 0.8 Constant = Yes Global Variable: PointsForSpeedUp Type = Number Initial value = 400 Constant = No Global Variable: PointsBetweenSpeedUps Type = Number Initial value = 400 Constant = No Global Variable: POINTSFORSPEEDUPMOD Type = Number Initial value = 1.4 Constant = Yes
يجب أن تبدو المتغيرات الجديدة كما يلي:



الآن بعد أن أصبح لدينا المتغيرات في مكانها، وسوف أشرح ما يفعله كل منها.
-
سبيدمود
هو المتغير الذي سوف نضاعف السرعة بواسطته لتعديله كلما وصل اللاعب إلى عدد من النقاط التي يحتاجها لتسبب زيادة السرعة. -
بوينتسفورسبيداب
هو عدد من النقاط التي يحتاجها اللاعب للانتقال الى السرعة التالية. -
بوينتسبيتوينسبيدابس
يمثل مقدار المتغيربوينتسفورسبيداب
الذي سوف يزيد عندما يحصل اللاعب على السرعة، لضبطه بحيث السرعة التالية حتى تأخذ المزيد من النقاط. حتى الآن هي 400، مثلبوينتسفورسبيداب
، ولكن عندما يحصل الاعب في الواقع على سرعة سيتم مضاعفتها بواسطةبوينتسفورسبيدوبمود
قبل إضافتها إلىبوينتسفورسبيداب
. - وأخيرا،
بوينتسفورسبيدوبمود
هو المتغير الذي سوف نستخدمه لتعديل عدد النقاط التي يحتاجها اللاعب للحصول على زيادة سرعته مرة أخرى فوق التي حصل عليها مؤخرا.
بالاضافة الى إعداد المتغيرات، نحن بحاجة أيضا إلى إنشاء كائن الشبح الجديد الذي سيكون بمثابة تنبيه اللاعب عندما تزيد السرعة.
انتقل إلى تخطيط 1 واتبع الخطوات التالية لإنشاء الشبح الجديد:
- إدرج كائن شبح جديد على تخطيط 1.
- مع محرر الرسوم المتحركة، افتح الصورة
SpeedIncImage.png
.- عين اسم ل
مؤشر زيادة السرعة
. - عين طبقة ل
مجال اللعبة
. - عين الموضع إلى
188، 329.
- عين الرؤية الأولية إلى
غير مرئية
.- أضف سلوك متلاشي إلى الشبح.
- عين نشط في البداية إلى
لا
. - عين زمن التلاشي إلى
2.5
. - عين التدمير إلى
لا
.
- عين اسم ل
يجب أن يبدو تخطيطك الآن كما يلي:



الآن سنقوم في الواقع بإنشاء الحدث الذي يغير السرعة:
Event: Condition: Function>On function Name = "CheckForSpeedUp" Condition: System>Compare variable Variable = Score Comparison = Greater or equal Value = PointsForSpeedUp Action: SpeedIncreaseIndicator>Set visible Visibility = Visible Action: SpeedIncreaseIndicator>Start fade Action System>Set value Variable = CurrentSpeed Value = CurrentSpeed * SPEEDMOD Action System>Set value Variable = PointsBetweenSpeedUp Value = PointsBetweenSpeedUp * POINTSFORSPEEDUPMOD Action: System>Add to Variable = PointsForSpeedUp Value = PointsBetweenSpeedUp
يجب أن يكون حدثك كما يلي:



عندما يتم استدعاء هذه الوظيفة، فإنها تتحقق من معرفة ما إذا كان اللاعب قد سجل نقاط كافية لضمان زيادة السرعة. إذا كان لديه، عندئذ:
- فإنها تنشط الشبح الذي يخبر اللاعب أنه قد زادت السرعة من خلال جعلها مرئية وتبدأ بالتلاشي
- إنه يزيد من السرعة بمضاعفتها بواسطة المعدل
- إنه يحدد عدد النقاط اللازمة قبل السرعة التالية، و
- يضيف تلك القيمة إلى العدد الإجمالي من النقاط التي سوف يحتاجها اللاعب قبل أن تزيد السرعة مرة أخرى.
مع اكمال هذه الوظيفة، علينا فقط التأكد من الحصول على ما يسمى. انتقل إلى وظيفة اعطاء النقاط ()
وأضف هذا الإجراء إلى نهاية الحدث الأساسي والحدث الفرعي:
Action: Function>Call function Name = "CheckForSpeedUp"
يجب أن تبدو الوظيفة اعطاء النقاط ()
الآن كما يلي:



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