إنشاء مشروع (Xcode) Mac OS : SDL
الأيقونات في Mac OSهي بصيغة . .icnsإن استعملت صيغة أخرى فستلاحظ أن الأيقونة لا تظهر.
إن أردت تحو يل صيغة صورة عادية إلى أيقونة استعمل برنامج .Icon Composerالمتواجد في المجلد
، /Developer/Applications/Utilitiesيكفي أن تسحب الأيقونة إلى المربع المخصص لها
ثم تحفظها.
في المنطقة Infoيمكننا أن نشير إلى العديد من المعلومات في البرنامج ،يمكنك الإطلاع عليها أكثر من خلال
قراءة الملفات التوثيقية الخاصة بـ .Appleالشيء الوحيد الذي يمكنك التعديل عليه هو Localizationمن
enإلى ، frكما يمكنك تعديل الـ Copyrightو وضع ما تريد.
توجّه الآن إلى Build Phasesو انقر على Copy Files / Add Build Phaseفي أسفل يمين
النافذة ،اضغط على Copy Filesو غي ّره إلى . Copy frameworks into appفي Destination
اختار Frameworksلتضيف الخاصة بك ،قم بسحبها من التفرع الشجري اليساري و افلاتها في المنطقة
، Build phaseكما يظهر بالصورة :
أنصحك بترتيب كل الـ frameworksالخاص بك في مجلد Frameworkو هذا لـكي يسهل عليك إيجادها.
و أيضا ًبالنسبة للشفرات المصدر ية ،أنصحك بترتيبها في مجلدات ليسهل الوصول إليها .لإنشاء مجلد انقر باليمين على
الشجرة اليسار ية و اختر New Groupثم اسحب الملفات إلى داخلها.
سنقوم الآن بإضافة الملفات SDLMain.hو ، SDLMain.mتوجه إلى المجلد devel-liteالمفتوح
مسبقا و قم بإضافة الملفين إلى المشروع .إذا ظهرت لك نافذة تحديد خصائص النسخ ،قم باختيار
). Copy items into destination group’s folder (if needed
آخر شيء :أنشئ ملفا . main.cتوجه إلى القائمة New File / New / Fileثم إلى ، C and C++
اختار C Fileثم ” .”Nextقم بتسمية الملف و هاقد أكملت.
301
الفصل ج .1.ٺثبيت الـSDL
ج 5.1.إنشاء مشروع (GNU/Linux) : SDL
لمن يستعملون بيئة تطوير ية من أجل الترجمة ،فعليهم بتغيير خواص المشروع )فالعملية مشابهة لما كنت قد
شرحت( .بالنسبة لمن يستعملون ،Code::Blocksفالطر يقة هي نفسها التي شرحتها سابقا ً.
ماذا عن الذين يقومون بترجمة الشفرات يدو يا ؟
قد يوجد بين القراء من اعتاد على ترجمة الشفرات يدو يا بالاستعانة بـ) Makefileملف يساعد على عملية
الترجمة(.
إذا كانت هذه حالتك ،فأدعوك لتحميل Makefileال ّذي ي ُمكن أن ي ُستخدم لترجمة مشار يع الـ.SDL
http://www.siteduzero.com/uploads/fr/ftp/mateo21/makefile_sdl
الشيء الوحيد المختلف ،هو إضافة المكتبة SDLإلى محر ّر الروابط ) .( LDFLAGSيجدر بك أن تكون قد
نزّلت المكتبة و ثب ّتها في مجل ّد ملفات المترجم ،بنفس طر يقة الويندوز )المجلدان include/SDLو .( lib
بعد ذلك يجب عليك أن تكتب الأوامر التالية في الـكونسول :
make # To compile the project
make clean )# To delete compilation files (useless .o files
make mrproper # To delete all files except source ones
مل ّخص
• الـ SDLمكتبة منخفضة المستوى ،تسمح بإنشاء نوافذ و التعامل مع الرسوميات .2D
• المكتبة ليست مس ّطبة تلقائيا في الحاسوب ،يجب عليك أن تنزّلها بنفسك و تقوم بتخصيص البيئة التطوير ية
لتعمل معها.
• المكتبة حرة و مج ّانية ،مما يسمح باستعمالها السر يع و الدائم.
• توجد آلاف المكتبات الأخرى ،و كثير منها ذو جودة عالية جدا ً .و قد تم اختيار المكتبة SDLلبقي ّة
هذا الدرس لأجل سهولتها .لمن يريد بناء واجهات رسومية بنوافذ ،أزرار و قوائم ،فأنا أنصحه بالمكتبة
GTK+مثلا.
302
الفصل ج2.
إنشاء نافذة و مساحات
في الدرس السابق ،قمنا بالإلمام حول أهم المميزات التي تمنحها المكتبة .SDLيجدر بك أن تكون قد ثب ّت
المكتبة ،و تعل ّمت كيفية إنشاء مشروع جديد يشتغل بشكل جيد .على الرغم من أن ّه كان فارغا.
سندخل في مضمون موضوعنا في هذا الفصل .سنقوم بتطبيق أساسيات لغة الـ Cمع .SDLكيف يتم تحميل
الـ SDL؟ كيف يتم فتح نافذة بالأبعاد التي نريد ؟ كيف نرسم داخل النافذة ؟
لدينا أمور كثيرة لنعرفها ،فهي ّا بنا !
ج 1.2.تحميل و إيقاف الـSDL
العديد من المكتبات المكتوبة بلغة الـ ،Cتستلزم أن يتم تحميلها ثم غلقها حين ننتهي منها ،و ذلك لاستعمال
دوال محددة .المكتبة SDLمن بين هذه المكتبات.
بالفعل ،فالمكتبة تحتاج أن يتم تحميل عدد معي ّن من المعلومات إلى الذاكرة العشوائية لتستطيع أن تشتغل
بشكل صحيح .يتم هذا التحميل بشكل حيّ باستعمال الدالة ) mallocإ ّنها مهمّة ج ّدا هنا !( .و كما تعلم فإن
قلت ، mallocسأقول كذلك ! free
يجب عليك تحرير الذاكرة التي حجزتها و لم تعد بحاجة إليها .إن لم تفعل ،فالبرنامج يمكن أن يأخذ حيّزا ً كبيرا ً من
الذاكرة بدون فائدة ،و يمكن لذلك أحيانا أن يدرّ بنتائج كارثية .تخي ّل القيام بحلقة غير منتهية من malloc
دون قصد ،في بضع ثوان ستس ّد ك ّل الذاكرة !
هاهما الدالتان الأولتان الخا ّصتان بالـ SDLاللتان يجب عليك أن تعرفهما :
• : SDL_Initتحميل المكتبة في الذاكرة العشوائية )باستخدام الـ .( malloc
• : SDL_Quitتحرير المكتبة من الذاكرة )باستعمال الـ .( free
أي أن أ ّول شيء يجب أن تقوم به في البرنامج هو استدعاء ، SDL_Initو آخر شيء هو استدعاء
. SDL_Quit
303
الفصل ج .2.إنشاء نافذة و مساحات
: SDL_Initتحميل المكتبة SDL
الدالة SDL_Initتستقبل معاملا .إذ يجب أن يتم تحديد أي جزء من المكتبة نريد تحميله.
آه ح ّقا ! هل الـ SDLٺتكون من كثير من الأجزاء ؟
نعم بالطبع ! فهناك جزء من المكتبة يتعامل مع الشاشة ،و آخر يتعامل مع الصوت ،إلخ.
توف ّر لنا المكتبة عددا ً من الثوابت التي تسمح لنا بتحديد اسم الجزء الذي نريد تحميله من المكتبة.
الشرح الثابت
تحميل الجزء الخاص بالعرض )الفيديو( ،إنه الجزء الذي نحمله غالبا ً. SDL_INIT_VIDEO
تحميل الجزء الخاص بالصوت ،هذا ما يسمح لك مثلا بتشغيل الموسيقى مثلا. SDL_INIT_AUDIO
SDL_INIT_CDROM
تحميل الجزء الخاص بقارئ القرص المضغوط ،و ذلك للتحكم به. SDL_INIT_JOYSTICK
تحميل الجزء الخاص بجهاز التحكم .Joystick
SDL_INIT_EVERYTHINGتحميل كل الأجزاء التي ذكرتها سابقا.
إذا استدعيت الدالة بهذا الشكل
;)1 SDL_Init(SDL_INIT_VIDEO
فإن نظام العرض سيتم تحميله في الذاكرة ،فيمكنك أن تفتح نافذة و ترسم فيها ،إلخ.
كل ما قمنا به هو إعطاء عدد إلى الدالة SDL_Initبالاستعانة بثابت .أنت لا تعرف أي عدد هو ،و هذا أمر
جيد .إذ أنك غير مجـبر على حفظ العدد ،بل التعبير عنه باسم الثابت فقط.
الدالة SDL_Initتقرأ العدد و هكذا تحدد الأنظمة الواجب تحميلها.
الآن لو تكتب :
;)1 SDL_Init(SDL_INIT_EVERYTHING
ستقوم بتحميل كل أنظمة الـ ،SDLلا تقم بهذا إلا في حالة كنت بالفعل تحتاج إلى ك ّل شيء ،ليس جيدا ً
إثقال الحاسوب بوحدات لا فائدة منها.
ماذا لو أردت تحميل الصوت و الفيديو فقط .هل يجدر بي استخدام SDL_INIT_EVERYTHING؟
لن تستعمل ، SDL_INIT_EVERYTHINGمن أجل تحميل وحدتين ،هذا جنون ! لحسن الحظ ،يمكننا
تجميع الخيارات بواسطة الرمز | .
304
تحميل و إيقاف الـSDL
1 // Loading the video and the audio
;)2 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO
كما يمكنك وضع ثلاثة دون مشاكل :
1 // Loading the video, the audio and the timer
;)2 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER
هذه ”الخيارات” التي نبعثها للدالة SDL_Initنسميها بـالأعلام ) .(flagsهذه الكلمة نستعملها كثيرا ً
في علوم الحاسوب.
تذكّر إذا أن الإشارة | خاصة بدمج الخيارات .إنها تشبه الإضافة إلى ح ّد ما.
: SDL_Quitإيقاف المكتبة SDL
هذه الدالة سهلة الاستعمال لأنها لا تحتاج إلى أي معامل :
;)(1 SDL_Quit
كل الأنظمة سيتم إيقافها و يتم تحرير الذاكرة.
باختصار ،هذه الدالة أداة للخروج من المكتبة بشكل نظيف ،و لنقل للخروج من برنامجك.
نموذج عن برنامج SDL
باختصار ،هذا ما يبدو عليه برنامج SDLفي نسخته الأبسط :
>1 #include <stdlib.h
>2 #include <stdio.h
>3 #include <SDL/SDL.h
4 )][int main(int argc, char *argv
5
{
6 SDL_Init(SDL_INIT_VIDEO); // Starting the SDL (Here we load
7 )the video system
8 SDL_Quit(); // Stopping the SDL (Freeing the memory).
}9 ;return 0
هذا نموذج عن برنامج بسيط ،عبارة عن مخطط لبرامج SDLالتي نكتبها .في الواقع ،البرنامج الحقيقي يكون
ممتلأ كثيرا إذ يحتوي ع ّدة استدعاءات لدوال ،تقوم بدورها بمزيد من الاستدعاءات.
الأمر المه ّم في النهاية ،هو أ ّن SDLيجب أن ت ُحم ّل في البداية و ت ُغلق عندما لا تصبح بحاجة إليها.
305
الفصل ج .2.إنشاء نافذة و مساحات
معالجة الأخطاء
الدالة SDL_Initتقوم بإرجاع قيمة :
• : 1-في حال وجود خطأ.
• : 0في حالة عدم وجود أي خطأ.
لست مجـبرا ً ،لـكن يمكنك اختبار القيمة الم ُرجعة .قد تكون طر يقة جي ّدة لمعالجة الأخطاء في برنامجك ،و
هذا ما سيساعدك على حل ّها.
لـكن كيف أقوم بإظهار الخطأ الحادث ؟
سؤال وجيه ! ليس لدينا كونسول الآن ،كيف نخز ّن و نعرض رسائل الخطأ ؟
هناك حل ّان :
• يمكننا التعديل على خاصيات المشروع ،لـكي نسمح له باستعمال الـكونسول أيضا ً .سنتم ّكن في هذه الحالة
من استخدام الدالة ،( printf
• أو نكتب الأخطاء في ملف .تستخدم الدالة . fprintf
لقد اخترت أن نكتب في ملف .و بهذا فإن العمل على ملف يحتاج إلى فتح هذا الأخير بـ fopenو غلقه
بـ ، fcloseو الأمر أق ّل سهولة من استعمال الـ . printf
لحسن الحظ ،هناك طر يقة أسهل و هي استعمال مخرج الأخطاء القياسي.
يوجد متغير stderrمعر ّف في stdio.hيقوم بالتأشير نحو المنطقة التي ي ُمكن أن ي ُكتب فيها الخطأ.
غالبا في الويندوز ،هذه المنطقة عبارة عن ملف يحمل الاسم . stderr.txtبينما في اللينكس فإن الأخطاء
غالبا ًما يتم إظهارها على الـكونسول .هذا المتغير يتم إنشاؤه تلقائيّا في بداية البرنامج و يتم حذفه في نهايته ،أي أنك
لست مجـبرا ً على استعمال fopenو . fclose
يمكنك استعمال الدالة fprintfعلى stderrبدون استعمال fopenو : fclose
>1 #include <stdlib.h
>2 #include <stdio.h
>3 #include <SDL/SDL.h
4 )][int main(int argc, char *argv
5
{
6 if (SDL_Init(SDL_INIT_VIDEO) == −1) // Starting the SDL, if
there’s an error :
{7
306
فتح نافذة
8 ”fprintf(stderr, ”Error while initializing SDL : %s\n
,
9
10 SDL_GetError()); // Writing the error
11 exit(EXIT_FAILURE); // We exit the program
12 }
13 ;)(SDL_Quit
} 14 ;return EXIT_SUCCESS
ما الجديد في هذه الشفرة المصدر ية ؟
• لقد كتبنا الخطأ الذي وجدناه في . stderrالرمز %sيسمح للـ SDLبالإشارة إلى تفاصيل الخطأ :
الدالة SDL_GetErrorفي الحقيقة تقوم بإرجاع آخر خطأ .SDL
• نخرج باستعمال الـ )( . exitلح ّد الآن لا يوجد شيء جديد مقارنة بما جرت العادة ،ستلاحظ أنني
استعمل الثابت EXIT_FAILUREكقيمة يقوم البرنامج الرئيسي بإرجاعها ،بينما استعملت في النهاية
الثابت EXIT_SUCCESSفي مكان الـ.0
ما الذي قمت به ؟ لقد قمت بتحسين الطر يقة التي تعودنا أن نكتب بها الشفرة .لقد استخدمت اسم الثابت
ال ّذي يعني ”خطأ” و الذي هو نفسه بالنسبة لجميع أنظمة التشغيل .بينما الأعداد تختلف من نظام إلى آخر.
لهذا فإن الملف stdlib.hتسمح باستعمال ثابتتين )معر ّفي : ( #define
– : EXIT_FAILUREقيمة يتم إرجاعها في حالة وجود خطأ ما في البرنامج.
– : EXIT_SUCCESSقيمة يتم إرجاعها في حالة عدم وجود أي خطأ.
باستعمال أسماء الثوابت بدلا ًمن قيمها ،ستضمن بأنك قد بعثت القيمة الصحيحة.
لماذا ؟ لأن الملف stdlib.hيتغيّر حسب نظام التشغيل ال ّذي أنت عليه ،لذا فقيم الثوابت ستتأقلم
مع النظام من دون أ ّن نحتاج إلى تغيير شيء ! و هذا ما يجعل لغة الـ Cمتوافقة مع ك ّل أنظمة التشغيل
)بافتراض أن ّك تبرمج بالطر يقة الصحيحة باستخدام الأدوات المتوف ّرة ،كما فعلنا هنا(.
استعمال أسماء الثوابت لا يعود علينا بكثير من النفع الآن ،لـكن من الأحسن استعمالها .سنقوم
بذلك انطلاقا ًمن الآن.
ج 2.2.فتح نافذة
حسنا ً ،لقد تم فتح و غلق المكتبة بنجاح .الخطوة التالية التي يريد تعل ّمها الجميع هي كيفية فتح نافذة !
لـكي نبدأ ،تأكد من أن لديك mainتشبه هذه :
307
الفصل ج .2.إنشاء نافذة و مساحات
1 )][int main(int argc, char *argv
2
{
)3 if (SDL_Init(SDL_INIT_VIDEO) == −1
{4
;)”5 fprintf(stderr, ”Error while initializing SDL
;)6 exit(EXIT_FAILURE
}7
;)(8 SDL_Quit
;9 return EXIT_SUCCESS
} 10
هذه هي حالتك لو اتبعت جيدا ً من بداية الفصل .حالي ّا ،ك ّل ما سنقوم بتحميله هو نظام العرض
) ،( SDL_INIT_VIDEOهذا ما يهمّنا.
إختيار وضع العرض
أول شيء نقوم به بعد ، SDL_Initهو تحديد وضع العرض الذي نريد استعماله ،أي الدقة )،(resolution
عدد الألوان بالإضافة إلى خصائص أخرى.
من أجل هذا سنستعمل الدالة SDL_SetVideoModeالتي تستقبل 4معاملات :
• عرض النافذة التي نريدها )،(pixels
• طول النافذة التي نريدها )،(pixels
• عدد الألوان القابلة للعرض )،(bits/pixel
• الخيارات )الأعلام(.
لا أعتقد أن طول و عرض النافذة يحتاجان إلى شرح ،بينما عدد الألوان و الأعلام هما المعاملان الأكثر
أهمي ّة.
• عدد الألوان :هو العدد الأقصى للألوان التي يمكن أن تظهر في النافذة .إن كنت من عشاق ألعاب
الفيديو ،ستكون معتادا ً على هذا الأمر .فإن قيمة 32 bits/pixelتسمح بإظهار ملايير الألوان ،بينما إنه
من الممكن أن نختار قيمة أقل كـ) 16 bits/pixelتسمح بعرض 65536لون( أو حتى 8 bits/pixel
)تسمح بعرض 256لون مختلف( ،هذا الأمر مفيد حينما تريد برمجة تطبيقات من أجل جهاز بسيط
كالـ PDAأو الهاتف المحمول.
• الخيارات :تماما مثل الـ ، SDL_Initعلينا باستعمال أعلام من أجل تعر يف خصائص .هذه أهم
الأعلام التي يمكنك استعمالها )يمكنك استعمال العديد منها ،يتم التفر يق بينها باستعمال الرمز | ( :
308
فتح نافذة
– : SDL_HWSURFACEالمعطيات سيتم حفظها في في الذاكرة الرسومية للبطاقة .3Dالشيء الجيد :
إنها الذاكرة الأكثر سرعة .الشيء الس ّيء :يصعب إيجاد مساحة شاغرة في هذا النوع من الذاكرة
مقارنة بالأخرى ) .( SDL_SWSURFACE
– : SDL_SWSURFACEالمعطيات يتم حفظها في ذاكرة النظام )أي في الـ ،(RAMالشيء الجيد :
يوجد الـكثير من المكان في هذه الذاكرة .الشيء السيء :أقل سرعة و أقل كفاءة.
– : SDL_RESIZABLEستصبح مقاييس أبعاد النافذة قابلة للتعديل ،لأنها ليست كذلك تلقائيا.
– : SDL_NOFRAMEلن يصبح للنافذة أية حواش أو شر يط علوي لكتابة عنوان النافذة.
– : SDL_FULLSCREENنمط الشاشة الكاملة .لن تستطيع رؤ ية أية نافذة أخرى لأن نافذة البرنامج
الحالي تهيمن على ك ّل الشاشة ،مع تعديل دق ّة الشاشة في حالة الضرورة.
– : SDL_DOUBLEBUFوضع ،double bufferingتقنية مستعملة بكثرة في برمجة الألعاب ثنائية
الأبعاد .تقضي بأن يكون تحر ّك الأشياء على الشاشة مرنا ً ،لأنه إن لم يكن كذلك ،سيكون التحر ّك
سيئا ً ،سأشرح هذا الأمر بالتفصيل لاحقا ً.
إذا كتبت الشفرة المصدر ية التالية :
;)1 SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
فإنه سيقوم بفتح نافذة ذات أبعاد ،640 × 480و بعدد ألوان ) 32 bits/pixelملايير الألوان( ،و ستم تحميل
النافذة على الذاكرة الرسومي ّة )إ ّنها الأسرع ،لذلك نف ّضل استعمالها(.
كمثال آخر ،لو نأخذ :
| 1 SDL_SetVideoMode(400, 300, 32, SDL_HWSURFACE | SDL_RESIZABLE
;)SDL_DOUBLEBUF
هذه الشفرة تفتح نافذة مقاييس أبعادها قابلة للتعديل ،بأبعاد ابتدائية ،400×300و بعدد ألوان 32 bits/pixel
كما أن تقنية double bufferingمفعّلة.
هذه أ ّول شفرة مصدر ية بسيطة يمكنك تجريبها :
>1 #include <stdlib.h
>2 #include <stdio.h
>3 #include <SDL/SDL.h
)][4 int main(int argc, char *argv
{5
;)6 SDL_Init(SDL_INIT_VIDEO
;)7 SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
;)(8 SDL_Quit
;9 return EXIT_SUCCESS
} 10
309
الفصل ج .2.إنشاء نافذة و مساحات
لقد اخترت أن أسحب معالجة الأخطاء لتبسيط الشفرة ،لـكن بالنسبة لك ،يجب عليك أن تكتب برامج كاملة
و أخذ ك ّل الاحتياطات اللازمة لمعالجة الأخطاء.
قم بتجريب الشفرة .ما الذي يحصل ؟ تظهر النافذة و تختفي بسرعة البرق.
الحقيقة أن استدعاء الدالة SDL_SetVideoModeيليه مباشرة استدعاء الدالة SDL_Quitالتي تقوم بإنهاء
ك ّل شيء.
توقيف البرنامج للحظات
ما العمل كي تقوم النافذة بالانتظار و لا تختفي مباشرة ؟
يجب أن نفعل ما تفعله جميع البرامج ،سواء كانت ألعابا أو غير ذلك ،حلقة تكرار ية غير منتهية .في الواقع،
بمساعدة حلقة غير منتهية بسيطة سنمنع البرنامج من التوقف .لـكن هذه الطر يقة فعالة ج ّدا لدرجة أن ّه لا يمكننا
إيقاف البرنامج )إلا إذا أجبرناه باستدعاء المعالج ،لـكنّها تبقى طر يقة عنيفة لانهاء عمل برنامج(.
هذه شفرة تعمل ،لـكن ّي أدعوك إلى عدم تجريبها ،أعطيها لك فقط كشرح :
1 )][int main(int argc, char *argv
2
{
;)3 SDL_Init(SDL_INIT_VIDEO
;)4 SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
;)5 while(1
;)(6 SDL_Quit
;7 return EXIT_SUCCESS
}8
أنت تعرف الـ ;) : while(1إ ّنها الحلقة التكرار ية غير المنتهية .بما أن 1يساوي القيمة المنطقية ”صحيح”
)تذكّر المتغيرات المنطقية( ،فإن الشرط صحيح دائما و بالتالي فستدور الحلقة إلى الأبد مع عدم وجود وسيلة
لإيقافها .هذا ليس حل ّا جي ّدا.
لـكي نتمكن من توقيف النافذة كي لا تختفي فجأة بدون اللجوء إلى حلقة غير منتهية ،سنستعمل دالتي ال ّتي
أنشأتها و سميتها : pause
)(1 void pause
{2
;3 int cont = 1
;4 SDL_Event event
)5 while (cont
{6
;)7 SDL_WaitEvent(&event
)8 switch(event.type
{9
310
فتح نافذة
10 case SDL_QUIT:
;11 cont = 0
} 12
} 13
} 14
لن أشرح لك تفاصيل الدالة الآن .فهذه الدال ّة تحتاج إلى ما نسميه بمعالجة الأحداث ال ّتي سأشرحها في
الفصل القادم .فإن شرحت لك ك ّل شيء الآن فقد تختلط عليك الأمور ! ث ِق في دال ّتي الخا ّصة بالتوقيف،
ستتعرف على طر يقة عملها قريبا ً.
هذا مثال عن شفرة مصدر ية كاملة ،يمكنك )أخيرا ً( تجريبها :
>1 #include <stdlib.h
>2 #include <stdio.h
>3 #include <SDL/SDL.h
;)(4 void pause
5 )][int main(int argc, char *argv
6
{
7 SDL_Init(SDL_INIT_VIDEO); // We initialize the SDL
;)8 SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
9 pause(); // We pause the program
10 SDL_Quit(); // We stop the SDL
11 return EXIT_SUCCESS; // We close the program
} 12
)(13 void pause
{ 14
;15 int cont = 1
;16 SDL_Event event
)17 while (cont
{ 18
;)19 SDL_WaitEvent(&event
)20 switch(event.type
{ 21
22 case SDL_QUIT:
;23 cont = 0
} 24
} 25
} 26
ستلاحظ أنني وضعت نموذج الدالة pauseفي أعلى البرنامج كي لا أضطر ّ لعرض أكثر من ملف.
لقد قمت باستدعاء الدالة pauseو هي تقوم بالدخول في حلقة تكرار ية غير منتهية أذكى من السابقة .هذه
الحلقة تنتهي حينما تنقر على الزر الأحمر Xأعلى النافذة !
الصورة التالية هي عبارة عن النافذة التي نحصل عليها حين نترجم الشفرة المصدر ية السابقة )النافذة ذات
أبعاد .(640 × 480
311
الفصل ج .2.إنشاء نافذة و مساحات
ها قد وصلنا !
إن أردت ،قم بوضع الع َلم الذي يسمح بتعديل مقاييس النافذة .للع ِلْم ،في ألعاب الفيديو نف ّضل النوافذ ذات
الأبعاد الثابتة )لأنه يسهل التعامل معها( ،إذا فلنترك النافذة ثابتة كما هي الآن.
إحذر من الع َل َم SDL_FULLSCREENالخاص بوضع الشاشة الكاملة ،و من الع َـلم SDL_NOFRAME
الذي يقوم بإخفاء حواشي النافذة .بما أن ّه لن يكون هناك شر يط للعنوان ،فلن نكون قادرين على الخروج
من البرنامج ،إلا بالاستعانة بالمعالج !
تريّث قليلا ًحتى نتعلّم معالجة الأحداث )في الفصول القادمة( و ستتمكن بعدها من الخروج من النافذة
بطر يقة أقل عنفا ًمن استدعاء المعالج.
تغيير عنوان النافذة
لح ّد الآن ،النافذة أخذت عنوانا تلقائيا )و هو SDL_appفي الصورة السابقة(.
هل تريد تغييره ؟
إن الأمر بسيط للغاية ،يكفي استعمال الدالة . SDL_WM_SetCaption
هذه الدالة تأخذ معاملين :المعامل الأول هو العنوان الذي تريد إعطاءه للنافذة ،و المعامل الثاني هو العنوان
الذي تريد إعطاءه للأيقونة.
312
فتح نافذة
خلافا ً على ما يعتقده الجميع ،تغيير اسم الأيقونة لا يعني تغيير صورة الأيقونة التي تظهر أعلى يسار النافذة.
هذا لا يعمل دائما )حسب معرفتي ،قد يعطي نتائج على الـ GNU/Linuxفي بيئة الـ .(Gnomeشخصيا ً ،أنا أبعث
القيمة NULLإلى الدالة .على أية حال ،يمكننا تغيير شكل الأيقونة التي تظهر أعلى يسار النافذة ،لـكننا سنتعلّم
ذلك في الفصل القادم ،لأ ّن هذا الأمر ليس بمستواك بعد.
هذه نفس الـ mainالسابقة ،مع إضافة الدالة : SDL_WM_SetCaption
1 )][int main(int argc, char *argv
2
{
;)3 SDL_Init(SDL_INIT_VIDEO
;)4 SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
;)5 SDL_WM_SetCaption(”My super SDL window !”, NULL
;)(6 pause
;)(7 SDL_Quit
;8 return EXIT_SUCCESS
}9
لاحظ بأنني استعملت القيمة NULLللمعاملات غير المهمة بشكل كبير .بالنسبة للـ ،Cيجب أن يتم
إعطاء قيم لكل المعاملات التي تستقبلها الدوال ،حتى لو كانت هذه المعاملات غير مهمة لك ،فأعطها
NULLكما فعلت أنا هنا .بينما الـ C++تسمح بألا نعطي أساسا ًقيمة لبعض المعاملات الاختيار ي ّة عندما
نستدعي الدوال.
للنافذة الآن عنوان.
313
الفصل ج .2.إنشاء نافذة و مساحات
ج 3.2.التعامل مع المساحات
لحد الآن تم ّكنا من فتح نافذة ذات خلفي ّة سوداء .ما نريد الآن هو أن نملأها ببعض الأشياء ،أي أن ”نرسم”
فيها.
كما قلت لك في الفصل السابق ،فإن المكتبة SDLهي مكتبة منخفضة المستوى ،أي أنها لا توف ّر لنا سوى
دوال قاعدية ،بسيطة جدا ً.
الصراحة هي أن الشكل الوحيد الذي تسمح لنا الـ SDLبرسمه هو المستطيل ! ك ّل ما سنقوم به هو جمع
بعض المستطيلات في نافذة .نسمّي هذه المستطيلات بـالمساحات ) ،(Surfacesالمساحة هي الوحدة الرسومية
القاعدية في الـ.SDL
إنه من الممكن أن نرسم أشياء أخرى ،مثل الدوائر و المثلثات ،إلخ .و لـكن لـكي نفعل ذلك ،يجب أن
نكتب بأنفسنا الدوال ال ّتي تم ّكن من فعل ذلك ،برسم تلك الأشكال بيكسلا ببيكسل ،و إما أن نستعمل
مكتبة أخرى إلى جانب الـ .SDLالأمر مع ّقد نوعا ً ما ،لـكن لا تقلق ،ستجد بأننا لسنا بحاجة إلى ك ّل
هذا في التطبيق.
مساحتك الأولى :الشاشة
في كل برامج الـ ،SDLتوجد على الأقل مساحة عمل واحدة و هي ما نسميه بالشاشة ) ،(Screenو هي مساحة
توافق كل النافذة ،أي ك ّل المساحة السوداء التي تظهر بالنافذة.
في الشفرة المصدر ية ،كل مساحة يتم تخزينها في متغير من نوع . SDL_Surfaceنعم ،إنه نوع بيانات تم
انشاؤه من طرف الـ) SDLهذا المتغير عبارة عن هيكل(.
بما أن أول مساحة ننشئها هي الشاشة ،فهيا بنا :
;1 SDL_Surface *screen = NULL
تلاحظ أنني قمت بإنشاء مؤش ّر .لماذا أفعل هذا ؟ لأن الـ SDLهي من ستقوم بحجز مكان في الذاكرة من
أجل مساحتنا .المساحة بالفعل ليس لها بالضرورة دائما نفس الحجم و لهذا فعلى الـ SDLأن تقوم بحجز حيّ من
أجلنا )هنا ،هذا يعتمد على حجم النافذة التي فتحناها(.
لم أقل لك هذا من قبل ،لـكن الدالة SDL_SetVideoModeتقوم بإرجاع قيمة ! ستقوم بإرجاع مؤش ّر
نحو المكان بالذاكرة المخصص لمساحة الشاشة.
ممتاز ،يمكننا إذا استرجاع المؤش ّر في المتغير : screen
;)1 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
المؤشّر يمكن الآن أن يساوي قيمتين :
314
التعامل مع المساحات
• : NULLالمتغير screenسيساوي NULLإذا فشلت الدالة SDL_SetVideoModeفي تحميل
أسلوب العرض الذي تم طلبه .و هذا يحصل حينما يتم اختيار دقة جد عالية أو عدد كبير جدا ً من
الألوان ،أكبر من أقصى عدد يتحمله جهازك.
• قيمة أخرى :إذا كانت القيمة مختلفة عن ، NULLفهذا يعني أن الـ SDLقامت بحجز المكان ،كل شيء
على ما يرام !
إنه من المستحسن هنا أن تتم معالجة الأخطاء ،تماما مثلما فعلنا حينما أردنا تحميل الـ ،SDLهاهي إذا الدالة
الكاملة بإضافة معالجة الأخطاء للـ . SDL_SetVideoMode
)][1 int main(int argc, char *argv
{2
3 SDL_Surface *screen = NULL; // The pointer which stores the
surface of the screen
;)4 SDL_Init(SDL_INIT_VIDEO
5 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE); // We
try to open the window
6 if (screen == NULL) // If we can’t, we note it and we exit.
{7
”8 fprintf(stderr, ”Impossible to load video mode :%s\n
9 ;))(, SDL_GetError
10 ;)exit(EXIT_FAILURE
11 }
12 ;)SDL_WM_SetCaption(”My super SDL window !”, NULL
13 ;)(pause
14 ;)(SDL_Quit
} 15 ;return EXIT_SUCCESS
الرسالة التي تتركها لنا الدالة ، SDL_GetErrorمفيدة من أجل معرفة ما ال ّذي لم يعمل.
حكاية صغيرة :مرة أخطأت بينما أريد أن أفتح نافذة بأسلوب الشاشة الكاملة ) ،(Full screenفي
عوض أن أطلب الدقة 1024×768كتبت بالخطأ ،10244×768لم أفهم لماذا لم يتم التحميل ،لأن ّي لم أنتبه
إلى أنني كتبت 4م ّرتين )ربما كنت متعبا ً( .و لحل المشكل ألقي ُت نظرة على الملف ، stderr.txt
توجهت إلى رسالة الخطأ و اكتشفت بأن الدقة التي طلبتها مرفوضة )شيء يثير الفضول أليس كذلك
؟(.
تلوين مساحة
لا توجد 36طر يقة لملء مساحة ،الحقيقة أنه توجد طر يقتان :
315
الفصل ج .2.إنشاء نافذة و مساحات
• إما أن يتم تلوين المساحة بلون موحّد.
• إما أن يتم ملؤها عن طر يق تحميل صورة.
يمكنك في الحقيقة الرسم في المساحة بيكسلا ببيكسل ،لـكن هذه الطر يقة مع ّقدة ،لن نراها هنا.
سنرى أولا كيف نقوم بتلوين مساحة بلون موحّد .في الفصل القادم سنتعلّم كيف نقوم بتحميل صورة.
الدالة التي تسمح بتلوين النافذة بلون موحّد هي ) SDL_FillRectالعبارة FillRectتعني ملء
مستطيل بالإنجليز ي ّة( .هذه الدالة تستقبل 3معاملات و هي :
• مؤش ّر نحو المساحة التي نريد التلوين عليها )مثلا .( screen
• الجزء من المساحة الذي نريد تلوينه ،إذا أردت تلوين كل المساحة )و هذا ال ّذي نريده( فلتكن قيمة
المؤشر . NULL
• اللون الذي نريد أن نلوّن به المساحة.
كمل ّخص :
;)1 SDL_FillRect(surface, NULL, color
التحكم في الألوان بالـSDL
في الـ SDLكل لون مخزن في عدد من نوع . Uint32
إذا كان عددا ً ،لماذا إذا لم نستعمل ببساطة النوع intأو النوع long؟
الـ SDLهي مكتبة متعددة المنصات ،و كما تعلم فحجم الـ intيتغير من نظام تشغيل إلى آخر .لهذا فإن
الـ SDLتقوم باستخدام أعداد من أنواع جديدة ،هذه الأنواع الجديدة تحجز نفس المكان بالذاكرة في كل أنظمة
التشغيل.
هناك مثلا ً:
• : Uint32عدد صحيح بحجم 32 bitsأي ) 4 octetsللتذكير .(1 octet = 8 bits :
• : Uint16عدد صحيح مشفر على .(2 octets) 16 bits
316
التعامل مع المساحات
• : Uint8عدد طبيعي مشفر على .(1 octet) 8 bits
لن تستعمل المكتبة سوى typedefلتقوم بتغيير قيمة العدد على حسب نظام التشغيل .إذا كنت فضوليا ً،
فألق نظرة على الملف . SDL_types.h
لن نتأخر في التعامل مع كل هذا ،فالتفاصيل لا تهم حاليا ً .كل ما عليك تذكره هو أن النوع Uint32لا
يخزن إلا عددا صحيحا ًليس إلا ،مثل . int
لـكن كيف أعرف أي عدد يوافق اللون ال ّذي أريد ؟
هناك بالفعل دالة من أجل ذلك ، SDL_MapRGB :هذه الأخيرة تستقبل 4معاملات :
• صيغة الألوان :هذه الصيغة تعتمد على عدد bits/pixelالتي قد طلبتها بالـ . SDL_SetVideoMode
يمكنك استرجاع القيمة فهي موجودة في المتغير الداخلي . screen->format
• كمية الأحمر في اللون.
• كمية الأخضر في اللون.
• كمية الأزرق في اللون.
قد لا يعرف البعض بأن كل الألوان يتم تشكيلها عن طر يق خلط الألوان :أزرق ،أحمر و أخضر.
كل كمية ٺتدرج من العدد ) 0لا يوجد لون( إلى العدد ) 255كل اللون موجود( .أي أننا لو كتبنا :
)1 SDL_MapRGB(screen−>format, 255, 0, 0
فاللون المتشكل سيكون أحمرا .لا وجود للأخضر و لا للأزرق ،أما لو نكتب :
)1 SDL_MapRGB(screen−>format, 0, 0, 255
اللون سيكون أزرقا ،بينما لو نكتب :
)1 SDL_MapRGB(screen−>format, 255, 255, 255
اللون سيكون أبيض لأننا دمجنا كل الألوان ،لو أنك تريد تشكيل اللون الأسود ،فلتجعل كل القيم على .0
ألا يمكننا استعمال لون آخر غير هذه الألوان ؟
كل ّا ،يمكنك ذلك لو أنك تقوم بمزج الألوان بشكل ذكي .للمساعدة في ذلك ،توجه إلى برنامج Paintثم إلى
، Modify the colors / Colorsانقر على Define the colorsثم . Custom
هنا ،اختر اللون ال ّذي يلائمك .أنظر إلى الصورة التالية :
317
الفصل ج .2.إنشاء نافذة و مساحات
مركّبات اللون متواجدة في أسفل يمين النافذة .كما ترى فقد اخترت لونا أخضر مزرق ّا ،و هو يتكوّن من 17
من الأحمر 206 ،من الأخضر ،و 112من الأزرق.
تلوين الشاشة
الدالة SDL_MapRGBتقوم بإرجاع عدد من نوع Uint32يوافق الل ّون المختار.
يمكننا إذا تعر يف متغير باسم blueGreenيحوي الشفرة الخاصة لاسترجاع هذا اللون :
;)1 Uint32 blueGreen = SDL_MapRGB(screen−>format, 17, 206, 112
ليس من الضروري المرور دائما على متغير لتخزين اللون المراد استعماله )إلا إن كنت تحتاجه فعلا في
برنامجك(.
يمكنك مباشرة إعطاء القيمة التي تم ارجاعها من طرف الدالة SDL_MapRGBإلى الدالة . SDL_FillRect
لو نريد أن نملئ الشاشة باللون الأخضر المزرق ،يمكننا كتابة :
))1 SDL_FillRect(screen , NULL, SDL_MapRGB(screen−>format, 17, 206, 112
;
لقد قمنا باستدعاء دالة خلال استدعاء دالة أخرى ،أعتقد أنك تعرف بأن الأمر ممكن و لا يسبب أ ّي
مشاكل في لغة .C
318
التعامل مع المساحات
تحديث الشاشة
لقد اقتربنا من تحقيق الهدف.
لقد نسينا أمرا ً بسيطا ً :و هو الأمر بتحديث الشاشة .بالفعل ،فالأمر SDL_FillRectيقوم بتلوين الشاشة،
لـكن هذا لا يحصل إلا في الذاكرة ،إذ يجب أن نطلب من الحاسوب تحديث الشاشة لاستعمال البيانات الجديدة.
من أجل هذا سنستعمل الدالة ، SDL_Flipسنتكلم بشكل مفصل عن هذه الدالة لاحقا.
الدالة تستقبل معاملا واحدا و هو الشاشة . screen
فلنل ّخص كل شيء !
هذه دالة mainتقوم بفتح نافذة ملونة باللون الأخضر المزرق :
1 )][int main(int argc, char *argv
2
{
;3 SDL_Surface *screen = NULL
;)4 SDL_Init(SDL_INIT_VIDEO
;)5 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
;)6 SDL_WM_SetCaption(”My super SDL window!”, NULL
7 // We colorize the screen with blue−green color
8 SDL_FillRect(screen, NULL, SDL_MapRGB(screen−>format, 17,
9 ;))206, 112
10 ;)SDL_Flip(screen
11 ;)(pause
12 ;)(SDL_Quit
} 13 ;return EXIT_SUCCESS
هاهي النتيجة :
319
الفصل ج .2.إنشاء نافذة و مساحات
رسم مساحة أخرى في الشاشة
النتيجة السابقة جيدة ،لـكننا لن نتوقف هنا .لحد الآن ليست لدينا سوى مساحة واحدة و هي الشاشة .نحن
نريد أن نقوم بالرسم عليها ،أي ”نلصق” مساحات أخرى عليها بألوان مختلفة.
لهذا يجب علينا إنشاء متغير من نوع SDL_Surfaceللمساحة الجديدة :
;1 SDL_Surface *rectangle = NULL
سنطلب إذا من الـ SDLأن تقوم بحجز مكان في الذاكرة من أجل المساحة الجديدة.
من أجل الشاشة كنا قد استعملنا . SDL_SetVideoModeلـكن هذه الأخيرة لا تعمل إلا على الشاشة
)المساحة الرئيسية( ،لا نريد أن نقوم بإنشاء نافذة من أجل كل مستطيل نريد إنشاءه !
توجد إذا دالة أخرى من أجل إنشاء مساحة . SDL_CreateRGBSurface :هذه هي التي سنقوم
باستعمالها في كل مرة نريد أن ننشئ مساحة جديدة.
هذه الدالة تستقبل العديد من المعاملات )ثمانية !( .لـكنني لن أتطر ّق إلا للمعاملات التي تهمّنا لح ّد الآن.
بما أن لغة Cتلُزمنا بإدخال قيم لكل المعاملات ،فإننا سنقوم بوضع القيمة 0في مكان كل معامل لا يهمّنا.
فلنتأمل قليلا في المعاملات الأربع الأولى )يجدر بها أن تذكّرنا بإنشاء الشاشة(.
• قائمة الأعلام )الخيارات( .لديك الاختيار بين :
320
التعامل مع المساحات
– : SDL_HWSURFACEالمساحة يتم تحميلها في الذاكرة الرسومي ّة .و هي تحتوي على مكان أقل
مقارنة بالذاكرة الخاصة بالنظام )حقيقة ،مع بطاقات الـ 3Dفي أيامنا هذه ،قد لا يكون لهذا تأثير(،
لـكنها ذاكرات سر يعة و فعّالة.
– : SDL_SWSURFACEيتم تحميل المساحة في الذاكرة الخاصة بالنظام ،أين يوجد الـكثير من المكان،
لـكن هذا الاختيار سيجبر المعالج على القيام بحسابات أكثر .لو أنك حم ّلت المساحة على الذاكرة
الرسومي ّة ،فإن البطاقة 3Dهي المسؤولة عن القيام بأغلب الحسابات.
• عرض المساحة ).(pixels
• ارتفاع المساحة ).(pixels
• عدد الألوان ).(bits/pixel
هكذا إذا نقوم بحجز مكان للمساحة الجديدة في الذاكرة :
1 rectangle = SDL_CreateRGBSurface(SDL_HWSURFACE, 220, 180, 32, 0, 0,
;)0, 0
الأربع معاملات الأخيرة تساوي ،0كما قلت لك ،لأننا لا نهتم بأمرها حاليا ً.
بما أننا قمنا بالحجز اليدوي للذاكرة ،فيجب علينا تحريرها باستعمال الدالة SDL_FreeSurfaceو التي
نستعملها قبل : SDL_Quit
;)1 SDL_FreeSurface(rectangle
;)(2 SDL_Quit
ليس هناك من داعي إلى تحرير المساحة screenباستعمال SDL_FreeSurfaceلأنه يتم تحريرها
تلقائيا ًعند استدعاء . SDL_Quit
يمكننا الآن تلوين المساحة الجديدة باللون الأبيض مثلا :
1 SDL_FillRect(rectangle, NULL, SDL_MapRGB(screen−>format, 255, 255,
;))255
لصق المساحة بالشاشة
اقتربنا من النهاية ،هيا بعض الشجاعة ! المساحة جاهزة ،لـكن لو تحاول تجريب البرنامج ،ستلاحظ أنها لن تظهر
على الشاشة ،بالفعل إذ أن المساحة screenهي وحدها التي تم إظهارها .لـكي نستطيع رؤ ية مساحتنا الجديدة
يجب أن نقوم بـتسو ية المساحة ،أي لصقها على الشاشة ،سنستعمل لأجل هذا الدالة . SDL_BlitSurface
هذه الدالة تنتظر :
321
الفصل ج .2.إنشاء نافذة و مساحات
• المساحة التي نريد لصقها )هنا .( rectangle
• معلومة حول الجزء من تلك المساحة الذي نريد لصقه )اختياري( .لن يهمنا الأمر الآن فنحن نريد لصق
كل المساحة و لهذا فستكون القيمة . NULL
• المساحة التي نريد أن نلصق عليها المساحة الجديدة )في حالتنا هذه نتكلم عن الشاشة .( screen
• مؤش ّر نحو متغير يحتوي الإحداثيّات .هذه الإحداثيات تشير إلى المكان الذي نريد أن نلصق عليه المساحة،
أي موقعه.
للإشارة إلى الإحداثيّات ،نحتاج إلى استعمال متغير من نوع . SDL_Rect
إن ّه هيكل يحتوي العديد من المركّبات ،إثنتان منها تهمّنا :
– : xالفاصلة.
– : yالترتيبة.
يجب أن تعرف ان الإحداثيّة ) (0, 0توافق اقصى نقطة في يسار أعلى الشاشة.
أما الإحداثيّة ) (640, 480فهي توافق النقطة الموجودة في أقصى يمين أسفل الشاشة ،و هذا إن كنت قد
فتحت نافذة بحجم 640 × 480مثلي.
هذا المخطط سيساعدك في الفهم :
إذا كنت قد درست الر ياضيات من قبل ،فعلى الأرجح لن تضيع بينما تحاول فهم كيفية العمل .فلننشئ
إذا متغيرا . positionسنعطي القيمة 0لكل من الفاصلة و الترتيبة و ذلك ليتم لصق مساحتنا )المستطيل(
في أعلى يسار النافذة :
322
التعامل مع المساحات
1 SDL_Rect position;
2 position.x = 0;
3 position.y = 0;
: يمكننا تسو ية المساحة الجديدة على الشاشة،و الآن بما أننا حددنا موقعنا في النافذة
1 SDL_BlitSurface(rectangle, NULL, screen, &position);
. position لاحظ أنني استعملت الرمز & و ذلك لأنه يجب علينا إرسال عنوان المتغير
تلخيص الشفرة المصدر ية
: ً أعتقد أن وضع الشفرة المصدر ية ال ّتي تلخص ما شرحته لن يكون مضرا
1 int main(int argc, char *argv[])
2
{
3 SDL_Surface *screen = NULL, *rectangle = NULL;
4 SDL_Rect position;
5 SDL_Init(SDL_INIT_VIDEO);
6 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE);
7 // Surface allocation
8 rectangle = SDL_CreateRGBSurface(SDL_HWSURFACE, 220, 180, 32,
0,0, 0, 0);
9 SDL_WM_SetCaption(”My super SDL window !”, NULL);
10 SDL_FillRect(screen, NULL, SDL_MapRGB(screen−>format, 17,
206,112));
11 position.x = 0; // The coordinates of the surface will be (0,
0)
12 position.y = 0;
13 // Filling the surface with white color
14 SDL_FillRect(rectangle, NULL, SDL_MapRGB(screen−>format,
255,255, 255));
15 SDL_BlitSurface(rectangle, NULL, screen, &position); //
16 Sticking the surface on the screen
17 SDL_Flip(screen); // Updating the screen
18 pause();
19 SDL_FreeSurface(rectangle); // Freeing the surface
20 SDL_Quit();
21 } return EXIT_SUCCESS;
: شاهد النتيجة
323
الفصل ج .2.إنشاء نافذة و مساحات
مركزةُ المساحة في الشاشة
نحن نجيد إظهار المساحة في أعلى اليسار .يسهل أيضا موقعتها أسفل يمين الشاشة .ستكون الإحداثيات
) ،(640 − 220, 480 − 180لأنه يجب إنقاص حجم المستطيل ليتم إظهاره كاملا.
لـكن كيف تتم مركزةُ المستطيل الأبيض ؟ لو تفكّر قليلا ًستجد بأن الحساب ر ياضياتيّ .فهنا نعرف الهدف
من الر ياضيات و الحساب الهندسي !
ك ّل هذا الأمر بمستوى سهل هنا :
;)1 position.x = (640 / 2) − (220 / 2
;)2 position.y = (480 / 2) − (180 / 2
فاصلة المستطيل هي نصف عرض الشاشة ) .(640/2و لـكن ،بالإضافة إلى هذا ،يجب أن يتم إنقاص
نصف طول المستطيل أيضا ً ) ،(220/2لأنك إن لم تنقص هذا الحجم ،سيكون تمركز المستطيل خاطئا ً )جرّب
عدم فعل ذلك و ستفهم ما ال ّذي أعنيه(.
كذلك بالنسبة للترتيبة مع ارتفاع الشاشة و المستطيل.
النتيجة :المستطيل الأبيض يتمركز بشكل جيد في الشاشة.
324
تمرين :إنشاء تدرّج لونيّ
ج 4.2.تمرين :إنشاء تدرّج لونيّ
سننهي الفصل بتمرين صغير )مص ّحح( متبوع بسلسلة تمارين أخرى )غير مصححة من أجل حث ّك على
التدر يب(.
التمرين المصحح ليس صعبا ًح ّقا :ما نريد إنشاءه هو نافذة متدرّجة الألوان عموديا من الأسود إلى الأبيض.
سيكون عليك إنشاء 255مساحة بارتفاع 1بيكسل .كل مساحة لها لون مختلف أكثر فأكثر سوادا.
هذا ما يجب عليك الحصول عليه في النهاية ،صورة مشابهة لهذه :
325
الفصل ج .2.إنشاء نافذة و مساحات
إنه أمر جميل ،أليس كذلك ؟ الشيء الأجمل هو أن بعض الحلقات التكرار ية كافية من أجل تحقيق
المطلوب.
لفعل ذلك يجب إنشاء 256مساحة )أي 256سطر( تحتوي مركبات الألوان )أحمر ،أخضر ،أزرق(
التالية :
1 0, 0, 0) // Black
2 (1, 1, 1) // Gray that is so so close from black
3 (2, 2, 2) // Gray that is so close from black
4 ...
)5 (128, 128, 128) // Medium gray (to 50 %
6 ...
7 (253, 253, 253) // Gray that is so close from white
8 (254, 254, 254) // Gray that is so so close from white
9 (255, 255, 255) // White
يجب على الجميع أن يعرف أن ّه بحاجة إلى حلقة تكرار ي ّة للقيام بهذا )لن تسعد بتكرار 256سطرا !( .و لهذا
سنقوم بإنشاء جدول من نوع * SDL_Surfaceمن 256خانة.
إلى العمل .لديك 5دقائق !
تصحيح !
يجب أولا أن نقوم بتعر يف جدول من . SDL_Surface* 256سنهي ّؤه على : NULL
;}1 SDL_Surface *lines[256] = {NULL
سنعرف متغيرا ً iمن أجل الحلقات . for
سنغيّر أيضا ًارتفاع النافذة لـكي تكون مناسبة للعمل .إذ سنعطيها 256بيكسلز كارتفاع ،و ذلك من أجل
عرض كل سطر من بين 256سطرا.
سنستعمل بعد ذلك حلقة تكرار ية forمن أجل حجز مكان لـ 256مساحة ال ّتي تم إنشاؤها .الجدول
سيستقبل 256مؤشّرا إلى ك ّل واحد من المساحات المنشأة :
)1 for (i = 0 ; i <= 255 ; i++
2 lines[i] = SDL_CreateRGBSurface(SDL_HWSURFACE, 640, 1, 32,
;)0,0, 0, 0
بعد ذلك نقوم بملء و لصق كل مساحة في الشاشة واحدة بواحدة.
326
تمرين :إنشاء تدرّج لونيّ
)1 for (i = 0 ; i <= 255 ; i++
{2
)3 position.x = 0; // The lines are to the left (0 abscissa
’4 position.y = i; // The vertical position depends on the line
s number
5 SDL_FillRect(lines[i], NULL, SDL_MapRGB(screen−>format, i, i,
i)); // Drawing
6 SDL_BlitSurface(lines[i], NULL, screen, &position); //
Sticking
}7
لاحظ أنني استعمل كل الوقت المتغير . positionإذ ليس لازما أن ننشئ 256واحدا ،لأننا لن نقوم
إلا ببعث المتغير إلى الدالة . SDL_BlitSurfaceيمكننا إذن إعادة استخدامه دون مشاكل.
في ك ّل مرة أقوم بالتعديل على الترتيبة ) ،( yلتسو ية المساحة على الارتفاع الصحيح .اللون يعتمد في ك ّل مرة
على قيمة المتغير ) iستكون 0, 0, 0في أ ّول م ّرة و 255, 255, 255في آخر م ّرة(.
لـكن لماذا قيمة xهي دائما ً؟
كيف يمكن للمساحة أن ٺتلون كليا إذا كانت قيمة الـ xدائما ؟
المتغير positionيشير إلى أي مكان ٺتواجد فيه المنطقة أعلى اليسار )هنا نتكلم عن السطر( .هي لا
تحدد عرض المساحة و إنما فقط أين ٺتواجد المر ّكبة على الشاشة.
بما أن كل الأسطر تبدأ في أقصى يسار النافذة ،فستكون الفاصلة مساو ية لـ .0حاول وضع فاصلة تساوي 50
لترى ماذا سيعطيك :كل الأسطر ستتنحي إلى اليمين.
بما أن المساحة تأخذ 640بيكسل كطول ،فإن الـ SDLتقوم بإنشاء 640بيكسلا في اتجاه اليمين )من نفس
اللون( إنطلاقا ًمن المركبات التي يشير إليها المتغير . position
في المخطط التالي أر يك مركبات النقطة المتواجدة أعلى يسار الشاشة )وضعية أول سطر( ومركبات النقطة
المتواجدة أسفل يسار الشاشة )وضعية آخر سطر(.
327
إنشاء نافذة و مساحات.2.الفصل ج
وحده يتغير من أجلy ( بينما0 يبقى مساو يا لـx ) المحور لا يتغير، من الأعلى إلى الأسفل،كما ترى
. position.y = i; و بهذا،كل سطر جديد
بمساعدة حلقة، مساحة المنشأة256أخيرا ً لا تنس أنه يجب تحرير الذاكرة من أجل كل مساحة من الـ
.بالطبع
1 for (i = 0 ; i <= 255 ; i++) // Don’t forget to free the 256 surfaces
2 SDL_FreeSurface(lines[i]);
main مل ّخص
: كاملةmain هذه هي الدالة
1 int main(int argc, char *argv[])
2{
3 SDL_Surface *screen = NULL, *lines[256] = {NULL};
4
SDL_Rect position;
5 int i = 0;
6 SDL_Init(SDL_INIT_VIDEO);
7 screen = SDL_SetVideoMode(640, 256, 32, SDL_HWSURFACE);
8 for (i = 0 ; i <= 255 ; i++)
9 lines[i] = SDL_CreateRGBSurface(SDL_HWSURFACE, 640,
1, 32,0, 0, 0, 0);
10 SDL_WM_SetCaption(”My SDL gradient !”, NULL);
11 SDL_FillRect(screen , NULL, SDL_MapRGB(screen −>format, 0, 0,
0));
12 for (i = 0 ; i <= 255 ; i++)
13 {
14 position.x = 0; // The lines are to the left
15 position.y = i; // The vertical position depends on
the line’s number
16 SDL_FillRect(lines[i], NULL, SDL_MapRGB(screen−>
format, i, i, i));
17 SDL_BlitSurface(lines[i], NULL, screen, &position);
18 }
19 SDL_Flip(screen);
20 pause();
21 for (i = 0 ; i <= 255 ; i++) // Don’t forget to free the 256
22 surfaces
23 SDL_FreeSurface(lines[i]);
24
25 } SDL_Quit();
return EXIT_SUCCESS;
”! ”أريد تمارين للتدريب
! مول ّد التمارين مُشَغّل،لا مشكلة
328
مل ّخص
• قم بإنشاء تدرج عكسي للألوان ،أي من الأبيض للأسود .هذا الأمر لن يكون صعبا للبدأ !
• يمكنك أيضا ً وضع كلى التدرّجين ،من الأبيض للأسود ثم من الأسود للأبيض )ستأخذ النافذة ضعف
الارتفاع الحالي(.
• أكثر صعوبة قليلا ،يمكنك وضع تدرج أفقي بدل التدرج العمودي.
• حاول إنشاء تدرج ألوان مختلفة عن الأسود و الأبيض .جرب مثلا من الأحمر إلى الأسود ،من الأخضر
إلى الأسود ،و من الأزرق إلى الأسود ،ثم ّ من الأحمر إلى الأبيض ،إلخ.
مل ّخص
• يتم تحميل الـ SDLبواسطة الـ SDL_Initفي بداية البرنامج ،و يتم إيقافها باستعمال SDL_Quitفي
النهاية.
• الأعلام هي ثوابت يمكن جمعها فيما بينها باستعمال الرمز | ،و هي تلعب دور الخواص.
• تقوم الـ SDLبالتعامل مع المساحات و التي هي عبارة عن مستطيلات من نوع . SDL_Surfaceالرسم
على النافذة يتم بالاستعانة بهذه المساحات.
• توجد دائما على الأقل مساحة واحدة و التي تحجز ك ّل النافذة ،و نسميها في أغلب الأحيان الشاشة
) .( screen
• ملء مساحة يتم باستعمال ، SDL_FillRectولصقها في الشاشة يتم باستعمال . SDL_BlitSurface
• الألوان معر ّفة بمزيج من الأحمر ،الأزرق و الأخضر.
329
الفصل ج .2.إنشاء نافذة و مساحات
330
الفصل ج3.
إظهار صور
لقد تعل ّمنا كيف نقوم بتحميل الـ ،SDLفتح نافذة و التعامل مع المساحات .إنها بالفعل من المبادئ التي
تجب معرفتها عن هذه المكتبة .لـكن لح ّد الآن لا يمكننا سوى إنشاء مساحات موحّدة اللون ،و هذا الأمر
بدائي قليلا ً.
في هذا الفصل ،سنتعلّم كيف نقوم بتحميل صور على مساحات ،مهما كانت صيغتها PNG ،BMPأو حتى
GIFأو .JPGالتحكم في الصور أمر مهم للغاية لأنه بتجميع الصور )نسميها أيضا ً” (”spritesنضع اللبنات الأولى
في بناء لعبة فيديو.
ج 1.3.تحميل صورة BMP
الـ SDLهي مكتبة بسيطة جدا ً .فهي لا تستطيع أساسا تحميل سوى صور من نوع ”) ”bitmapذات امتداد
.( .bmpلا تقلق ،فبفضل إضافة خا ّصة بالـ) SDLالمكتبة ،(SDL_Imageسنرى بأنه بإمكاننا أيضا ً تحميل
صور من صيغ أخرى.
للبدأ ،سنكتفي الآن بما تسمح لنا به الـ SDLبشكل قاعدي .سنقوم بدراسة تحميل صور .BMP
الصيغة BMP
الصيغة ) BMPإختصار لـ (bitmapهي صيغة صور.
الصور ال ّتي نجدها في الحاسوب مخز ّنة في ملفات .يوجد العديد من صيغ الصور ،أي العديد من الطرق لتخزين
صورة في ملف .على حسب الصيغة ،يمكن للصورة أخذ الـكثير أو القليل من مساحة القرص الصلب ،و تملك
جودة أحسن أو أسوء.
الـ Bitmapهي صيغة غير مضغوطة )على عكس الـ ،GIF ،PNG ،JPGإلخ( .فعلي ّا ،هذا يعني الأمور التالية :
331
الفصل ج .3.إظهار صور
• يكون الملف سر يعا ً جدا ً من ناحية قراءته ،على عكس الصيغ المضغوطة التي يجب أن يتم فك الضغط
عنها ،مما يكل ّفنا بعض الوقت.
• جودة الصورة مثالية .بعض الصيغ المضغوطة )أفكّر في الـ JPGخصوصا ،لأن الـ PNGو الـ GIFلا يغيّرون
في الصورة( تقوم بتخريب جودة الصورة ،و هذا ليس هو الحال بالنسبة للـ.BMP
• لـك ّن الملف سيكون ضخما ًبما أنه ليس مضغوطا ً!
توجد هناك إذا مزايا و مساوئ.
بالنسبة للـ ،SDLالشيء الجيد هو أن نوع الملف سيكون بسيطا و سهل القراءة .إذا كان عليك تحميل الصور دائما
في نفس وقت تشغيل برنامجك ،من المستحسن استعمال صور بصيغة .BMPسيكون حجم الملف ضخما حتما،
لـكنه يـ ُحمّل بشكل أسرع من الـ GIFمثلا ً .سيكون الأمر مه ّما إذا كان على برنامجك تحميل الـكثير من الصور
في وقت قليل.
تحميل صورة Bitmap
تنز يل حزمة الصور
في هذا الفصل سنقوم بالعمل على كثير من الصور .إذا أردت القيام بتجريب الشفرات بينما أنت تقرأ )و هذا
ما يجدر بك فعله !( ،فأنصحك بتنز يل حزمة الصور التي تحتوي كل الصور التي نحتاج إليها.
https://openclassrooms.com/uploads/fr/ftp/mateo21/pack_images_sdz.zip
)(1 Mo
بالطبع ،يمكنك استعمال صورك الخاصة .يجب عليك فقط أن تع ّدل مقاييس النافذة على حسب مقاييس
الصورة.
قم بوضع كل الصور في مجل ّد المشروع .سنبدأ أولا ّ بالعمل على الصورة . lac_en_montagne.bmp
هي عبارة عن لقطة تم استخلاصها من مشهد ثلاثي الأبعاد مأخوذ من البرنامج الممتاز الخاص بنمذجة المناظر
الطبيعية ،Vue d’Espri 4و الذي تم إيقاف تسو يقه .منذ ذلك ،تم ّ تغيير اسم البرنامج إلى Vueو تم تطويره
كثيرا ً .لمن يريد معرفة المزيد عنه ،يمكنه ز يارة الموقع .http://www.e-onsoftware.com/ :
تحميل صورة في مساحة
سنقوم باستعمال دالة تقوم بتحميل صورة ذات صيغة BMPو لصقها في مساحة.
هذه الدالة تدعى SDL_LoadBMPو سترى أن استعمالها سهل للغاية :
;)”1 mySurface = SDL_LoadBMP(”image.bmp
332
BMP تحميل صورة
: تقوم بتعو يض دالتين تعرفهماSDL_LoadBMP الدالة
تقوم بحجز مكان في الذاكرة من أجل تخزين مساحة ذات الحجم: SDL_CreateRGBSurface •
.( malloc المطلوب )تكافئ دالة
. تقوم بملئ الهيكل بلون موحّد: SDL_FillRect •
: لماذا تقوم الدالة بتعو يض هذين الدالتين ؟ الأمر بسيط
اذا كان حجم الصورة هو: • الحجم الذي نقوم بحجزه في الذاكرة من أجل المساحة يعتمد على حجم الصورة
. فستأخذ المساحة نفس الحجم250 × 300
.BMP يتم ملئ المساحة بيكسلا ببيكسل بمحتوى الصورة،• من جهة أخرى
: فلنكتب الشفرة دون أي تأخير
1 int main(int argc, char *argv[])
2
{
3 SDL_Surface *screen = NULL, *backgroundImage = NULL;
4 SDL_Rect backgroundPosition;
5 backgroundPosition.x = 0;
6 backgroundPosition.y = 0;
7 SDL_Init(SDL_INIT_VIDEO);
8 screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
9 SDL_WM_SetCaption(”Loading the images on SDL”, NULL);
10 /* Loading a Bitmap image in a surface */
11 backgroundImage = SDL_LoadBMP(”lac_en_montagne.bmp”);
12 /* We blit on the screen */
13
SDL_BlitSurface(backgroundImage, NULL, ecran, &
14 backgroundPosition);
15 SDL_Flip(screen);
16 pause();
17 SDL_FreeSurface(backgroundImage); // We free the surface
18 SDL_Quit();
19 } return EXIT_SUCCESS;
( و نحو كل المركّبات الموافقة لهاbackgroundImage ) و بهذا أكون قد أنشأت مؤش ّرا ً نحو مساحة
.( backgroundPosition )
. SDL_LoadBMP تم إنشاء المساحة في الذاكرة و ملؤها من طرف الدالة
: و هذا ك ّل شيء ! الصورة التالية تو ّضح النتيجةscreen نقوم بتسويتها على المساحة
333
الفصل ج .3.إظهار صور
كما ترى ،لم يكن الأمر صعبا ً!
إرفاق أيقونة بالتطبيق
بما أننا الآن نجيد تحميل الصور ،يمكننا اكتشاف كيفية إرفاق أيقونة بالبرنامج .سيتم إظهار الأيقونة في أعلى يسار
النافذة )و أيضا ًفي شر يط المهام( .لح ّد الآن نحن لا نملك إلا ّ أيقونة افتراضي ّة.
لـكن ألا يجدر بأيقونات البرامج أن تكون ذات الامتداد .ico؟
كل ّا ،ليس شرطا ً ! على ك ّل فالامتداد .icoلا يوجد إلا في نظام الويندوز .الـ SDLٺتعامل مع ك ّل
أنظمة التشغيل باستعمالها نظاما خاصا بها :المساحة !
نعم ،أيقونة برنامج SDLماهي إلا مساحة بسيطة.
يجدر بالأيقونة أن تكون ذات حجم 16 × 16بيكسلز .بينما في الويندوز يجب أن تكون بحجم 32 × 32
بيكسلز و إلا فستسوء جودتها .لا تقلق إذ يمكن للـ” SDLتصغير” أبعاد الصورة لتتمكن من الدخول في
16 × 16بيكسلز.
لإضافة الأيقونة إلى النافذة ،نستعمل الدالة . SDL_WM_SetIcon
هذه الدالة تأخذ معاملين :المساحة التي تحتوي الصورة التي نريد إظهارها كما أنها تستقبل معلومات حول الشفافية
334
التحكم في الشفافية
التحكّم في الشفافية الخاصة بأيقونة مع ّقد قليلا ً )يجب تحديد.( تعني أننا لا نريد أية شفافيةNULL )القيمة
. لن ندرس ذلك إذا،(البيكسلز الشفافة واحدة بواحدة
: سنقوم باستدعاء دالة في استدعاء لأخرى
1 SDL_WM_SetIcon(SDL_LoadBMP(”sdl_icone.bmp”), NULL);
. SDL_WM_SetIcon و تم بعث عنوان المساحة مباشرة إلىSDL_LoadBMP تم تحميل الصورة في الذاكرة بواسطة
أي أنه يجدر بها التواجد، قبل أن يتم فتح النافذةSDL_WM_SetIcon يجب أن يتم استدعاء الدالة
. في الشفرة المصدر يةSDL_SetVideoMode قبل
. مقارنة بالشفرة السابقةSDL_WM_SetIcon ستلاحظ أنني أضفت.هذه هي الشفرة المصدر ية الكاملة
1 int main(int argc, char *argv[])
2
{
3 SDL_Surface *screen = NULL, *backgroundImage = NULL;
4
SDL_Rect backgroundPosition;
5 backgroundPosition.x = 0;
6 backgroundPosition.y = 0;
7 SDL_Init(SDL_INIT_VIDEO);
8 /* Loading the icon before SDL_SetVideoMode*/
9
SDL_WM_SetIcon(SDL_LoadBMP(”sdl_icone.bmp”), NULL);
10 screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
11 SDL_WM_SetCaption(”Loading images on SDL”, NULL);
12 backgroundImage = SDL_LoadBMP(”lac_en_montagne.bmp”);
13 SDL_BlitSurface(backgroundImage, NULL, screen, &
14 backgroundPosition);
15 SDL_Flip(screen);
16 pause();
17 SDL_FreeSurface(backgroundImage);
18 SDL_Quit();
19 } return EXIT_SUCCESS;
. تم تحميل الصورة و عرضها أعلى يسار النافذة: النتيجة
التحكم في الشفافية2.3.ج
335
إظهار صور.3.الفصل ج
مشكل الشفافية
. في النافذةbitmap لقد قمنا قبل قليل بتحميل صورة
غالبا اللاعب الذي يتحر ّك في الخر يطة. و هذا ما يحصل كثيرا ً في الألعاب.لنفرض أننا نريد لصق صورة فوقها
. تتحرك فوق صورة خلفيةbitmap هو عبارة عن صورة
OpenClassrooms سلف الموقعSite du Zéro فهو شعار، )لمن لا يعرفهZozor سنقوم بلصق صورة
: حالي ّا( في المشهد
1 int main(int argc, char *argv[])
2{
3 SDL_Surface *screen = NULL, *backgroundImage = NULL, *zozor =
NULL;
4 SDL_Rect backgroundPosition, zozorPosition;
5 backgroundPosition.x = 0;
6 backgroundPosition.y = 0;
7 zozorPosition.x = 500;
8 zozorPosition.y = 260;
9 SDL_Init(SDL_INIT_VIDEO);
10 SDL_WM_SetIcon(SDL_LoadBMP(”sdl_icone.bmp”), NULL);
11 screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
12 SDL_WM_SetCaption(”Loading images on SDL”, NULL);
13 backgroundImage = SDL_LoadBMP(”lac_en_montagne.bmp”);
14 SDL_BlitSurface(backgroundImage, NULL, ecran, &
15 backgroundPosition);
16 //Loading and blitting Zozor on the screen
17 zozor = SDL_LoadBMP(”zozor.bmp”);
18 SDL_BlitSurface(zozor, NULL, screen, &zozorPosition);
19 SDL_Flip(screen);
20 pause();
21 SDL_FreeSurface(backgroundImage);
22 SDL_FreeSurface(zozor);
23 SDL_Quit();
24 } return EXIT_SUCCESS;
: و التي نقوم بلصقها في مكان معي ّن من المشهد،Zozor لقد قمنا فقط بإضافة مساحة لنخز ّن فيها
336
التحكم في الشفافية
يبدو المشهد سي ّئا ،أليس كذلك ؟
بالطبع يعود ذلك إلى الخلفية الزرقاء التي هي خلف ! Zozor
لأنك تعتقد أنه بوجود خلفية سوداء أو بن ّي ّة ،ربما سيكون المظهر لائقا ً أكثر ؟ لا بالطبع ،المشكل هنا هو
أنه من اللازم أن يكون شكل الصورة عبارة عن مستطيل ،أي أنه إذا قمنا بلصقها على المشهد ،سنرى خلفيتها،
مما يشوّه المظهر.
من حسن الح ّظ أن الـ SDLتتحكم في الشفافية !
جعل صورة شفافة
الخطوة : 1تحضير الصورة
كبداية ،يجب تحضير الصورة التي نريد تسويتها على المشهد.
الصيغة BMPلا تتحكم في الشفافية ،على عكس الصيغتين GIFو .PNGلهذا يحب علينا أن نجد حلا ًآخر.
يجب استعمال نفس اللون للخلفية على الصورة .هذه الأخيرة ستكون شفافة من طرف الـ SDLفي وقت
التسو ية .لاحظ كيف تبدو الصورة zozor.bmpمن ناحية أقرب :
337
الفصل ج .3.إظهار صور
الخلفية الزرقاء إذا مـ ُختارة .لاحظ أنني اخترت اللون الأزرق بشكل عشوائي ،كان بإمكاني استعمال اللون
الأحمر أو الأصفر مثلا ً .الشيء المهم هو أنه يجب على اللون أن يكون وحيدا ً و موحّدا .لقد اخترت اللون
الأزرق لأنه ليس متواجدا في صورة Zozorلأنني لو اخترت اللون الأخضر ،سأخاطر بجعل العشب الذي
يتناوله الحمار )أسفل يسار الصورة( شفافا ً.
استعمل إذا أي برنامج كان )… ،The Gimp ،Photoshop ،Paintلك ّل واحد من ّا ذوقه( لإعطاء خلفية
موحّدة للصورة.
الخطوة : 2تحديد اللون الشفاف
لـكي نقوم بتحديد اللون الذي يجب أن تجعله SDLشفافا ً ،يجب أن نستعمل الدالة . SDL_SetColorKey
يجب استدعاء هذه الدالة قبل تسو ية الصورة.
هكذا نقوم بتحو يل اللون الذي خلف Zozorإلى الشفاف :
1 SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor−>format, 0,
;))0, 255
هناك ثلاثة معاملات :
• المساحة التي يجب أن نقوم بتحو يلها إلى اللون الشفاف )هنا نتكلم عن .( zozor
• قائمة الأعلام :استعمل SDL_SRCCOLORKEYلتفعيل الشفافية 0 ،من أجل تعطيلها.
• حدد بعد ذلك اللون الذي يجب أن يتم تحو يله إلى الشفاف .لقد استعملت SDL_MapRGBلإنشاء
اللون بصيغة عدد ) ( Uint32كما فعلنا بالسابق .كما ترى إنه اللون الأزرق ) (0, 0, 255ال ّذي سيتم
تحو يله إلى الشفاف.
كملخص ،نقوم أولا بتحميل الصورة باستعمال ، SDL_LoadBMPثم ّ نحدد اللون الشفاف باستعمال
SDL_SetColorKeyثم نقوم بتسو ية المساحة باستعمال . SDL_BlitSurface
;)”1 zozor = SDL_LoadBMP(”zozor.bmp
2 SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor−>format, 0,
;))0, 255
;)3 SDL_BlitSurface(zozor, NULL, screen, &zozorPosition
338
التحكم في الشفافية
النتيجة :تم دمج صورة Zozorبشكل ممتاز في المشهد :
هذه هي التقنية المبدئية التي ستعيد استعمالها كل الوقت في برامجك .تعلّم كيف تتحكم جيدا ً بالشفافية لأنها
من أساسيات صنع لعبة تملك الح ّد الأدنى من الواقعية.
الشفافية Alpha
هو نوع آخر من الشفافية.
لح ّد الآن قمنا بتعر يف لون واحد شفاف )الأزرق مثلا( .هذا اللون لا يظهر في الصورة الملُصقة.
الشفافية Alphaتوافق شيئا ًآخر ،إنها تسمح بعمل ”مزج” بين صورة و خلفية .هذا نوع من التلاشي.
يمكن تفعيل الشفافية Alphaلمساحة عن طر يق الدالة : SDL_SetAlpha
;)1 SDL_SetAlpha(zozor, SDL_SRCALPHA, 128
يوجد هنا ثلاثة معاملات كذلك :
• المساحة التي نتكلم عنها ) .( zozor
• قائمة الأعلام :ضع SDL_SRCALPHAمن أجل تفعيل الشفافية 0 ،من أجل تعطيلها.
339
الفصل ج .3.إظهار صور
• مهم جدا :قيمة الشفافية Alphaهي عدد يتراوح بين ) 0صورة شفافة تماما ً أي غير مرئية( و 255
)صورة ظاهرة كليا ً ،و كأن الشفافية Alphaلم تكن موجودة(.
كلما كان العدد Alphaصغيرا ً كلما زادت شفافية الصورة و تلاشيها في الخلفية.
هذا مثال عن شفرة تقوم بتطبيق شفافية بقيمة 128على الصورة : Zozor
;)”1 zozor = SDL_LoadBMP(”zozor.bmp
2 SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor−>format, 0,
;))0, 255
3 /* Average Alpha transparency (128) : */
;)4 SDL_SetAlpha(zozor, SDL_SRCALPHA, 128
;)5 SDL_BlitSurface(zozor, NULL, screen, &zozorPosition
تلاحظ أنني حافظت على شفافية . SDL_SetColorKeyيمكن دمج النوعين الاثنين للشفافية معا ً.
الجدول التالي يو ّضح لك كيف يبدو Zozorباختلاف قيم .Alpha
النتيجة Alpha
) 255مرئيّة بالكامل(
190
) 128شفافي ّة متو ّسطة(
75
340
تحميل صيغ صور أخرى باستعمال الـSDL_Image
) 0غير مرئيّة بالكامل(
قيمة الشفافية ) 128 Alphaشفافية متوسطة( هي قيمة خا ّصة و كثيرة الإستعمال بالـ .SDLهذا النمط
من الشفافية أسرع من ناحية حسابات المعالج مقارنة بالأنماط الأخرى .قد يكون من المهم لك معرفة
هذه المعلومة خاصة إن كنت تستعمل الشفافية Alphaبشكل كبير في برامجك.
ج 3.3.تحميل صيغ صور أخرى باستعمال الـSDL_Image
الـ SDLلا ٺتعامل إلا مع الـ) bitmapالصيغة (BMPكما رأينا.
و لـكن هذا ليس بمشكل لأن قراءة الصور ذات الصيغة BMPأسرع بالنسبة للـ ،SDLو لـكن يجب معرفة أنه
في أيامنا هذه يتم استعمال صيغ أخرى للصور .بالتحديد الصيغ ”المضغوطة” كالـ ،PNGالـ GIFو الـ .JPEGلهذا
الغرض توجد مكتبة تسمى SDL_Imageو تقوم بالتعامل مع كل صيغ الصور التالية :
• ،TGA
• ،BMP
• ،PNM
• ،XPM
• ،XCF
• ،PCX
• ،GIF
• ،JPG
• ،TIF
• ،LBM
• .PNG
بالمناسبة فإنه بالإمكان أن تتم إضافة صيغ أخرى للـ .SDLو هي المكتبات التي تحتاج إلى الـ SDLلـكي تعمل.
يمكننا تسمية هذا الأمر بالـ) add-onsبمعنى ”إضافات”( SDL_Image .هي واحدة من بين هذه المكتبات .
341
الفصل ج .3.إظهار صور
ٺثبيت الـ SDL_Imageعلى Windows
التنز يل
توجد صفحة خاصة من موقع الـ SDLتشير إلى المكتبات التي تستعملها الـ .SDLهذه الصفحة تحمل عنوان
” .”Librariesستجد رابطا ًفي القائمة اليسار ية.
ستلاحظ أن هناك الـكثير من المكتبات و أغلبها ليس من طرف المبرمجـين الأصليين للـ .SDLبل هم مبرمجون
عاديون يستعملون الـ SDLو يقومون باقتراح مكتباتهم الخاصة لتحسين هذه الأخيرة.
بعض هذه المكتبات مفيد جدا ً و يستحق إلقاء النظر عليه ،و بعضها أق ّل جودة بل رب ّما فيه أخطاء .لهذا
يجب ترتيب هذه المكتبات حسب أهميتها.
حاول إيجاد SDL_Imageفي القائمة ،ستدخل إلى الصفحة المخصصة لهذه المكتبة :
https://www.libsdl.org/projects/SDL_image
حم ّل النسخة التي تناسبك من القسم ”) ”Binaryلا تحم ّل الملفات المصدر ية ،لن نحتاجها !(.
إذا كنت تعمل على الويندوز ،حم ّل الملف ، SDL_image-devel-1.2.10-VC.zipو هذا حتى و إن
لم تكن تستعمل البيئة التطوير ية ! Visual C++
التثبيت
في الملف .zipهذا ،ستجد :
• : SDL_image.hالملف الرأسي الوحيد الذي تحتاجه الـ ،SDL_Imageقم بلصقه في المسار
C:\Program Files\CodeBlocks\SDL-1.2.13\include
بمعنى آخر ،إلى جانب الملفات الرأسية للـ.SDL
• : SDL_image.libقم بلصقه في المسار
. C:\Program Files\CodeBlocks\SDL-1.2.13\lib
أعرف أنك ستخبرني بأن الملفات ذات الامتداد .libهي محجوزة للبيئة التطوير ية ،Visual C++
لـكن هذه حالة استثنائية ،فالملف .libيعمل حتى مع المترجم .mingw
• الـكثير من الملفات : DLLقم بوضعها كلها في المجل ّد الخاص بالمشروع )أي بجانب الملف .( SDL.dll
بعد ذلك ،يجدر بك تغيير خواص المشروع من أجل محر ّر الروابط ) (Linkerللملف . SDL_image.lib
إذا كنت تعمل بالـ Code::Blocksمثلا ً ،توجّه إلى القائمة ، Build options / Projectsفي الفرع
Linkerأنقر على الزر Addو اختر المسار الذي يتواجد به الملف ، SDL_image.libلاحظ الصورة :
342
تحميل صيغ صور أخرى باستعمال الـSDL_Image
إذا ظهرت لك رسالة تحمل سؤال ،”Keep as a relative path ?” :فلتجب بما أردت لأنه لن يغير شيئا ً
في الوقت الحالي .أنصحك بالإجابة بالسلب ،شخصي ّا.
بعد ذلك ،ما عليك سوى تضمين الملف الرأسي SDL_image.hفي الشفرة المصدر ية .على حسب المكان
الذي وضعت فيه الملف SDL_image.hسيكون عليك استعمال هذه الشفرة :
>1 #include <SDL/SDL_image.h
أو هذه
>1 #include <SDL_image.h
جرّبهما كليهما ،يجدر بأحداهما أن تعمل.
إذا كنت تعمل بالـ Visual Studioفستكون العملي ّة نفسها .لأنه إن تمكنت من ٺثبيت الـ SDLلن يصعب
عليك ٺثبيت الـ.SDL_Image
ٺثبيت الـ SDL_Imageعلى Mac OS X
إن كنت تستعمل ،Mac OS Xنزّل الملف ذو الامتداد .dmgمن موقع الـ SDLو ضعه في المجلد
. Library/Frameworks
343
الفصل ج .3.إظهار صور
اكتب بعد ذلك ” ”search pathsفي حقل البحث الخاص بـ .Xcodeاعثر على السطر
، Header search pathsانقر مرتين على السطر من اليمين و أضف
. /Library/Frameworks/SDL_image.framework/Headers
لم يبق لك سوى اضافة إطار العمل إلى المشروع .الصورة التالية توضح لك كيف يظهر
الـ Header search pathsبعد ٺثبيت الـ.SDL_image
و بهذا يجب عليك تضمين الملف الرأسي في بداية الـكود كالتالي :
”1 #include ”SDL_image.h
في عوض استعمال الإشارتين > < قم باستعمال الكتابة السابقة فيما سأعطيك لاحقا.
تحميل الصور
الحقيقة أن ٺثبيت الـ SDL_imageأصعب بمئة مرة من استعمالها ! إنه عليك أنت تحديد صعوبة العمل بالمكتبة !
توجد دالة وحيدة عليك معرفتها . IMG_Load :
و هي تستقبل معاملا واحدا :اسم الملف الذي نريد فتحه.
و هذا أمر عملي لأن هذه الدالة تتمكن من تحميل أي نوع من الملفات التي ٺتعامل معهاالـSDL_image
) GIF ،PNG ،JPGو حتى الـ ،TIFإلخ( .إذ تقوم وحدها بتحديد نوع الملف من خلال امتداده.
بما أن الـ SDL_Imageتستطيع أيضا ً فتح الصور ،BMPفيمكنك الآن نسيان أمر استعمال الدالة
SDL_LoadBMPو استعمال الدالة IMG_Loadلتحميل كل أنواع الصور.
شيء جيد آخر :إذا كانت الصورة التي تحم ّلها تملك الشفافية )كما هو حال الصور PNGو (GIFفإ ّن
SDL_Imageتفعّل تلقائيّا الشفافية من أجل هذه الصورة ! مما يعني عدم وجود دا ٍع لاستدعاء الدالة
. SDL_SetColorKey
344
SDL_Imageتحميل صيغ صور أخرى باستعمال الـ
. و إظهارهاsapin.png سأ ٌق ّدم لك الشفرة المصدر ية التي تقوم بتحميل الصورة
SDL_SetColorKey كما أنني لا استدعي الدالةSDL/SDL_image.h لاحظ جيدا أنني قمت بتضمين
. التي استعملها شفافة طبيعي ّاPNG لأن الصورة
. SDL_LoadBMP في ك ّل مكان بالشفرة و ذلك بتعو يض الدالةIMG_Load سترى أنني أستعمل الدالة
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <SDL/SDL.h>
4 /* including the header of SDL_image (adapt your directory) */
5 #include <SDL/SDL_image.h>
6 void pause();
7 int main(int argc, char *argv[])
8
{
9 SDL_Surface *screen = NULL, *backgoundImage = NULL, *sapin =
NULL;
10 SDL_Rect backgoundPosition, sapinPosition;
11 backgoundPosition.x = 0;
12 backgoundPosition.y = 0;
13 sapinPosition.x = 500;
14 sapinPosition.y = 260;
15 SDL_Init(SDL_INIT_VIDEO);
16 SDL_WM_SetIcon(IMG_Load(”sdl_icone.bmp”), NULL);
17 screen = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
18 SDL_WM_SetCaption(”Loading images on SDL”, NULL);
19 backgoundImage = IMG_Load(”lac_en_montagne.bmp”);
20 SDL_BlitSurface(backgoundImage, NULL, screen, &
backgoundPosition);
21 /* Loading a PNG image with IMG_Load
22 We won’t have any problem because the PNG image contains the
transparency information inside */
23 sapin = IMG_Load(”sapin.png”);
24 SDL_BlitSurface(sapin, NULL, screen, &sapinPosition);
25 SDL_Flip(screen);
26 pause();
27 SDL_FreeSurface(backgroundImage);
28 SDL_FreeSurface(sapin);
29 SDL_Quit();
30 return EXIT_SUCCESS;
31 }
32 void pause()
33 {
34 int cont = 1;
35 SDL_Event event;
36 while (cont)
37 {
38 SDL_WaitEvent(&event);
39 switch(event.type)
40 {
41 case SDL_QUIT:
42 cont = 0;
345
} 43 الفصل ج .3.إظهار صور
} 44 كما يمكننا الملاحظة ،فقد تم دمج الصورة مع الخلفية بشكل ممتاز :
} 45
مل ّخص
• تسمح الـ SDLبتحميل صور على مساحات .افتراضي ّا ،هي تسمح بالتعامل مع الصور ذات الصيغة BMP
باستعمال الدالة . SDL_LoadBMP
• يمكننا تعر يف لون شفاف باستعمال الدالة . SDL_SetColorKey
• يمكننا جعل الصورة أكثر أو أقل شفافية و ذلك باستعمال الدالة . SDL_SetAlpha
• المكتبة SDL_imageتسمح بإدخال صور من أي ّة صيغة كانت ) (… ،PNG ،JPGباستعمال الدالة
. IMG_Loadلـكن علينا تسطيب هذه المكتبة بالإضافة إلى الـ.SDL
346
الفصل ج4.
معالجة الأحداث
معالجة الأحداث هو من أهم الأساسيات في الـ.SDL
و رب ّما قد يكون الشطر الأكثر شغفا ًلاكتشافه .لأنه انطلاقا من هنا ستبدأ فعلا ًفي التحكّم في تطبيقك.
ك ّل من مرفقات الحاسوب )فأرة ،لوحة مفاتيح (… ،قادرة على إنتاج حدث .سنتعلّم كيف نستقبل كل
حدث و نتعامل معه .تطبيقك سيصبح أخيرا ً تفاعلي ّا !
فعليا ً ،ما هو الحدث ؟ الحدث هو عبارة عن إشارة ) (signalيتم إرسالها عن طر يق إحدى مرفقات
الحاسوب )) (peripheralsأو عن طر يق نظام التشغيل بذاته( إلى التطبيق .هذه أمثلة عن بعض الأحداث
المألوفة :
• حينما يضغط الم ُستعمل على زر من لوحة المفاتيح.
• و أيضا ًحينما ينقر بالفأرة.
• حينما يحر ّك الفأرة.
• حينما يقوم بتصغير النافذة.
• حينما يطلب إغلاق النافذة.
• إلى آخره.
الهدف من هذا الدرس هو تعلّم كيفية معالجة الأحداث .يمكنك أخيرا ًالقول للحاسوب ” :إذا نقر المستعمل
في هذا المكان ،قم بفعل كذا ،و إن لم يفعل ،قم بكذا .إذا حرّك الفأرة ،قم بكذا .إذا ضغط على الزر ، Q
أوقف البرنامج .إلخ”.
347
الفصل ج .4.معالجة الأحداث
ج 1.4.مبدأ عمل الأحداث
لنتعوّد على الأحداث ،سنتعلّم كيف نتعامل مع أسهل حدث :طلب غلق البرنامج .هذا حدث يـ ُنت ُج حينما
يقوم المستعمل بالنقر على الزر : X
إنه فعلا ً الحدث الأكثر سهولة .إضافة على ذلك ،هو حدث قد استعملته سابقا ً دون أن تعلم بذلك لأنه
متواجد في الدالة ! pause
بالفعل ،دور هذه الدالة هو انتظار المستعمل حت ّى يقررّ غلق البرنامج ،لأننا لو لم نستعملها كانت النافذة لتظهر و
تختفي بسرعة البرق !
يمكنك من الآن نسيان الدالة . pauseقم بحذفها من الشفرة المصدر ية لأننا سنتعلّم كيف نكتب
محتواها بأنفسنا.
متغيّر الحدث
لمعالجة الأحداث ،ستحتاج إلى التصريح عن متغيّر )واحد فقط ،كن متأكدا( من نوع . SDL_Event
فلتقم بتسميته بالاسم الذي يحلو لك ،أنا سأسم ّيه ، eventو هي تعني ”حدث” بالإنجليز ي ّة.
;1 SDL_Event event
من أجل اختبارات الشفرة ،سنستعمل دالة mainبسيطة للغاية تقوم بإظهار نافذة فقط ،مثلما رأينا في
الفصول الأولى .هذا ما يجب أن تبدو عليه الدالة : main
1 )][int main(int argc, char *argv
2
{
3 ;SDL_Surface *screen = NULL
4
SDL_Event event; // This variable will help us to manage the
5 events
6 ;)SDL_Init(SDL_INIT_VIDEO
7 ;)screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE
8 ;)SDL_WM_SetCaption(”Managing the events in SDL”, NULL
9 ;)(SDL_Quit
} 10 ;return EXIT_SUCCESS
إذا ،هي شفرة بدائية جدا ً ،و هي لا تحوي سوى شيء جديد :تعر يف المتغير eventالذي سنستعين به
قريبا ً.
جرّب الشفرة :مثلما توق ّعنا ،يجدر بالنافذة أن تظهر و تختفي في لحظة.
348
مبدأ عمل الأحداث
حلقة الأحداث
حينما نريد انتظار حدث ،نستعمل غالبا ًحلقة .هذه الحلقة التكرار ية تستمر في الاشتغال مادُمنا لم نستقبل الحدث
المـ ُراد.
يجب علينا أن نستعمل متغيرا ً منطقيا ًلـكي يحدد لنا ما إن كان علينا البقاء في الحلقة أو الخروج منها.
أنشئ هذا المتغير و سم ّه مثلا : 1 cont
;1 int cont = 1
هذا المتغير المنطقي يأخذ القيمة 1في البداية لأننا نريد للحلقة أن ٺتكرر مادام المتغير contيحمل هذه القيمة
)صحيح( .ما إن يأخذ المتغير المنطقي القيمة ) 0خطأ( ،نخرج من الحلقة و يتوقف البرنامج.
هذا ما تبدو عليه الحلقة :
)1 while (cont
{2
3 // Dealing with the event
}4
هكذا إذا ً :لدينا لح ّد الآن حلقة غير منتهية لا تنتهي إلا إذا أخذ المتغير contالقيمة .0الأكثر أهمية هو
ما نكتبه في داخل تلك الحلقة.
استرجاع الحدث
الآن سنقوم باستدعاء دالة من الـ SDLلـكي نتحقق ما إن تم إنتاج حدث.
لدينا دالتان للقيام بهذا العمل ،لـكن كلا منه ُما تعمل بطر يقة مختلفة عن الأخرى :
• : SDL_WaitEventتقوم بانتظار إنتاج حدث .هذه الدالة نقول عنها تعطيلية لأنها توقف عمل البرنامج
مادام لم يتم إنتاج أي حدث.
• : SDL_PollEventهذه الدالة تقوم بنفس العمل لـكنها ليست تعطيلية .لأنها ُتخـبرنا ما إن تم انتاج
حدث أم لا ،فإن لم يكن هناك أي حدث فإنها تعيد التحكّم إلى البرنامج مباشرة.
هاتان الدال ّتان مهمّتان ،لـكن في حالتين مختلفتين.
لتبسيط الأمور ،إذا استعملت SDL_WaitEventفإن برنامجك لن يـ ُتعب كثيرا ًالمـ ُعالج لأنه سيتوقف مـ ُنتظرا ً
إنتاج حدث.
1إذا كنت تفكّر في تسميته continueفلا تفعل ،لأ ّنها كلمة مفتاحي ّة )محجوزة( ،و بالتالي لا يمكن استخدامها كاسم لمتغيّر.
349
الفصل ج .4.معالجة الأحداث
بالمـ ُقابل ،إذا استعملت ، SDL_PollEventسيقوم البرنامج بالعمل على الحلقة whileو استدعاء الدالة
SDL_PollEventبشكل غير معر ّف إلى حين انتاج حدث مُعين .و بهذا تستعمل الم ُعالج بنسبة .% 100
لـكن ألا يجب أن نستعمل دائما ًالدالة SDL_WaitEventبما أنها لا تستعمل المـ ُعالج كثيرا ً ؟
كل ّا ،لأنه توجد حالات لا يمكن الاستغناء فيها عن الدالة . SDL_PollEventو هي حالة الألعاب
التي يتم فيها تحديث الشاشة حتى و إن لم يكن هناك أي حدث.
فلنأخذ مثلا ًاللعبة : Tetrisتقوم الكتل بالنزول لوحدها ،لا يحتاج الم ُستعمل إلى إنتاج حدث من أجل حصول
هذا الأمر ! لو استعملنا ، SDL_WaitEventسيبقى البرنامج مـ ُع ّطلا و لن تتم ّكن من تحديث الشاشة لإنزال
الكتل !
ماذا تفعل SDL_WaitEventلـكي لا تستهلك من الـمُعالج كثيرا ً ؟
فبعد كل شيء ،الدالة ُمجـبرة على البقاء في حلقة غير منتهية لـكي تختبر ك ّل الوقت ما إن كان هناك حدث
أم لا ،أليس كذلك ؟
الحقيقة أنني كنت أطرح هذا السؤال قبل وقت قليل .الإجابة مع ّقدة قليلا ًلأنها تخ ّص الطر يقة التي يتحكّم
فيها النظام بالعملي ّات )) (Processesالبرامج التي هي في طور الاشتغال(.
إذا كنت تريد -لـكن ّي سأتح ّدث بسرعة ،-بالنسبة للدالة ، SDL_WaitEventعملي ّة البرنامج ت ُوضع في طور
الانتظار.
إذا فإن البرنامج لا يعمل عليه المعالج بعد تلك اللحظة.
سيتم ”إيقاظه” من طرف نظام التشغيل حينما يتم إنتاج حدث .يعني أن المعالج سيعود إلى العمل على البرنامج
في هذه اللحظة .هذا ما يشرح ل ِم َ لا يستهلك البرنامج من المعالج شيئا بينما يكون في طور انتظار الحدث.
أدري أن هذه المفاهيم تبدو مجر ّدة لك الآن .لـكنك لست ُمجـبرا ً على فهم كل هذا الآن لأنك ستبدأ في
التأقلم مع هذه المعلومات شيئا ًفي شيئا ًمع التطبيق.
الآن سنستعمل SDL_WaitEventلأن البرنامج سيبقى بسيطا باستخدامها .على أي حال فالتعامل مع هاتين
الدالتين لن يتغير من واحدة إلى أخرى.
يجب أن تبعث للدالة عنوان المتغير eventالذي يقوم بتخزين الحدث.
بما أن هذا المتغير ليس عبارة عن مؤش ّر )أعد رؤ ية طر يقة التصريح به أعلاه( ،سنستعمل الاشارة & قبل اسم
المتغير و ذلك لن ُعطي عنوانه :
;)1 SDL_WaitEvent(&event
بعد استدعاء هذه الدالة ،المتغير eventيحتوي إجبار يا ًحدثا ًما.
350