مقدمة
هناك عشرات منصات تطوير الألعاب في السوق الآن، منها التجاري باهظ الثمن ومنها المجاني، منها ما يحتاج إلى خبرة هائلة ومنها ما يستطيع المبتدؤون البدء باستخدامه بسهولة، منها ما يطور ألعاب لـWindows ومنها ما يطور الألعاب لنظام آخر...
ولكن ليس هناك أي واحد منها مجاني، شديد السهولة، يبرمج ألعاب PC و XBOX360، إلا XNA!
الكثير ممن يعملون الآن في تطوير الألعاب بدؤوا في هذا الطريق بعد أن لعبوا لعبة ممتعة ما ففكروا كيف تم تطويرها، ليبدؤوا بتعلم لغة برمجة ومنصة تطوير ألعاب يعتمدون عليها ثم ليعملوا في إحدى الشركات الكبرى.
إن كنت ممن فكر في إحدى المرات بتطوير لعبة ما فهذه المقالة حتماً لك، ستجد فيها جميع المعلومات التي تحتاجها للبدء في الخوض في بحر تطوير الألعاب باستخدام منصة XNA ولغة برمجة C#، وكذلك ستتعلم في هذه المقالة أن تقوم بتطوير لعبة لطيفة ستعطيك المعلومات الأساسية التي ستعتمد عليها في ألعابك المستقبلية القادمة المعتمدة على XNA.
تاريخ برمجة الألعاب في الـ PC و XBOX – كيف ظهر XNA إلى الوجود [Wikipedia: DirectX]
تبدأ القصة مع نظام MS-DOS، كان النظام يسمح لمبرمجي الألعاب بالتعامل مع عتاد الكمبيوتر (Hardware) و الذي يشمل بطاقة الإظهار، بطاقة الصوت وأجهزة الإدخال بشكل مباشر، ولكن ذلك كان يخلق مشاكلاً كثيرة، فهناك صعوبة العملية وتعقيدها وهناك الاختلافات بين الأجهزة المختلفة، فإن كتبت لعبة عملت على جهازك فهذا لا يعني أنها ستعمل على جهاز آخر من شركة مصنعة أخرى.
وعندما طرحت مايكروسوفت نظام Windows 95، أصيب مبرمجو الألعاب بخيبة أمل، لقد وجدوا أنفسهم عاجزين عن القيام بما كانوا يقومون به في أيام نظام DOS، فلم يكن النظام الجديد يسمح لهم بالتعامل مباشرةً مع أجهزة الهاردوير لبرمجة ألعابهم ففقدوا القدرة على القيام بالوظائف السريعة و المتقدمة و التي تحتاجها برمجة الألعاب، فكان لابد من حل لذلك، جاء في البداية تحت اسم Windows Game SDK و كان يحوي عدة أجزاء منها DirectSound للتعامل "مباشرةً" مع بطاقة الصوت في الكمبيوتر و DirectDraw للتعامل "مباشرةً" مع أجهزة الإظهار، و DirectPlay للتواصل مع الألعاب الأخرى وغيرها، وبدأ البعض باستخدام الاسم Direct "X" للتعبير عن جميع أجزاء هذا الـSDK بدلاً من استعمال ذلك الاسم الطويل حتى قامت مايكروسوفت باعتماد هذا الاسم و أصدرت النسخة الأولى منDirectX في الثلاثين من سبتمبر\أيلول 1995، أي بعد إصدار نظام ويندوز 95 بعدة أشهر.
الميزة التي قدمها DirectX للمبرمجين هي أن ألعابهم أصبحت ستعمل على جميع الأجهزة وبغض النظر عن مواصفاتها، فلم يعودوا محتاجين إلى التعامل مع كل جهاز لوحده، وكان هذا بفضل تعاون مصنعي الأجهزة مع مايكروسوفت لدعم أجهزتهم، فقد قام أغلب المصنعين بكتابة مشغلات Drivers لأجهزتهم لتستطيع التعامل مع DirectX، و قام DirectX بدوره بتأمين واجهة بسيطة وقياسية تسمح للألعاب بالتعامل عن طريقه مع تلك الأجهزة، فارتاح مبرمجو الألعاب من عبء التعامل مع الأجهزة والتفتوا إلى عملهم الأساسي، برمجة الألعاب.
بعد ذلك بعام طرح الإصدار الثاني من DirectX و الذي لم يقدم تحسينات تذكر باستثناء دعمه لـWindows NT 4.0، و لكن بعد فترة قصيرة، طرحت مايكروسوفت الإصدار الثالث من DirectX لتقدم Direct3D والذي سمح للمبرمجين بكتابة ألعاب ثلاثية الأبعاد.
لم يتم طرح إصدار رابع من DirectX لأسباب مجهولة وطرح مباشرةً الإصدار الخامس من DirectX في العام 1997 ثم طرحت له تحديثات بعد عام ليدعم نظام Windows 98 الجديد، ثم طرح الإصداران السادس و السابع في العامين 1998 و 1999.
وعندما طرح الإصدار الثامن من DirectX في العام 2000 كانت بطاقات الإظهار تدعم مظللات الرؤوس والبكسلات (vertex and pixel shaders) فقامت مايكروسوفت بالسماح للبرمجين بتمرير كودات مكتوبة بلغة التجميع (assembly language) إلى بطاقات الإظهار لتقوم بتشغيلها بشكل مباشر، أتاح هذا مرونة كبيرة في برمجة الألعاب، مع الاحتفاظ بالسرعة العالية التي كانت موجودة قبل استخدام المظللات.
(إن كنت تريد معرفة المزيد عن المظللات Shaders فقم بمراجعة مقالة ويكيبيديا العربية على العنوان : http://ar.wikipedia.org/wiki/مظلل)
و تم استخدام هذا الإصدار (الثامن) من DirectX بعد تطويره بتعاون بين مايكروسوفت و NVIDIA لتشغيل جهاز XBOX.
في عام 2002، طرحت مايكروسوفت الإصدار التاسع من DirectX و الذي دعم الجيل الثاني من المظللات، ثم تم طرح DirectX 9.0C و الذي دعم الجيل الثالث بالإضافة إلى نسخة خاصة لـXBOX 360.
في العام 2008 تم طرح DirectX 10 و الذي يعمل على ويندوز فيستا Vista و يستخدم نظام المشغلات Drivers الجديد الخاص بذلك الإصدار من ويندوز، ولهذا، لم يتم طرح النسخة العاشرة من DirectX لويندوز XP.
تدعم هذه النسخة (بل تحتاج) إلى الجيل الرابع من المظللات.
وأخيراً DirectX 11 و الذي سيطرح مع ويندوز 7، و سيدعم نظام Vista أيضاً (بما أن ويندوز 7 يستعمل نفس نظام المشغلات drivers الخاص بفيستا).
مهلاً، لم نعد نتحدث عن التاريخ! لقد أصبحنا في المستقبل! أرجو ألا تمانعوا أن نعود معاً إلى الإصدار التاسع من DirectX...
في نفس الفترة الذي ظهر فيها DirectX 9، ظهر إطار عمل دوت نيت .NET الشهير، فأراد مبرمجو دوت نيت أن يحظوا بفرصة تطوير الألعاب عن طريق DirectX وتمنوا ألا يكون ذلك مقتصراً على مبرمجي C++.
قامت مايكروسوفت - مستجيبة لطلباتهم - بطرح ما سمي Managed DirectX (MDX) والذي تواسط بين مبرمجي دوت نيت و DirectX، هذه الـ"Managed" تدل على دعم هذه نسخة DirectX هذه للبرامج و الألعاب التي يقوم دوت نيت بـ"إدارتها" هو بدلاً من جعل النظام يقوم بتشغيلها بشكل مباشر، و تتضمن هذه الإدارة إدارة الذاكرة.
لم يكن MDX بسرعة DirectX الأصلي ولكنه لم يكن أبطأ كثيراً، على كل حال، معظم الألعاب - حتى التجارية و الضخمة منها - ستعمل بنفس السرعة إن قمت ببرمجتها على MDX، فليست جميع الألعاب تحتاج كل تلك السرعة الضخمة.
بتضحيتك بقليل من السرعة ستحظى بالكثير من الميزات الرائعة التي يقدمها دوت نيت و التي ستجعل الحياة تستحق الاستمرار بها، فهناك نظام دوت نيت الرائع لإدارة الذاكرة عن طريق الـGarbage Collector الخاص به (الـGarbage Collector هو وحدة برمجية تعمل دون تدخل من المبرمج وظيفتها تفريغ الذاكرة من الموارد التي لم تعد برامجك و ألعابك بحاجة إليها).
لكن تم إيقاف تطوير الإصدار الثاني من MDX في النسخة التجريبية Beta بعد أن قررت مايكروسوفت أنها تريد لمبرمجي دوت نيت أفضل من ذلك، تريد لهم أي يطوروا لأجهزة أكثر من مجرد الكمبيوتر الشخصي PC. لذلك، تم تحويل موظفي MDX للعمل في مشروع مايكروسوفت الجديد، XNA...
ماذا تعني بـ XNA؟ [Wikipedia: XNA]
XNA هو اختصار لـ"XNA ليس اختصاراً" (XNA's Not Acronymed)!
وهي باختصار منصة مايكروسوفت لتطوير الألعاب للكمبيوتر الشخصي PC و XBOX360 عن طريق البيئة المدارة Managed، وعندما نقول XNA فنحن نعني أحد شيئين : إما XNA Framework أو XNA Game Studio.
XNA Game Studio هي بيئة لتطوير الألعاب المبنية على XNA Framework، وهذه البيئة تعتمد على Visual Studio وتضيف إليه بعض الميزات الجديدة، الفرق بينهما تماماً كالفرق بين Visual Studio و .NET Framework، الأولى بيئة لبناء برامج معتمدة على الثانية.
لماذا XNA؟ [ChadCarter]
أو بعبارة أخرى ماهي فوائد استعمال XNA؟ الجواب باختصار هو أن XNA يقدم لك وسيلة سهلة و مجانية للتعامل مع عتاد (Hardware) الجهاز (و الذي لم يعد الـPC فقط، سنتحدث عن هذا بعد قليل)، و هذا يشمل أجهزة الإدخال (لوحة المفاتيح، الماوس، عصا الألعاب، ...)، بطاقة الإظهار و بطاقة الصوت، و هو يسمح بتخزين المعلومات واسترجاعها فيما بعد، في حال أردت تسجيل المرحلة التي وصل إليها اللاعب مثلاً، تشغيل مقاطع الفيديو (بدءاً من الإصدار القادم، 3.1)، عمل نسخ تجريبيبة من ألعابك، اللعب الجماعي المدعوم بدءاً من الإصدار الثاني من XNA، القيد هنا أنك ستحتاج في معظم الحالات (مثل لعب PC مع PC على الإنترنت) إلى اشتراك بخدمات غير مجانية.
كل هذه الخدمات تقدم لك أساسات تطوير لعبة كاملة بشكل مجاني إلا في حالات معينة، وحتى في تلك الحالات، المبلغ الذي ستدفعه زهيد مقارنةً بما ستحصل عليه، مما يجعل الأمر عرضاً لا يقاوم فعلاً.
إحدى أهم الميزات في XNA – كما لمحنا من قبل – هي دعم تطوير الألعاب لمنصة ألعاب بثقل XBOX360، لم تسمح أي شركة من قبل للهواة ببرمجة الألعاب على منصتها حتى فعلت مايكروسوفت ذلك في عام 2006 مع إصدار XNA وبشكل رخيص، فجميع أدوات التطوير مجانية ولكنك ستحتاج إلى اشتراك بخدمة Microsoft Creators Club والتي تكلفك 100 دولار في العام (أو مبلغ أقل لفترة أقصر)، ولكنك ستربح بالمقابل ميزة عظيمة هي أنك ستستطيع وضع لعبتك للبيع في خدمة Xbox Live Marketplace وبالثمن الذي تريده (200، 400 أو 800 نقطة) ثم لتحصل على 70% من عائدات بيع لعبتك عن طريق تلك الخدمة، وهو ما أدى إلى انتشار XNA وحصوله على شعبية كبيرة في مجال تطوير الألعاب، أضف إلى ذلك الطرق الأخرى التي تشجع بها مايكروسوفت استخدام XNA كالمسابقة السنوية لتطوير الألعاب التي تعقدها مايكروسوفت (DreamBuildPlay.com) والتي تقدم فيها مبلغ 10,000 دولار أمريكي للمرتبة الأولى.
[CodeProject]
لكي تطور لعبة تستخدم XNA فإنك تحتاج إلى شيئين :
- بيئة تطوير الألعاب والتي تشمل XNA Framework و XNA Game Studio (في الإصدار الثالث حالياً).
- بيئة برمجة هي Visual Studio والذي يتوافر منه نسخة مجانية وكافية بالنسبة لنا هي Visual C# Express Edition 2008 SP1.
إن أردت تطوير لعبة ثلاثية الأبعاد فستحتاج إلى أداة لعمل النماذج (Modeling)، إن أردت برنامج مجاني فهناك برنامج Blender المفتوح المصدر والذي يعتبر برنامجاً جيداً وخاصةً للهواة، كما يمكنك استعمال برنامجك المفضل والذي يبلغ ثمنه بضعة آلاف من الدولارات إن شئت.
يمكنك البحث عن هذه الأدوات وتحميلها بشكل مجاني من مواقع الشركات المنتجة لها.
كيف أبدأ؟ [CoDe]
لتبدأ بتطوير لعبة ما باستخدام XNA ستحتاج إلى معرفة جيدة بلغة C#، لن تحتاج إلى احتراف كامل للغة ولكن فهم ميزات اللغة الأساسية أمر مطلوب، فمثلاً ستحتاج إلى فهم مبادئ البرمجة الكائنية التوجه OOP والتي يستخدمها إطار عمل دوت نيت وإطار عمل XNA.
ربما تظن (إن كنت ممن يستخدمون منصة DirectX) أن الخطوة الأولى لمن يريد البدء باستخدام XNA هي عمل نافذة سوداء والتحكم بدقتها وجعلها تملأ الشاشة Full Screen وهو ما يحتاج عملاً لا بأس به لإنجازه بالنسبة لمبتدأ، أذكر تماماً الوقت الطويل الذي قضيته حتى حصلت على شاشة سوداء فارغة مرسوم بها دائرة بيضاء باستخدام DirectX!، ولكن لا تقلق، الحياة في عالم XNA أشد جمالاً وسلاماً.
لتلقي نظرة أولى قم بتشغيل XNA Game Studio، ستجد هناك أمـ...، مهلاً! أليس هذا Visual C# Express Edition؟ نعم، هو كذلك، مع بعض الإضافات التي منحتك إياها بيئة تطوير ألعاب XNA.
بالدخول إلى نافذة مشروع جديد (لا داعي لإخبارك أنه الأمر New Project من القائمة File) ستجد أنك تستطيع أن تبني لعبة Game أو مكتبة Game Library من أجل كل جهاز يدعمه إصدار XNA الذي تستعمله، إن كنت تستعمل الإصدار الأخير فستجد ثلاث أجهزة، PC، XBOX360 و Zune.
كما ستجد لعبة فضاء باسم Spacewar game جاهزة يمكنك تشغيلها والإطلاع على كودها لتأخذ فكرة عن البرمجة باستخدام XNA.
بعد أن تنتهي من اللعب، أقصد من تفحص كود اللعبة قم بعمل مشروع جديد من نوع Windows Game لتجد أن XNA قام بوضع كل الكود الذي يلزمك لتبدأ، قم بضغط F5 لتشغل اللعبة الجديدة لتجد أن نافذة اللعبة عملت فوراً وظهرت لك نافذة فارغة زرقاء اللون، وبدون أن تكتب سطراً من الكود، سيعطيك هذا بعضاً من الاطمئنان إلى سهولة تطوير الألعاب باستخدام XNA فكل ما عليك فعله الآن (كما سنرى بعد قليل) هو إضافة شكل هنا وآخر هناك والتحكم بها لتحظى بلعبة جاهزة! والأهم من ذلك، سيطمئنك تشغيل هذا المشروع الفارغ أن بطاقة الإظهار لديك تدعم احتياجات التطوير باستخدام XNA والتي تشمل دعم جيل المظللات 1.1 كحد أدنى، وبعض الألعاب قد تتطلب جيل المظللات 2.0. وأما إن حصلت على رسالة خطأ من نوع ما فالأغلب أن بطاقة إظهارك لا تدعم XNA!
من ضمن ما يسهله عليك XNA هو إدارة المحتوى (ما يسمى خط إنتاج Content Pipeline) وهو الذي سيساعدك في جلب كل ما تحتاجه من محتوى إلى لعبتك لاستخدامه فيها، فعلى سبيل المثال، سيسهل لك التعامل مع النماذج ثلاثية الأبعاد 3D Models، مع الإكساءات Textures، مع الصور، مع الأصوات، مع ملفات المعلومات (كملفات XML) وغيرها، ثم يقوم بحفظ كل هذا المحتوى في اللعبة النهائية على شكل ملفات XNA ثنائية (XNA Binary Files) ذات اللاحقة xnb مما سيجعلها محمية بشكل جيد من العبث فيها.
كما أن من ضمن خدمات XNA هو التعامل السهل جداً مع المستخدم، حيث يوفر XNA طريقة سهلة لمعرفة "حالة" State أحد أجهزة تحكم المستخدم (لوحة المفاتيح Keyboard، الماوس Mouse، وجهاز التحكم Gamepad)، فمثلاً، لتعرف إن كانت لوحة المفاتيح في "حالة" الضغط على مفتاح Escape لتخرج من اللعبة ستكتب كود شبيه بما يلي :
if (Keyboard.GetState().IsKeyDown(Keys.Escape)
== true)
{
this.Exit();
}
لاحظ أن كلمة Keyboard عنصر (Object) جاهز يقدمه XNA ولسنا نحن من قمنا بتعريفه، ففي الكود البسيط السابق طلبنا من لوحة المفاتيح (Keyboard) أن تخبرنا إن كانت في حالة (GetState()) يضغط المستخدم على زر (IsKeyDown) هو Escape (Keys.Escape)، بسيط جداً، أليس كذلك؟
وبنفس البساطة التعامل مع أجهزة تحكم أخرى كالماوس و الـ Gamepad.
تحتوي اللعبة مجموعة من المكونات المسماة GameComponent (Microsoft.Xna.Framework.GameComponent) وكل منها تحوي إجراءات أساسية خاصة بها للتحديث Update، الرسم Draw والإعداد الأولي Initialize، وتقوم اللعبة باستدعاء هذه الإجراءات لأي مكون تلقائياً بعد أن يضاف إلى قائمة مكونات اللعبة (Game.Components.Add).
سنبدأ ببناء لعبة بسيطة ثنائية الأبعاد لتوضيح مبادئ تطوير الألعاب باستخدام XNA من جهة، وسهولة التعامل معه من جهة أخرى.
اللعبة التي سنبنيها عبارة عن لعبة تسير فيها سيارة في طريق مملوء بالعقبات وعليها أن تتجاوز تلك العقبات، للطريق مسارين يمكن للاعب الانتقال من أحدها إلى الآخر أثناء سير السيارة عن طريق الضغط على مفتاح Space ليتجنب العقبات القادمة.
ستقوم اللعبة بعد العوائق التي استطاع اللاعب أن يتجنبها، ومع ازدياد هذا العدد ستزداد سرعة السيارة مما سيزيد صعوبة اللعبة. ستنتهي اللعبة مع فشل اللاعب بتجاوز أحد العقبات ليحظى بخيار الخروج من اللعبة أو البدء من جديد.
لنبدأ إذن، قم بعمل مشروع جديد من نوع "Windows Game"، وأعط اللعبة اسماً يروق لك.
سنبدأ بإضافة صورة الطريق، والتي ستظهر متحركة بشكل طولي للدلالة على سير السيارة في طريقها، سنقوم بإضافة الصورة (والتي تجدها وجميع الصور المستخدمة مرفقة مع المقالة) عن طريق الضغط على اسم المشروع بالزر الأيمن للماوس ثم اختيار Existing Item من الخيار Add لتظهر لنا نافذة إضافة عنصر جاهزة والتي تظهر ملفات لغة C# بشكل افتراضي (Visual C# Files) وهو تصنيف لا تندرج تحته ملفات الصور والتي تندرج تحت تصنيف آخر هو Content Pipeline Files، قم باختيار هذا التصنيف عن طريق الخيار " Files of type" الظاهر في النافذة السابقة.
اختر صورة الطريق المرفقة وقم بإضافتها لتتم إضافتها إلى ملفات المشروع، سنقوم الآن بإظهارها على الشاشة عن طريق إضافة كائنين (Objects) إلى الـClass الافتراضية للعبة (وسيكون ملفها بشكل افتراضي يحمل الاسم Game1.cs) وهما الذان سيخزنان الصورة ويقوما برسمها، سنعرفهما كما يلي :
private SpriteBatch mSpriteBatch;
private Texture2D mRoad;
SpriteBatch هو عنصر XNA الذي يستخدم لرسم الصور على الشاشة، بينما يقوم Texture2D بتخزين الصور وهما عنصران يستخدمان بكثرة وخاصةً مع الألعاب ثنائية الأبعاد 2D في XNA ولهذا من المهم تعلم التعامل معهما ومعرفة ما يمكنهما فعله.
الخطوة التالية هي الإعداد الأولي لاستخدامهما فيما بعد (Initialize)، ولنقوم بذلك سنستخدم الإجراء الموجود بشكل افتراضي والمسمى LoadGraphicsContent.
(لاحظ أن XNA يقدم لنا بعض العناصر والإجراءات الجاهزة في ملفات اللعبة وهي تشكل قالب اللعبة الذي نقوم بملئه لبناء لعبتنا) إذاً سنضيف الكود التالي إلى الإجراء LoadGraphicsContent وتحديداً داخل العبارة الشرطية If :
if (loadAllContent)
{
mSpriteBatch = new SpriteBatch(graphics.GraphicsDevice);
mRoad = content.Load<Texture2D>("Road");
}
يقوم الكود السابق بدايةً بإعداد عنصر الـ SpriteBatch ثم تحميل صورة الطريق إلى عنصر الـ Texture2D. إذاً قمنا بـ"جلب" صورة الطريق، والآن سنقوم برسمها بوضع كود الإظهار في إجراء الرسم Draw()، وهو إجراء جاهز آخر يضاف تلقائياً عند عمل لعبة جديدة وتوضع فيه كل كودات الرسم.
في البداية سنقوم بـ"مسح" الشاشة وتعبئتها بلون مناسب يتناسب مع الطريق الذي نريد رسمه، كاللون البني مثلاً، ولليقام بذلك سنقوم بتعديل سطر الكود الذي يمحو الشاشة باللون الأزرق ليصبح :
graphics.GraphicsDevice.Clear(Color.SaddleBrown);
ثم سنضيف بعده الكود التالي :
mSpriteBatch.Begin();
mSpriteBatch.Draw(mRoad, new Vector2(0,0),Color.White);
mSpriteBatch.End();
في السطر الأول نخبر العنصر mSpriteBatch أننا نريد "البدء" Begin() بالرسم إلى الشاشة، ثم سنقوم لرسم صورة الطريق mRoad بتحديد إحداثيات البدء للصورة، وقد قمنا في السطر الثاني بتحديد الإحداثيات (0,0) للدلالة على الزاوية اليسرى العليا من الشاشة، واللون الأبيض Color.White الذي نمرره لإجراء الرسم (في نفس السطر) يخبره ألا يقوم بإضافة أي "صبغة" من أي لون إلى الصورة.
ثم نخبر mSpriteBatch في السطر الثالث أننا انتهينا End() من عمليات الرسم وعندها ستكون صورة الطريق قد أصبحت جاهزة للظهور.
قم بتجريب تشغيل اللعبة (F5) لتظهر لك صورة الطريق وعلى جانبيها اللون البني الذي قمنا بملء الشاشة به.
سنبدأ الآن بجعل الطريق يتحرك شاقولياً، ولنقوم بذلك سنقوم بإضافة بعض العناصر إلى الصنف الذي نعمل به.
بداية أضف الكود التالي إلى أعلى الصنف، حيث عرفنا عنصري SpriteBatch و Texture2D:
private float mVelocityY;
private int[] mRoadY = new int[3];
سنستعمل مصفوفة الأعداد الصحيحة لتسجيل تعاقب أقسام الطريق والتي ستستمر بالحركة الطولية لتظهر انتقال السيارة على الطريق. أما العدد ذو الفاصلة فهو لتسجيل سرعة حركة السيارة (على المحور Y طبعاً، بما أن الطريق شاقولية) وبالتالي لنعرف السرعة المناسبة لنحرك الطريق بها، وله قيمة افتراضية تتزايد فيما بعد لزيادة صعوبة اللعبة.
سنقوم بعمل إجراء جديد باسم StartGame يقوم بضبط المتغيرات المختلفة إلى قيمها الافتراضية، وسنستخدم الإجراء نفسه عندما يريد اللاعب إعادة اللعبة من جديد، سنضيف الإجراء إلى نفس الملف الافتراضي، Game1.cs:
protected void StartGame()
{
mRoadY[0] = 0;
mRoadY[1] = mRoadY[0] + -mRoad.Height + 2;
mRoadY[2] = mRoadY[1] + -mRoad.Height + 2; mVelocityY = 0.3F;
}
سنقوم باستدعاء الإجراء السابق عندما تبدأ اللعبة أي سنقوم باستدعائه من الإجراء Initialize وذلك بإضافة:
StartGame();
وعلينا أن نقوم بإضافته بعد base.Initialize() حصراً، لأن علينا قبل استدعاء الإجراء StartGame أن نتأكد أن عنصر الـ Texture2Dالمسمى mRoadY قد تم إعداده وذلك لأننا سنتستخدم قيمة "الطول" لذلك الطريق في هذا الإجراء لتحديد التوضع الصحيح للطريق.
وبعد أن قمنا بإعداد المتغيرات اللازمة، سنقوم بإضافة الكود اللازم لجعل الطريق يتحرك بالفعل، وسنضيف لذلك إجراءً جديداً باسم ScrollRoad :
private void ScrollRoad(GameTime theTime)
{
//Loop the road
for (int aIndex = 0; aIndex < mRoadY.Length; aIndex++)
{
if (mRoadY[aIndex] >= this.Window.ClientBounds.Height)
{
int aLastRoadIndex = aIndex;
for (int aCounter = 0; aCounter <
mRoadY.Length; aCounter++)
{
if (mRoadY[aCounter] < mRoadY[aLastRoadIndex])
{
aLastRoadIndex = aCounter;
}
}
mRoadY[aIndex] =
mRoadY[aLastRoadIndex] -
mRoad.Height + 2;
}
}
//Move the road
for (int aIndex = 0; aIndex < mRoadY.Length;
aIndex++)
{
mRoadY[aIndex] += (int)(mVelocityY *
theTime.ElapsedGameTime.TotalMilliseconds);
}
}
كما تلاحظ، كود C# واضح تماماً يقوم بحسابات بسيطة.
نريد أن نقوم باستدعاء هذا الإجراء في كل مرة يتم فيها تحديث شاشة اللعبة ليتم تغيير وضع الطريق إلى وضعه الجديد ولهذا سنقوم باستدعائه من ضمن الإجراء Update :
ScrollRoad(gameTime);
والآن سنقوم بتعديل سطر كود رسم الطريق ليتم رسم الطريق في إحداثيات متغيرة الترتيب Y بدلاً من وضع ثابت، أي سنقوم بتبديل السطر الموجود في الإجراء Draw :
mSpriteBatch.Draw(mRoad, new Vector2(0,0),Color.White);
بالأسطر التالية :
for (int aIndex = 0; aIndex < mRoadY.Length;
aIndex++)
{
mSpriteBatch.Draw(mRoad, new Vector2(0, mRoadY[aIndex]), Color.White);
}
سنقوم الآن بإضافة السيارة تماماً كما قمنا بإضافة الطريق، سنضيف صورتها (مرفقة أيضاً) إلى المشروع ، وسننشيء عنصرTexture2D لتخزين هذه الصورة، أي أضف التالي :
private Texture2D mCar;
private Rectangle mCarPosition;
لقد قمنا بشرح عنصر الـ Texture2Dمن قبل عندما أضفنا الطريق، وأما الـ Rectangleفهو عنصر في XNA وظيفته تخزين معلومات "مستطيل" ما، وهي البعدين (الطول والعرض) والموقع (إحداثيا الزاوية اليسرى العليا منه) وسنستخدمه في لعبتنا للتحكم بحجم السيارة وموقعها.
سنقوم الآن بإعداد العناصر الجديدة، أضف السطر التالي إلى الإجراء StartGame لإعداد موقع العنصر :
mCarPosition = new Rectangle(280, 440,
(int)(mCar.Width * 0.2f), (int)(mCar.Height * 0.2f));
وكما هو واضح، قمنا بتحديد إحداثيات موقع السيارة (280, 440) ثم أخذ خمس (5/1) بعديها كحجم.
لنقوم بإعداد عنصر الـTexture2D سنقوم كما قمنا مع الطريق، أخذ صورة السيارة من الملف المضاف إلى المشروع :
mCar = content.Load<Texture2D>("Car");
بعد أن قمنا بتحميل الصورة وضبط موقعها، لم يبق إلا رسمها باستخدام العنصر mSpriteBatch الذي عرفناه سابقاً.
من المهم جداً أن تتذكر عند رسم الصور باستخدام عنصر SpriteBatch أن تقوم بالرسم من الخلف إلى الأمام لتظهر الصورة كما تريدها ولا تغطي صورة على صورة أخرى، ففي حالتنا نريد رسم الطريق أولاً ثم رسم السيارة.
أضف السطر التالي إلى إجراء الرسم Draw، وتأكد أن تضعه بعد كود رسم الطريق :
mSpriteBatch.Draw(mCar, mCarPosition, Color.White);
قم بتشغيل لعبتك لتتأكد أن الأمور تسير على ما يرام حتى الآن، يجب أن ترى الآن السيارة ظاهرة بشكل صحيح في الطريق، وتحديداً في أول المسار الأيسر، كالظاهر في هذه الصورة تماماً :
الخطوة التالية هي تحريك السيارة حسب ما يضغطه المستخدم، وسنستخدم لوحة المفاتيح، وسأريك كيف يمكن أن تحصل على مدخلات المستخدم من جهاز تحكم Gamepad جهاز XBOX360.
لنستطيع التعامل مع إدخالات المستخدم سنحتاج إلى تعريف عنصر جديد ومتغير رقمي جديد :
private KeyboardState mPreviousKeyboardState;
private int mMoveCarX;
سبق أن لمحنا إلى عنصر KeyboardState، وهو عنصر مقدم من XNA لتخزين حالة لوحة المفاتيح لكي نعرف أي الأزرار مضغوطة أو غير مضغوطة من قبل المستخدم في وقت ما، العنصر الذي قمنا بتعريفه هو لتخزين الحالة السابقة للوحة المفاتيح لكي نعرف إن كان الزر الذي يقوم المستخدم بضغطه الآن قد كان مضغطوطاً في المرة السابقة التي قمنا فيها بفحص الحالة، وهو ما سيجبر المستخدم على ترك الزر ثم ضغطه من جديد للقيام بالعملية مرة أخرى، ففي لعبتنا، نريد عندما يضغط المستخدم زر Space أن تنتقل السيارة من مسارها إلى المسار الآخر فقط، حتى لو استمر ضغط المستخدم للزر، ثم إن أراد الانتقال إلى المسار الآخر مرة ثانية عليه أن يترك ضغط الزر ثم يعيد ضغطه من جديد، أي أننا لا نريد أن تستمر السيارة بالانتقال من مسار إلى آخر مع استمرار المستخدم بضغط الزر نفسه.
أما المتغير الرقمي فهو لتحديد المقدار الذي ستتحرك به السيارة عرضياً (على المحور X) عند الانتقال من مسار إلى آخر، وسنعين قيمته الافتراضية (160) من داخل الإجراء StartGame :
mMoveCarX = 160;
سنقوم الآن بإضافة بعض الكود إلى الإجراء Update لنقوم بتحديد إن قام المستخدم بضغط زر الانتقال من مسار إلى آخر لنحرك السيارة بالشكل المناسب، وكما قلنا، سنقوم بفحص ضغط الزر Space وسنتأكد أن الزر قد ترك وضغط مرة أخرى للانتقال للمسار الآخر مرة ثانية، وسنقوم بفحص ضغط الزر Escape للخروج من اللعبة، وسنقوم بذلك عن طريق إضافة الكود التالي إلى الإجراء Update :
KeyboardState aCurrentKeyboardState = Keyboard.GetState();
if (aCurrentKeyboardState.IsKeyDown(Keys.Escape) == true)
{
this.Exit();
}
if (aCurrentKeyboardState.IsKeyDown(Keys.Space) == true &&
mPreviousKeyboardState.IsKeyDown(Keys.Space)== false)
{
mCarPosition.X += mMoveCarX;
mMoveCarX *= -1;
}
mPreviousKeyboardState = aCurrentKeyboardState;
في البداية يقوم الكود بتفحص حالة لوحة المفاتيح وتخزينها في متغير مؤقت يسمى aCurrentKeyboardState، ثم سنقوم بفحص إن كانت الأزرار التي تهمنا (Space و Escape) قد ضغطت وذلك باستخدام الإجراء المسمى IsKeyDown والذي يأخذ وسيطاً هو الزر المطلوب فحصه.
إن كان المستخدم قد ضغط زر Space لأول مرة فسنقوم بتغيير فاصلة السيارة (X السيارة) بالمقدار mMoveCarX الموجب بشكل افتراضي، ثم سنقوم بتغيير قيمته بضربها بالعدد (1-) لتصبح سالبة لكي تنتقل السيارة بالاتجاه المعاكس عندما نحركها المرة التالية. وفي النهاية، نقوم بتخزين حالة لوحة المفاتيح الحالية لنفحصها في المرة التالية التي نتأكد فيها من الحالة.
قم بتجربة اللعبة للتأكد أن السيارة تتحرك وتنتقل من مسار إلى آخر كما ينبغي.
بالنسبة لـGamepad جهاز XBOX360، يمكنك التعامل معها بشكل مشابه جداً للطريقة التي استخدمناها سابقاً مع لوحة المفاتيح، سواءً كنت تستعملها على الكمبيوتر الشخصي أو مع XBOX360، فيمكنك وصل واستعمال الـGamepad الخاصة بـXBOX360 على الكمبيوتر الشخصي سواءً سلكياً أو لاسليكاً عن طريق منفذ USB، وهذا مدعوم بشكل كامل من XNA. سأريك كيف يمكنك التعامل معها في لعبتنا :
بشكل مشابه لعنصر "حالة لوحة المفاتيح" KeyboardState الذي استعملناه سابقاً، سنقوم بتعريف عنصر لحالة الـGamepad وهي يسمى GamePadState :
private GamePadState mPreviousGamePadState;
ثم ستقوم بفحص إن كان الزر X في الـGamepad مضغوطاً أو لا لتقوم بالانتقال إلى المسار الآخر، أو الزر Back مضغوطاً للخروج من اللعبة، سنقوم بتعديل الإجراء Update في اللعبة كما يلي :
if (aCurrentKeyboardState.IsKeyDown(Keys.Escape)== true ||
aCurrentGamePadState.Buttons.Back == ButtonState.Pressed)
{
this.Exit();
}
if
((aCurrentKeyboardState.IsKeyDown(Keys.Space)== true &&
mPreviousKeyboardState.IsKeyDown(Keys.Space)== false) ||
(aCurrentGamePadState.Buttons.X == ButtonState.Pressed &&
mPreviousGamePadState.Buttons.X == ButtonState.Released))
{
mCarPosition.X += mMoveCarX;
mMoveCarX *= -1;
}
mPreviousKeyboardState = aCurrentKeyboardState;
mPreviousGamePadState = aCurrentGamePadState;
أعتقد أن الكود مألوف بالنسبة لك، لكي تتحرك السيارة يجب أن يكون الزر Space مضغوطاً على لوحة المفاتيح أو الزر X مضغوطاً على الـGamepad ولأول مرة، وللخروج من اللعبة يجب أن يكون الزر Escape مضغوطاً على لوحة المفاتيح أو الزر Back مضغوطاً على الـGamepad، ثم تخزين حالة كل من لوحة المفاتيح والـGamepad للتحقق منها فيما بعد.
العنصر الأخير من عناصر اللعبة هي الحواجز التي على السيارة تجاوزها، سنقوم في البداية بالخطوة التقليدية، إضافة صورة الحاجز إلى المشروع ثم إضافة عنصر الـTexture2D لتخزينها فيه :
private Texture2D mHazard;
private Rectangle mHazardPosition;
المستطيل Rectangle وظيفته كما مع السيارة، تخزين موقع الحاجز وحجمه.
ثم سنقوم بإعداد الموقع والحجم الافتراضي للحاجز من الإجراء StartGame :
mHazardPosition = new Rectangle(275, 0,
(int)(mHazard.Width * 0.4f),
(int)(mHazard.Height * 0.4f));
ثم تحميل ملف الصورة إلى الـTexture، من الإجراء LoadGraphicsContent :
mHazard = content.Load<Texture2D>("Hazard");
وأخيراً، رسم الحاجز على الشاشة، من الإجراء Draw :
mSpriteBatch.Draw(mHazard, mHazardPosition, Color.White);
وتذكر أن تضع الكود السابق قبل SpriteBatch.End() ولكن بعد أن تقوم برسم الطريق والسيارة لكي تظهر الصور بالترتيب الصحيح.
إن قمت بتجريب المشروع الآن فيجب أن يظهر الحاجز في أعلى المسار الأيسر، ولكن دون أن يتحرك، رغم أن الطريق متحرك! سنصلح ذلك عن طريق جعل الحاجز يتحرك طولياً مع تحرك الطريق، ثم إذا وصل إلى أسفل نافذة اللعبة سيظهر مرة أخرى في الأعلى عشوائياً على اليمين أو على اليسار، ولذلك سنستخدم عنصر العشوائية :
private Random mRandom = new Random();
من النصائح المفيدة أن تقوم بتعريف عنصر عشوائية واحد في ألعابك ثم استخدامه في كامل اللعبة، وذلك سيزيد احتمال العشوائية التي ترغبها، فتعريف أكثر من عنصر قد يؤدي عند استدعائهما معاً أن يعطيا الرقم نفسه لأول مرة.
سنجعل ظهور الحاجز أجمل عن طريق جعله غير ظاهر عند بدء اللعبة ليظهر بعد لحظات مع تحرك الطريق، بدلاً من الوضع الحالي والذي يبدأ فيه في موقع محدد وظاهر على الطريق عند بداية اللعبة.
سنقوم بتغيير كود تغيير موقع الحاجزmHazardPosition الموجود في الإجراء StartGame ليصبح :
mHazardPosition = new Rectangle(275,
-mHazard.Height,
(int)(mHazard.Width * 0.4f),
(int)(mHazard.Height * 0.4f));
ثم سنقوم بإضافة إجراء جديد باسم UpdateHazard لتحريك الحاجز :
private void UpdateHazard(GameTime theTime)
{
mHazardPosition.Y += (int)(mVelocityY *
theTime.ElapsedGameTime.TotalMilliseconds);
if (mHazardPosition.Y >
this.Window.ClientBounds.Height)
{
mHazardPosition.X = 275;
if (mRandom.Next(1, 3) == 2)
{
mHazardPosition.X = 440;
}
mHazardPosition.Y = -mHazard.Height;
}
}
يقوم الكود السابق بتحريك الحاجز والتأكد أنه لم يتجاوز حدود الشاشة السفلى وبالتالي أصبح غير ظاهر، وإن تجاوزها يعود للظهور أعلى الشاشة، وعلى المسار الأيمن أو الأيسر حسب القيمة العشوائية التي يعطيها العنصر mRandom.
لكي يتم تنفيذ هذا الإجراء (UpdateHazard)، يجب أن نقوم باستدعائه من داخل الإجراء Update بعد أن يتم تحريك الطريق :
UpdateHazard(gameTime);
يمكنك الآن أن تقوم بتجريب المشروع والاطمئنان أن الحاجز يتحرك ثم يختفي ثم يظهر من جديد كما نريد له تماماً. ولكنك ستلاحظ أن السيارة تخترق الحواجز دون أن يخسر اللاعب، وهو ما سنصلحه فيما بعد.
الخطوة التالية هي حساب عدد الحواجز التي استطاع اللاعب أن يتجاوزها بنجاح ثم إظهارها على الشاشة، ومن حسن الحظ أن XNA يقدم وسيلة سهلة للتعامل مع النصوص وإظهارها عن طريق ما يسمى Sprite Fonts.
سنقوم بإضافة ملف Sprite Font جديد عن طريق الضغط على اسم المشروع بزر الماوس الأيمن ثم سنختار الأمر "Add New Item"، ثم سنختار نوع العنصر الجديد كـ Sprite Font وسنعطيه اسماً مثل myFont.
سيضيف هذا ملف Sprite Font إلى المشروع، وهو ملف XML نضع فيه صفات الـFont الذي نريد إظهار الكتابة باستخدامه، ففي حالتنا، نريد استخدام Font من نوع Courier New وحجم 18 وتباعد (Spacing) 2 ومظهر (Style) عادي Regular، أي سنقوم بتعديل بعض الأسطر لتصبح :
<FontName>Courier New</FontName>
<Size>18</Size>
<Spacing>2</Spacing>
<Style>Regular</Style>
وبهذا نكون قد أضفنا Font وحددنا كيف نريد له أن يبدو، سنقوم بالعودة إلى الملف Game1.cs وتعريف عنصر ليحمل ملف الـFont لاستخدامه في رسم النصوص على الشاشة، ومتغير عددي صحيح ليخزن عدد الحواجز التي استطاع اللاعب تفاديها :
private int mHazardsPassed;
private SpriteFont mFont;
يجب عند بداية اللعبة أن نقوم بإعادة عدد الحواجز التي تجاوزها اللاعب إلى الصفر، أي من الإجراء StartGame سنقوم بـ:
mHazardsPassed = 0;
ثم سنقوم بتحميل ملف الـFont إلى عنصر الـ SpriteFontالذي قمنا بتعريفه، وذلك من الإجراء LoadGraphicsContent :
mFont = content.Load<SpriteFont>("myFont");
سنقوم بزيادة عدد الحواجز التي تجاوزها اللاعب في كل مرة يصل الحاجز إلى أسفل الشاشة بعد أن تفاداه اللاعب بنجاح، لذلك سنقوم بتعديل الإجراء UpdateHazard الذي عرفناه سابقاً ليقوم بذلك، وليقوم عندها بزيادة سرعة حركة السيارة.
أضف السطرين التاليين إلى الإجراء UpdateHazard داخل عبارة If التي تتحقق إن كان الحاجز قد وصل أسفل الشاشة :
mHazardsPassed += 1;
mVelocityY += 0.1F;
ثم سنظهر عدد الحواجز المتفاداة على الشاشة من الإجراء Draw :
mSpriteBatch.DrawString(mFont,
"Hazards: " + mHazardsPassed.ToString(),
new Vector2(5, 25), Color.White, 0, new
Vector2(0, 0),
1.0f, SpriteEffects.None, 0);
يقوم السطر السابق برسم نص باستخدام الـFont الذي قمنا بتعريفه، وباستخدام بعض المعلومات الممرة إلى الإجراء DrawString.
إن قمت بتشغيل اللعبة فيجب أن تشاهد عبارة تخبرك بعدد الحواجز المتجاوزة بمجرد أن تصل تلك الحواجز إلى أسفل الشاشة.
عاجلاً أو آجلاً ستصل في أي لعبة إلى المرحلة التي تدرك بها أن على اللعبة أن تظهر شيء في أوقات معينة دون أوقات أخرى، أي في "حالة" معينة دون "حالة" أخرى، فإن تحدثنا عن لعبتنا فإننا نواجه، الحالة الأولى هي حالة اللعب، أي أثناء استمرار اللاعب باللعبة بنجاح، والحالة الثانية هي حالة الاصطدام أو الخسارة، عندما يفشل اللاعب بتجاوز حاجز ما ويصطدم به.
الطريقة الأمثل لتعريف جميع الحالات الممكنة في لعبة هي وضعها ضمن Enum نضع فيه جميع الأوضاع، وهو بسيط جداً في لعبتنا ولكنه لن يكون كذلك في ألعاب أخرى أكثر تعقيداً :
private enum State
{
Running,
Crash
}
private State mCurrentState;
قمنا بتعريف جميع الأوضاع الممكنة ثم قمنا بتعريف المتغير mCurrentState لنخزن فيه الوضع الحالي.
يجب أن نقوم بإعداد القيمة الأولية لهذا المتغير مع بداية اللعبة، أي من الإجراء StartGame :
mCurrentState = State.Running;
ثم سنقوم بتعديل الإجراء Update لكي يصبح ما يقوم به مختلفاً حسب الوضع الحالي للعبة، مازلنا نريد الخروج من اللعبة عند ضغط زر الخروج (Escape أو Back) مهما كانت الحالة، وأما زر الحركة (Space أو X) فنريده أن يقوم بالحركة فقط عندما تكون اللعبة في وضع اللعب.
switch (mCurrentState)
{
case State.Running:
{
if
((aCurrentKeyboardState.IsKeyDown(Keys.Space)== true &&
mPreviousKeyboardState.IsKeyDown(Keys.Space)== false) ||
(aCurrentGamePadState.Buttons.X == ButtonState.Pressed &&
mPreviousGamePadState.Buttons.X == ButtonState.Released))
{
mCarPosition.X += mMoveCarX;
mMoveCarX *= -1;
}
ScrollRoad(gameTime);
UpdateHazard(gameTime);
break;
}
case State.Crash:
{
break;
}
}
قمنا بإضافة عبارة Switch تقوم بفصح حالة اللعبة ثم القيام بشيء مختلف حسب كل حالة، لاحظ أننا نقلنا كود التحقق من الزر Space أو X ليصبح ضمن حالة اللعب فقط (case State.Running).
حتى الآن ما زالت اللعبة لا تقوم بشيء في حالة التصادم، وهي أصلاً لا تستطيع معرفة إن حدث تصادم! هذا ما سنغيره حالاً، بعد أن نقوم بتشغيل اللعبة لنتأكد أنها مازالت تعمل كما كانت بعد أن قمنا بتلك التعديلات.
بقي أن نقوم بالمهمة الأخيرة لتصبح اللعبة جاهزة، وهي تحديد التصادم بين السيارة والحواجز ليخسر اللاعب، وهو شيء بسيط جداً لأن XNA يقدم إجراءً جاهزاً من إجراءات عنصر المستطيل يسمى Intersects يخبرنا إن كان المستطيل يتقاطع مع مستطيل آخر، فإن تقاطع مستطيل السيارة مع مستطيل الحاجز فهذا معناه أن السيارة قد اصطدمت به، وإن وجدنا أن هناك تصادماً فسنغير حالة اللعبة إلى "اصطدام" Crash، لتنتهي عندها اللعبة، وسنقوم بذلك من الإجراء Update، استبدل كود استدعاء الإجراء UpdateHazard الموجود في الإجراء Update ليصبح :
if (mHazardPosition.Intersects(mCarPosition) ==
false)
{
UpdateHazard(gameTime);
}
else
{
mCurrentState = State.Crash;
}
عند انتهاء اللعبة يجب أن نسمح للاعب أن يعيد اللعبة من جديد أو يخرج من اللعبة، وسنظهر له عبارة تخبره أن عليه أن يضغط Enter ليلعب من جديد، أو أن يضغط Escape ليخرج من اللعبة، لذلك سنضيف الأسطر التالية إلى عبارة الـ switchالخاصة بحالة التصادم (case State.Crash) :
if (aCurrentKeyboardState.IsKeyDown(Keys.Enter) ==
true || aCurrentGamePadState.Buttons.Start
== ButtonState.Pressed)
{
StartGame();
}
أي إن كانت اللعبة في حالة التوقف وضغط المستخدم زر Enter يتم استدعاء الإجراء StartGame لإعادة اللعبة من جديد.
لم يبق إلا أن نظهر بعض العبارات التي تظهر عندما يخسر اللاعب لتخبره بما يستطيع أن يقوم به، لذلك سنقوم بتعديل الإجراء Draw ونضيف الأسطر التالية بعد السطر الذي يظهر عدد الحواجز التي استطاع اللاعب تجاوزها :
if (mCurrentState == State.Crash)
{
mSpriteBatch.DrawString(mFont, "Crash!",
new Vector2(5, 200), Color.White, 0,
new Vector2(0, 0), 1.0f,
SpriteEffects.None, 0);
mSpriteBatch.DrawString(mFont,
"'Enter' to play again.",
new Vector2(5, 230),
Color.White, 0, new Vector2(0, 0), 1.0f,
SpriteEffects.None, 0);
mSpriteBatch.DrawString(mFont,
"'Escape' to exit.",
new Vector2(5, 260),
Color.White, 0, new Vector2(0, 0), 1.0f,
SpriteEffects.None, 0);
}
وهو كود يظهر بعض النصوص، تماماً بنفس الطريقة التي أظهرنا فيها عدد الحواجز التي استطاع اللاعب تجاوزها، وتظهر هذه النصوص فقط عند التأكد من أن اللعبة قد انتهت، أي في وضع التصادم، لتخبر المستخدم بالخيارات المتوافرة.
لقد انتهينا! يمكنك الآن تشغيل لعبتك والاستمتاع بإنجازك!
أعتقد أنك تتفق معي الآن أن XNA هو أحد الحلول الرائعة لأي مطور ألعاب سواءً كان محترفاً أم هاوياً، فهو سهل التعلم، سهل التعامل معه، مجاني (أو بكلفة منخفضة جداً)، وله العديد من المصادر ومنتديات دعم قوية جداً (منتديات موقع XNA.com)، أضف إلى ذلك عشرات المحركات المجانية والمفتوحة المصدر المبنية عليه، كمحركات الفيزياء، محركات الجزيئات Particles، وغيرها (والكثير من تلك المحركات موجود على www.codeplex.com)، ولذلك فإن XNA يعد بلاشك مدخل ذهبي إلى عالم برمجة الألعاب للمبتدئين الذين يريدون الدخول إلى هذا العالم..
والآن لم يبق إلا أن أودعك بعد أن أعطيك قائمة بأهم المواقع التي تتحدث عن XNA إن أردت المزيد من المعلومات :
المصادر و المراجع :
وأما الكود وملفات الصور المرفقة مع المقالة للاستخدام فهي من هذا الرابط.