مقدمة
^title Introduction
في الصف الخامس، مُنحتُ أنا وأصدقائي حقّ دخول غرفة صفّ صغيرة مهجورة تضمّ جهازَي TRS-80 قديمين إلى حدّ البلي. ورجاءً لإلهامنا، وجد معلّم مطبوعةً من بعض برامج BASIC البسيطة لنمطح بها.
كانت مشغّلتا الأقراص الصوتية في الحاسوبين معطوبتين، لذا كلما أردنا تشغيل بعض الشيفرة، كان علينا أن نكتبها بعناية من الصفر. وهذا ما دفعنا إلى تفضيل البرامج التي لا تتجاوز بضعة أسطر:
10 PRINT "BOBBY IS RADICAL!!!"
20 GOTO 10
ومع ذلك، كان هذا المسار محفوفاً بالمخاطر. كنا لا نعرف كيف نبرمج، لذا كان خطأ نحويّ صغير كافياً ليبقى غامضاً عن أعيننا. وإذا لم يعمل البرنامج — وكان ذلك يحدث غالباً — كنا نبدأ من جديد منذ البداية.
في أسفل كومة الأوراق كان هناك وحشٌ حقيقي: برنامجٌ يشغل عدة صفحات كثيفة من الشيفرة. استغرقني وقتٌ حتى جمعنا الشجاعة لتجربته، لكنه كان لا يُقاوم — فالعنوان أعلى الشيفرة كان «Tunnels and Trolls». لم نعرف ما الذي يفعله، لكنه بدا كأنه لعبة، وما أبرد من لعبة حاسوب تبرمجها بنفسك؟
لم ننجح يوماً في تشغيله، وبعد سنة انتقلنا من تلك الغرفة. (وحين تعلّمت BASIC فعلاً بعد وقت طويل، أدركت أنه لم يكن سوى مولّد شخصيات للعبة طاولة، وليس لعبة بذاتها.) لكن القدر قُضي — فمنذ تلك اللحظة أردت أن أكون مبرمج ألعاب.
في مراهقتي، اشترت عائلتي حاسوباً من نوع Macintosh مع QuickBASIC ثم THINK C لاحقاً. وقد أنفقت كل عطلاتِ صيف تقريباً في العبث بتركيب ألعاب. كان التعلّم وحدي بطيئاً ومؤلماً. فكنت أُشغّل شيئاً بسهولة — ربما شاشة خريطة أو لغزاً صغيراً — لكن كلما كبر البرنامج، صار أصعب وأصعب.
في البداية، كان التحدي هو مجرد جعل شيء ما يعمل. ثمّ صار هو أن أكتشف كيف أكتب برامج أكبر ممّا يتّسع في رأسي. وبدلاً من أن أقرأ عن «كيف تبرمج في C++» فحسب، بدأت أبحث عن كتب عن كيفية تنظيم البرامج.
بعد عدّة سنوات، أعطاني صديق كتاباً: أنماط التصميم: عناصر برمجيات كائنية التوجه قابلة لإعادة الاستخدام. أخيراً! الكتاب الذي كنت أبحث عنه منذ أن كنت مراهقاً. قرأته من الغلاف إلى الغلاف في جلسة واحدة. ظللت أعاني مع برامجي الخاصة، لكنه كان ارتياحاً كبيراً أن أرى أشخاصاً آخرين يكافحون أيضاً ويصلون إلى حلول. شعرت كأنني أملك أخيراً بضعة أدوات أستعملها بدل يديَّ العاريتين فقط.
في عام 2001، حصلت على الوظيفة التي كنت أحلم بها: مهندس برمجيات في Electronic Arts. لم ألبث أن أرى بعض الألعاب الحقيقية وأنظر كيف يجمع المحترفون بعضها بعضاً. فكيف كانت البنية المعمارية (architecture) للعبة ضخمة مثل Madden Football؟ وكيف تفاعلت الأنظمة المختلفة معاً؟ وكيف جعلوا قاعدة شيفرة واحدة تعمل على منصّات متعددة؟
كان فتح الشيفرة المصدرية تجربة مُذلّة ومفاجئة في آن. كانت هناك شيفرة عبقرية في الرسوميات والذكاء الاصطناعي (AI) والحركة والمؤثرات البصرية. وكان لدينا أشخاص يعرفون يستخرجون آخر دورة معالجة يمكن الحصول عليها من المعالج ويستخدمونها استثماراً جيداً. أشياء لم أكن أعرف حتى أنها ممكنة، كان هؤلاء الأشخاص ينجزونها قبل الغداء.
لكن البنية التي تتدلّى منها هذه الشيفرة العبقرية كانت غالباً فكرة متأخرة. كانوا منهمكين تماماً في المزايا، فغفلوا عن التنظيم. وكان هناك ترابط شديد بين الوحدات. وكثيراً ما كانت المزايا الجديدة تُلحَم بقواعد الشيفرة في أي مكان يمكن أن تتّسع فيه. وفي عيني المحبطتين، بدا الأمر وكأن مبرمجين كثيرين، إن فتحوا أنماط التصميم أصلاً، لم يتجاوزوا قط المفرد (Singleton).
وبالطبع لم يكن الأمر إلى هذا الحدّ من السوء. كنت أتخيّل مبرمجي الألعاب جالسين في برج عاجي مغطّى بلوحات بيضاء، يناقشون دقائق البنية المعمارية بهدوء لأسبوعين متتاليين. لكن الواقع أن الشيفرة التي كنت أنظر إليها كتبها أشخاص يعملون تحت مواعيد نهاية متوترّة. فعلوا أفضل ما استطاعوا، وكما أدركت تدريجياً، كان أفضلهم غالباً جيداً جداً. وكلما قضيت وقتاً أطول في العمل على شيفرة الألعاب، وجدت أجزاءً أكثر من العبقرية مختبئة تحت السطح.
ولأسف، كانت كلمة «مختبئة» وصفاً دقيقاً في كثير من الأحيان. فهناك جواهر مدفونة في الشيفرة، لكن كثيرين مشوا فوقها مباشرة. لقد رأيت زملاء يكافحون من أجل إعادة اختراع حلول جيدة، بينما كانت أمثلة لما يحتاجونه بالضبط مدفونة في قاعدة الشيفرة نفسها التي يقفون عليها.
تلك هي المشكلة التي يسعى هذا الكتاب إلى حلّها. لقد استخرجت أفضل الأنماط التي وجدتها في الألعاب وصقلتها، وقدّمتها هنا كي نتمكّن من قضاء وقتنا في اختراع أشياء جديدة بدلاً من إعادة اختراعها.
ماذا ينتظرك
هناك عشرات الكتب عن برمجة الألعاب في العالم. فلماذا نكتب كتاباً آخر؟
معظم كتب برمجة الألعاب التي رأيتها تقع في واحدة من فئتين:
-
كتب خاصة بمجال محدّد. تُعطيك هذه الكتب الضيّقة التركيز غوصاً عميقاً في جانب محدّد من تطوير الألعاب. فهي تعلّمك عن الرسوميات ثلاثية الأبعاد، أو العرض الفوري، أو محاكاة الفيزياء، أو الذكاء الاصطناعي، أو الصوت. وهذه هي المجالات التي يتخصّص فيها كثير من مبرمجي الألعاب مع تقدّم مسيرتهم.
-
كتب تغطّي المحرك بأكمله. وفي المقابل، تحاول هذه الكتب أن تامس أجزاءَ محرك اللعبة بأكمله. فهي موجّهة إلى بناء محرك كامل يناسب نوعاً محدّداً من الألعاب، عادةً لعبة تصويب ثلاثية الأبعاد من منظور الشخص الأول.
أحبّ كتابَي النوعين، لكنني أعتقد أنهما يتركان بعض الثغرات. فالكتب الخاصة بمجال معين نادراً ما تخبرك كيف يتفاعل الجزء من الشيفرة هذا مع بقية اللعبة. قد تكون ساحراً في الفيزياء والعرض، لكن هل تعرف كيف تربط بينهما بسلاسة؟
قد تكون ساحراً في الفيزياء والعرض، لكن هل تعرف كيف تربط بينهما بسلاسة؟
الفئة الثانية تغطّي ذلك، لكنني أجد غالباً في الكتب التي تغطّي المحرك بأكمله أنها مفرطة في التصلّب ومفرطة في الارتباط بنوع محدّد من الألعاب. وخاصةً مع ظهور الألعاب المحمولة والعابرة، فنحن في عصر تُصنع فيه أنواع مختلفة كثيراً من الألعاب. فلسنا جميعاً مجرد نسخ من Quake. أما الكتب التي ترافقك خطوة بخطوة عبر محرّك واحد فلا تفيد عندما لا تلائم لعبتك التي تريدها ذلك القالب.
بدلاً من ذلك، ما أحاول فعله هنا أقرب إلى à la carte. فكل فصل في هذا الكتاب فكرة مستقلة يمكنك تطبيقها على شيفرتك. وهكذا، يمكنك أن تخلطها وتُنسبها بالطريقة التي تناسب اللعبة التي تريد صنعها.
علاقتها بأنماط التصميم
أي كتاب برمجة في عنوانه كلمة «الأنماط» لا بدّ أنه يرتبط بالكتاب الكلاسيكي أنماط التصميم: عناصر برمجيات كائنية التوجه قابلة لإعادة الاستخدام لإريك غاما وريتشارد هلم ورالف جونسون وجون فليسيدز. (وهم ما يُشار إليهم على نحو مثير للريبة بـ«رباعية الأنماط»).
بتسمية هذا الكتاب «أنماط برمجة الألعاب»، لا أحاول أن أوحي بأن كتاب الرباعية غير صالح للألعاب. على العكس تماماً: فقسم أنماط التصميم من جديد في هذا الكتاب يغطي كثيراً من أنماط أنماط التصميم، لكن مع تركيز على كيفية تطبيقها في برمجة الألعاب.
وفي المقابل، أعتقد أن هذا الكتاب ينطبق على البرمجيات غير المرتبطة بالألعاب أيضاً. فقد كان بإمكاني أن أسمي هذا الكتاب المزيد من أنماط التصميم، لكنني أعتقد أن الألعاب تقدّم أمثلة أكثر إثارة. أتريد فعلاً أن تقرأ كتاباً آخر عن سجلات الموظفين والحسابات البنكية؟
ومع ذلك، فبينما الأنماط المقدَّمة هنا مفيدة في برمجيات أخرى، أعتقد أنها تناسب بصورة خاصة تحديات الهندسة التي تُصادف عادةً في الألعاب:
-
الزمن والتسلسل غالباً ما يكونان جزءاً أساسياً من بنية اللعبة. فلا بدّ أن تحدث الأشياء بالترتيب الصحيح وفي الوقت الصحيح.
-
دورات التطوير مضغوطة إلى حدٍّ كبير، ويحتاج عدد من المبرمجين إلى القدرة على البناء السريع والتكرار على مجموعة غنية من السلوكيات المختلفة دون أن يتداخل بعضهم مع بعض أو يترك آثاراً في كل أنحاء قاعدة الشيفرة.
-
وبعد أن يُعرَّف كل هذا السلوك، يبدأ في التفاعل. فالوحوش تعضّ البطل، وجرعات الأكسير تختلط ببعضها، والقنابل تنفجر في الأعداء والأصدقاء على حدٍّ سواء. ولا بدّ أن تحدث تلك التفاعلات دون أن تتحوّل قاعدة الشيفرة إلى كتلة متشابكة من الخيوط.
-
وأخيراً، الأداء حاسم في الألعاب. فمطوّرو الألعاب في سباق دائم لمعرفة من يستطيع استخراج أكبر قدر من منصّته. فالحيل التي تقتطع بها دورات المعالجة قد تعني الفرق بين لعبة من الفئة الأولى ومبيعات بالملايين، أو بين إطارات متقطّعة ونقّاء غاضبين.
كيف تقرأ الكتاب
ينقسم أنماط برمجة الألعاب إلى ثلاثة أقسام واسعة. يقدّم القسم الأول الكتاب ويضعه في إطاره. وهو الفصل الذي تقرؤه الآن مع الفصل التالي.
القسم الثاني، أنماط التصميم من جديد، يمرّ على حفنة من أنماط كتاب الرباعية. وفي كل فصل، أقدّم رؤيتي للنمط وكيف أراه مرتبطاً ببرمجة الألعاب.
القسم الأخير هو جوهر الكتاب الحقيقي. فهو يقدّم ثلاثة عشر نمط تصميم وجدتُها مفيدة. وقد جُمعت في أربع فئات: أنماط التسلسل، الأنماط السلوكية، أنماط فصل الترابط، وأنماط التحسين.
يُشرح كل واحد من هذه الأنماط ببنية موحّدة تتيح لك استخدام هذا الكتاب مرجعاً والوصول سريعاً إلى ما تحتاجه:
-
يقدّم قسم النيّة وصفاً موجزاً (snapshot) للنمط من حيث المشكلة التي يقصد حلّها. وهو في المقدّمة كي تتمكّن من الانتقال بسرعة بين صفحات الكتاب للعثور على نمط يساعدك في معركتك الحالية.
-
يصف قسم الدافع مشكلة مثال سنطبّق النمط عليها. فعلى خلاف الخوارزميات المجرّدة، فإن النمط غالباً ما يكون بلا شكل ما لم يُطبَّق على مشكلة بعينها. وتعليم النمط دون مثال هو كتعليم الخَبز دون ذكر العجين. يقدّم هذا القسم العجين الذي ستخبزه الأقسام اللاحقة.
-
يستخلص قسم النمط جوهر النمط من المثال السابق. فإذا أردت وصفاً جافاً من كتاب دراسي للنمط، فهذا هو. وهو أيضاً مراجعة مفيدة إن كنت على معرفة بالنمط أصلاً وتريد التأكّد من أنك لم تنسَ مكوّناً منه.
-
حتى الآن، لم يُشرح النمط إلا في ضوء مثال واحد. لكن كيف تعرف أن النمط سيكون جيداً لمشكلتك أنت؟ يقدّم قسم متى تستخدمه بعض الإرشادات حول متى يكون النمط مفيداً ومتى يُستحسن تجنّبه. ويشير قسم ضع في اعتبارك إلى النتائج والمخاطر المترتّبة على استخدام النمط.
-
إن كنت، مثلي، تحتاج إلى أمثلة ملموسة كي تفهم شيئاً فعلاً، فقسم الشيفرة النموذجية هو قسمي. فهو يمشي بك خطوة بخطوة عبر تنفيذ كامل للنمط، لترى بالضبط كيف يعمل.
-
تختلف الأنماط عن الخوارزميات المفردة لأنها مفتوحة النهاية. فكلما استخدمت النمط، من المرجّح أن تنفّذه بطريقة مختلفة. والقسم التالي، قرارات التصميم، يستكشف هذه المساحة ويعرض عليك خيارات مختلفة للتفكير فيها عند تطبيق النمط.
-
ولخاتمة الأمر، هناك قسم قصير بعنوان انظر أيضاً يوضّح كيف يرتبط هذا النمط بغيره، ويوجّهك إلى شيفرة مفتوحة المصدر من العالم الحقيقي تستخدمه.
عن الشيفرة النموذجية
الشيفرة النموذجية في هذا الكتاب مكتوبة بـ C++، لكن ذلك لا يعني أن هذه الأنماط مفيدة في تلك اللغة وحدها، أو أن C++ لغة أفضل لها من غيرها. فأي لغة تقريباً ستفي بالغرض، وإن كانت بعض الأنماط تفترض أن لغتك تملك كائنات وأصنافاً.
اخترت C++ لسببين. أولاً، إنها أكثر لغة انتشاراً في الألعاب المُطلَقة تجارياً، وهي لغة التواصل المشتركة في هذه الصناعة. وفوق ذلك، فإن صياغة C التي تقوم عليها C++ هي أيضاً أساس Java وC# وJavaScript ولغات كثيرة أخرى. حتى إن كنت لا تعرف C++، فالاحتمال كبير أن تفهم شيفرة الأمثلة هنا بجهد يسير.
هدف هذا الكتاب ليس أن يعلّمك C++. فالأمثلة مُبقيّة بأبسط شكل ممكن ولا تمثّل أسلوب C++ الجيد ولا استعمالات الجيدة له. اقرأ شيفرة الأمثلة لتفهم الفكرة التي يُعبَّر عنها، لا الشيفرة التي تعبّر عنها.
على وجه التحديد، الشيفرة هنا مكتوبة بأسلوب «حديث» -- أي C++11 أو أحدث. فهي لا تستخدم المكتبة القياسية ونادراً ما تستخدم القوالب. وهذا يجعلها شيفرة C++ «سيئة»، لكنني آمل أن يكون تجريدها بهذا الشكل أقرب إلى القلب لمن يأتي من C أو Objective-C أو Java أو غيرها من اللغات.
ولتجنّب إهدار مساحة على شيفرة رأيتَها من قبل أو لا علاقة لها بالنمط، ستُحذف أحياناً أجزاء من الشيفرة في الأمثلة. وعند حدوث ذلك، توضع علامة حذف (ellipsis) في المثال لتبيّن مكان الشيفرة المفقودة.
تخيّل دالة تؤدّي بعض العمل ثم تُعيد قيمة. النمط الذي يُشرح هنا لا يعنيه إلا القيمة المُعادة، لا العمل الذي يُنجَز. في تلك الحالة، ستبدو شيفرة المثال هكذا:
^code update
إلى أين نذهب من هنا
الأنماط جزء من تطوير البرمجيات يتغيّر باستمرار ويتّسع. يواصل هذا الكتاب العملية التي بدأتها الرباعية من توثيق أنماط البرمجيات التي رآتها ومشاركتها، وستستمر هذه العملية بعد أن يجفّ حبر هذه الصفحات.
أنت جزء أساسي من تلك العملية. فبينما تطوّر أنماطك الخاصة وتحسّن أنماط هذا الكتاب أو تفنّدها، فإنك تسهم في مجتمع البرمجيات. وإن كانت لديك اقتراحات أو تصحيحات أو أي ملاحظات أخرى على ما هنا، فلا تتردّد في التواصل معنا!