The words you are searching are inside this book. To get more targeted content, please make full-text search by clicking here.

ترجمة لدرس تعلّم البرمجة بلغة السي الخاص بموقع OpenClassrooms

Discover the best professional documents and content resources in AnyFlip Document Base.
Search
Published by Hamza Abbad, 2017-08-03 11:01:25

تعلّم البرمجة بلغة C

ترجمة لدرس تعلّم البرمجة بلغة السي الخاص بموقع OpenClassrooms

‫مُنـشئ المستو يات‬

‫أخيرا ً‪ ،‬تحميل المستوى من الملف لم يكن معقدا ً‪ .‬الف ّخ الوحيد الذي وُجب تجن ّبه هو التفكير في تحو يل القيمة‬
‫‪ ’0’ ASCII‬إلى الرقم ‪) 0‬نفس الشيء بالنسبة لـ ‪.(… ، 4 ، 3 ، 2 ، 1‬‬

‫ِحفظ المستوى ‪saveLevel‬‬

‫هذه الدالة أسهل ‪:‬‬
‫)]‪1 int saveLevel(int level[][NB_BLOCKS_HEIGHT‬‬
‫{‪2‬‬
‫;‪3 FILE* file = NULL‬‬
‫;‪4 int i = 0, j = 0‬‬
‫;)”‪5 file = fopen(”levels.lvl”, ”w‬‬
‫)‪6 if (file == NULL‬‬
‫;‪7 return 0‬‬
‫)‪8 for (i = 0 ; i < NB_BLOCKS_WIDTH ; i++‬‬
‫{‪9‬‬
‫)‪10 for (j = 0 ; j < NB_BLOCKS_HEIGHT ; j++‬‬
‫{ ‪11‬‬
‫;)]‪12 fprintf(file, ”%d”, level[j][i‬‬
‫} ‪13‬‬
‫} ‪14‬‬
‫;)‪15 fclose(file‬‬
‫;‪16 return 1‬‬
‫} ‪17‬‬

‫استعملت الدالة ‪ fprintf‬من أجل ”ترجمة” أعداد الجدول إلى حروف ‪ .ASCII‬كانت هنا الصعوبة‬
‫الوحيدة ‪ :‬تجب عدم كتابة ‪ 0‬و إنما ’‪. ’0‬‬

‫ج‪ 5.5.‬مُنـشئ المستو يات‬

‫هذا الأخير سهل الكتابة أكثر مما تتخيل‪.‬‬
‫بالمناسبة‪ ،‬هذه تقنية تسمح بز يادة عمر لعبتنا‪ ،‬فلما نتجاهلها ؟‬

‫هكذا تسري الأمور ‪:‬‬

‫• نستعمل الفأرة لوضع الكتل التي نريدها في النافذة‪.‬‬

‫• النقر باليمين يسمح بمسح الكتلة الذي ٺتواجد فوقها الفأرة‪.‬‬

‫• النقر باليسار يسمح بوضع شيء على الخر يطة‪ .‬هذا الشيء يكون مخز ّنا ً ‪ :‬افتراضي ّا‪ ،‬نقوم بوضع الجدران‬
‫بالنقر بيسار الفأرة‪ .‬يمكننا تغيير الشيء الذي نريد وضعه في الخر يطة بالضغط على الأزرار المتواجدة في‬

‫لوحة الأرقام ‪:‬‬

‫‪401‬‬

‫الفصل ج‪ .5.‬عمل تطبيقي ‪Mario Sokoban :‬‬

‫‪ .1‬جدار‪.‬‬
‫‪ .2‬صندوق‪.‬‬
‫‪ .3‬منطقة مُستهدفة‪.‬‬
‫‪ .4‬مكان انطلاق ‪.Mario‬‬
‫• بالضغط على ‪ S‬يتم حفظ المستوى‪.‬‬

‫• يمكننا الرجوع إلى القائمة الرئيسية بالضغط على ‪. Esc‬‬

‫التهيئات‬

‫بشكل عام‪ ،‬تشبه هذه الدالة‪ ،‬الدالة الخاصة باللعبة‪ .‬ولذلك فقط بدأت في كتابتها باستعمال ”نسخ‪-‬لصق” لدالة‬
‫اللعبة‪ ،‬و بعد ذلك قم ُت بنزع ما لا أحتاجُه و أضفت مميزات جديدة‪.‬‬

‫هذه كانت البداية ‪:‬‬

‫‪1‬‬ ‫)‪void editor(SDL_Surface* screen‬‬
‫‪2‬‬
‫{‬

‫‪3 SDL_Surface *wall = NULL, *box = NULL, *level = NULL, *mario‬‬

‫;‪= NULL‬‬

‫;‪4 SDL_Rect position‬‬

‫‪402‬‬

‫مُنـشئ المستو يات‬

‫;‪5 SDL_Event event‬‬
‫= ‪6 int cont = 1, leftClickInProgress = 0, rightClickInProgress‬‬

‫;‪0‬‬

‫;‪7 int currentObject = WALL, i = 0, j = 0‬‬
‫;}‪8 int map[NB_BLOCKS_WIDTH][NB_BLOCKS_HEIGHT] = {0‬‬
‫‪9 // Loading the objects and the level‬‬
‫;)”‪10 wall = IMG_Load(”wall.jpg‬‬
‫;)”‪11 box = IMG_Load(”box.jpg‬‬
‫;)”‪12 level = IMG_Load(”level.png‬‬
‫;)”‪13 mario = IMG_Load(”mario_bas.gif‬‬
‫))‪14 if (!loadLevel(map‬‬
‫;)‪15 exit(EXIT_FAILURE‬‬

‫هنا تجد تعر يف المتغيرات و التهيئات اللازمة‪.‬‬
‫تلاحظ أنني لا أقوم بتحميل إلا ‪ Mario‬واحد )المت ّجه نحو الأسفل(‪ .‬في الواقع‪ ،‬لن نقوم بتوجيه ‪ Mario‬بلوحة‬

‫المفاتيح و إنما نحتاج إلى ملصق يمث ّل وضعية الإنطلاق الخاصة به‪.‬‬

‫المتغير ‪ currentObject‬يحفظ الشيء الذي يختاره الم ُستعمل حاليا ً‪ .‬افتراضي ّا‪ ،‬هذا الشيء هو ‪. WALL‬‬
‫أي أننا في البداية إذا نقرنا بالزرّ اليسار سنقوم بوضع جدار‪ ،‬لـكن يمكن تغيير هذا بواسطة المستعمل و ذلك‬

‫بالضغط على ‪ 3 ، 2 ، 1‬أو ‪. 4‬‬

‫المتغيرات المنطقية ‪ leftClickInProgress‬و ‪ rightClickInProgress‬كما تشير أسماؤها‪،‬‬
‫تسمح بحفظ ما إن كان هناك نقر ياليمين حاليا ً )أي أن زر الفأرة مضغوط(‪ .‬سأشرح لك المبدأ لاحقا ً‪ .‬على‬

‫أي حال‪ ،‬هذه التقنية تسمح لنا بإضافة أشياء إلى الخر يطة بترك زر الفأرة مضغوطا ً‪ ،‬و إلا فسنكون مجـبرين على‬
‫الضغط على الزر عدة مرات من أجل وضع نفس الشيء ع ّدة مرات في الخر يطة في أمكنة مختلفة‪ ،‬و هذا أمر‬

‫مُتعب قليلا‪.‬‬

‫أخيرا ً‪ ،‬يتم تحميل الخر يطة المحفوطة حاليا ًفي الملف ‪ . levels.lvl‬سيكون نقطة انطلاقنا‪.‬‬

‫معالجة الأحداث‬

‫هذه المرة سيكون علينا معالجة كثير من الأحداث المختلفة‪ .‬هيا بنا‪ ،‬واحدا ً واحدا ً‪.‬‬

‫‪SDL_QUIT‬‬

‫‪1 case SDL_QUIT:‬‬
‫;‪2 cont = 0‬‬
‫;‪3 break‬‬

‫‪403‬‬

‫الفصل ج‪ .5.‬عمل تطبيقي ‪Mario Sokoban :‬‬

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

‫لتجد حلا ًبنفسك‪.‬‬

‫‪SDL_MOUSEBUTTONDOWN‬‬

‫‪1 case SDL_MOUSEBUTTONDOWN:‬‬
‫)‪2 if (event.button.button == SDL_BUTTON_LEFT‬‬
‫{‪3‬‬
‫‪4 // We put the chosen object (wall, box) in the click position‬‬
‫]‪5 map[event.button.x / BLOCK_SIZE][event.button.y / BLOCK_SIZE‬‬

‫;‪= currentObject‬‬

‫‪6 leftClickInProgress = 1; // We put in mind that there’s a‬‬

‫‪pushed button‬‬

‫}‪7‬‬
‫‪8 else if (event.button.button == SDL_BUTTON_RIGHT) // Right click to‬‬

‫‪erase‬‬

‫{‪9‬‬
‫]‪10 map[event.button.x / BLOCK_SIZE][event.button.y /BLOCK_SIZE‬‬

‫‪11‬‬ ‫;‪= EMPTY‬‬
‫} ‪12‬‬ ‫;‪rightClickInProgress = 1‬‬
‫;‪13 break‬‬

‫نبدأ باختبار الزر المضغوط )نرى ما إن كان ضغطا ًبالزر الأيسر أو الأيمن( ‪:‬‬

‫• إذا كان ضغطا بالزر الأيسر‪ ،‬نقوم بوضع الشيء الحالي ‪ currentObject‬على الخر يطة في الموضع‬
‫الذي تشير إليه الفأرة‪.‬‬

‫• إذا كان ضغطا بالزر الأيمن‪ ،‬نمسح مايوجد في الموضع الحالي للفأرة )نضع ‪ EMPTY‬كما سبق و قل ُت‬
‫لك(‪.‬‬

‫كيف نعرف في أي ”خانة” من الخر يطة نحن متواجدون ؟‬

‫نعرف ذلك عن طر يق عملية حسابية صغيرة‪ .‬يكفي أن نأخذ إحداثيات الفأرة ) ‪event.button.x‬‬
‫مثلا ً( و نقسم هذه القيمة على حجم كتلة ‪. BLOCK_SIZE‬‬

‫هذه قسمة لأعداد صحيحة‪ .‬و بما أن قسمة الأعداد الصحيحة في لغة ‪ C‬ت ُعطي عددا ً صحيحا‪ ،‬فنتح ّصل بالتأكيد‬
‫على قيمة توافق خانة من الخر يطة‪.‬‬

‫‪404‬‬

‫مُنـشئ المستو يات‬

‫مثلا ً‪ ،‬لو أنني في البيكسل الـ‪ 75‬من الخر يطة )على محور الفواصل ‪ ،(x‬أقسم هذا العدد على ‪BLOCK_SIZE‬‬
‫و التي تساوي هنا ‪ .34‬يكون لدينا هنا ‪:‬‬

‫‪75/34 = 2‬‬

‫‪ .‬لا تنس هنا أننا نتجاهل باقي القسمة و نقوم بحفظ الجزء الصحيح فقط لأننا نتكلم عن قسمة أعداد صحيحة‪.‬‬
‫نحن نعلم إذا أننا نتواجد في الخانة رقم ‪) 2‬أي الخانة الثالثة لأن الجدول يبدأ من ‪ ،0‬لا تنس ذلك(‪.‬‬

‫مثال آخر ‪ :‬لو أنني في البيكسل العاشر )أي أنني قريب من الحافة(‪ ،‬ستكون لدينا العملية الحسابية التالية ‪:‬‬

‫‪10/34 = 0‬‬

‫أي أننا في الخانة رقم ‪! 0‬‬

‫بفضل هذه العملية الحسابية البسيطة يمكننا أن نعرف في أي خانة من الخر يطة نحن متواجدون‪.‬‬
‫= ]‪1 map[event.button.x / BLOCK_SIZE][event.button.y / BLOCK_SIZE‬‬

‫;‪currentObject‬‬

‫شيء آخر مهم ‪ :‬إعطاء القيمة ‪ 1‬للمتغير المنطقي ‪) leftClickInProgress‬أو ‪rightClickInProgress‬‬
‫حسب الحالة( يسمح لنا بمعرفة‪ ،‬خلال حدث ‪ ، MOUSEMOTION‬ما إن كان زر الفأرة مضغوطا ً خلال‬

‫الإنتقال‪.‬‬

‫‪SDL_MOUSEBUTTONUP‬‬

‫‪1 case SDL_MOUSEBUTTONUP: // We disable the boolean which indicates‬‬

‫‪that there’s a clicked button‬‬

‫)‪2 if (event.button.button == SDL_BUTTON_LEFT‬‬
‫;‪3 leftClickInProgress = 0‬‬
‫)‪4 else if (event.button.button == SDL_BUTTON_RIGHT‬‬
‫;‪5 rightClickInProgress = 0‬‬
‫;‪6 break‬‬

‫الحدث ‪ MOUSEBUTTONUP‬يقوم ببساطة بإعادة القيمة ‪ 0‬للمتغير المنطقي‪ .‬نحن نعرف بأن النقر انتهى و‬
‫بهذا لا يوجد أي ”نقر حالي” بالفأرة‪.‬‬

‫‪SDL_MOUSEMOTION‬‬

‫‪1 case SDL_MOUSEMOTION:‬‬
‫‪2 if (leftClickInProgress) // If we move the mouse and the left button‬‬

‫‪is clicked‬‬

‫{‪3‬‬
‫]‪4 map[event.motion.x / BLOCK_SIZE][event.motion.y /BLOCK_SIZE‬‬

‫;‪= currentObject‬‬

‫‪405‬‬

‫الفصل ج‪ .5.‬عمل تطبيقي ‪Mario Sokoban :‬‬

‫}‪5‬‬
‫‪6 else if (rightClickInProgress) // The same thing for the right button‬‬
‫{‪7‬‬
‫]‪8 map[event.motion.x / BLOCK_SIZE][event.motion.y / BLOCK_SIZE‬‬

‫;‪= EMPTY‬‬

‫}‪9‬‬
‫;‪10 break‬‬

‫هنا يمكن لنا رؤ ية أهمية المتغيرات المنطقية‪ .‬نختبر حينما نقوم بتحر يك الفأرة ما إن كان هناك نقر حالي‪.‬‬
‫إذا كانت هذه هي الحالة‪ ،‬نضع على الخر يطة شيئا ًما )أو الفراغ إذا كان نقرا باليمين(‪.‬‬

‫هذا يسم ُح لنا بوضع شيء واحد لعدة مرات دون الحاجة إلى إلى النقر في ك ّل مرة من أجل ك ّل تكرار للشيء‪،‬‬
‫يكفي إذا ً أن ن ُبقي زر الفأرة مضغوطا ًبينما نسح ُب هذه الأخيرة‪.‬‬

‫الأمر واضح ‪ :‬في ك ّل مرة نحر ّك فيها الفأرة )يكون ذلك ببيكسل واحد(‪ ،‬نختبر ما إن كانت المتغيرات‬
‫المنطقية مفعّلة‪ .‬إذا كان الأمر كذلك‪ ،‬نقوم بوضع شيء على الخر يطة‪ .‬و إلا ً‪ ،‬لا نقوم بأي شيء‪.‬‬

‫مل ّخص ‪ :‬سألخ ّص التقنية لأنها ستكون مفيدة من أجل برامج أخرى‪.‬‬
‫تسمح هذه التقنية بمعرفة ما إن كان زر الفأرة مضغوطا ً بينما يتم تحر يك هذه الأخيرة‪ .‬يمكننا أن نستفيد من‬

‫هذا الأمر لبرمجة السحب و الإفلات )‪.(drag and drop‬‬

‫‪ .1‬خلال حدث ‪ : MOUSEBUTTONDOWN‬نعطي القيمة ‪ 1‬للمتغير المنطقي ‪. clickInProgress‬‬

‫‪ .2‬خلال حدث ‪ : MOUSEMOTION‬نختبر ما إن كان المتغير المنطقي ‪ clickInProgress‬يساوي‬
‫”صحيح”‪ .‬إذا كان الأمر كذلك فسنعرف أننا نقوم بالسحب باستخدام الفأرة‪.‬‬

‫‪ .3‬خلال حدث ‪ : MOUSEBUTTONUP‬نعيد القيمة ‪ 0‬للمتغير المنطقي ‪ clickInProgress‬لأن النقر‬
‫قد انتهى )إفلات زر الفأرة(‪.‬‬

‫‪SDL_KEYDOWN‬‬

‫تسمح أزرار لوحة المفاتيح بتحميل و حفظ المستوى و أيضا ًبتغيير الشيء المخُتار من أجل النقر اليساري بالفأرة‪.‬‬
‫‪1 case SDL_KEYDOWN:‬‬
‫)‪2 switch(event.key.keysym.sym‬‬
‫{‪3‬‬
‫‪4 case SDLK_ESCAPE:‬‬
‫;‪5 cont = 0‬‬
‫;‪6 break‬‬
‫‪7 case SDLK_s:‬‬
‫;)‪8 saveLevel(map‬‬

‫‪406‬‬

‫مُنـشئ المستو يات‬

9 break;
10 case SDLK_c:
11
12 loadLevel(map);
13 break;
14 case SDLK_KP1:
15 currentObject = WALL;
16 break;
17 case SDLK_KP2:
18 currentObject = BOX;
19 break;
20 case SDLK_KP3:
21 currentObject = GOAL;
22 break;
23 case SDLK_KP4:
24 currentObject = MARIO;
25 } break;
26 break;

‫ نقوم بحفظ المستوى إذا‬،‫ نقوم بتغيير الشيء إذا تم الضغط على الأرقام في اللوحة‬.‫هذه الشفرة سهلة للغاية‬
. C ‫ و نقوم بتحميل آخر مستوى تم حفظه بالنقر على‬S ‫تم الضغط على‬

! ‫وقت اللصق‬

.‫ لقد أتتممنا ك ّل الأحداث‬: ‫ها نحن ذا‬
‫ الشفرة التالية تشبه الشفرة التي‬.‫ لم يتب ّق لنا سوى لصق كل عناصر الخر يطة بمساعدة حلقتين متداخلتين‬،‫الآن‬

: ‫ سأعطيها لك لـكن ّي لن أعيد شرحها هنا‬.‫استعملناها في دالة اللعبة‬

1 // Clearing the screen
2 SDL_FillRect(screen, NULL, SDL_MapRGB(screen−>format, 255, 255, 255))

;

3 // Placing the objects in the screen

4 for (i = 0 ; i < NB_BLOCKS_WIDTH ; i++)

5{

6 for (j = 0 ; j < NB_BLOCKS_HEIGHT ; j++)

7{

8 position.x = i * BLOCK_SIZE;
9 position.y = j * BLOCK_SIZE;

10 switch(map[i][j])

11 {

12 case WALL:

13 SDL_BlitSurface(wall, NULL, screen, &position

);

14 break;
15 case BOX:
16 SDL_BlitSurface(box, NULL, screen, &position)

;

407

‫الفصل ج‪ .5.‬عمل تطبيقي ‪Mario Sokoban :‬‬

‫‪17‬‬ ‫;‪break‬‬
‫‪18‬‬ ‫‪case GOAL:‬‬
‫‪19‬‬ ‫& ‪SDL_BlitSurface(level, NULL, screen,‬‬

‫‪20‬‬ ‫;)‪position‬‬
‫‪21‬‬ ‫;‪break‬‬
‫‪22‬‬ ‫‪case MARIO:‬‬
‫& ‪SDL_BlitSurface(mario, NULL, screen,‬‬
‫‪23‬‬
‫} ‪24‬‬ ‫;)‪position‬‬
‫} ‪25‬‬ ‫;‪break‬‬
‫} ‪26‬‬
‫‪27 // Updating the screen‬‬
‫;)‪28 SDL_Flip(screen‬‬

‫لا يجب أن ننسى أن نحرر الذاكرة بعد الانتهاء من الحلقة الرئيسية بالشكل اللازم )باستعمال ‪( SDL_FreeSurface‬‬
‫‪:‬‬

‫;)‪1 SDL_FreeSurface(wall‬‬
‫;)‪2 SDL_FreeSurface(box‬‬
‫;)‪3 SDL_FreeSurface(level‬‬
‫;)‪4 SDL_FreeSurface(mario‬‬

‫حسنا ً‪ ،‬إنتهينا من التنظيف !‬

‫مل ّخص و تحسينات‬

‫حسنا ًلقد انتهينا من ك ّل شيء و حان وقت التلخيص !‬

‫هي ّا فلنل ّخص !‬

‫و ماذا سيكون أحسن تلخيص من الشفرة المصدر ية الكاملة للعبة مع التعلقيات المف ّصلة ؟‬
‫بسبب عدم رغبتي في كتابة عشرات الصفحات من الشفرة تشمل ك ّل ما رأيناه إلى ح ّد الآن‪ ،‬أف ّضل أن‬

‫تقوم بتنز يل الشفرة المصدر ي ّة الكاملة مع الملف التنفيذي )الم ُترجم للويندوز(‪.‬‬

‫‪https://openclassrooms.com/uploads/fr/ftp/mateo21/mario_sokoban.zip‬‬
‫)‪(436 Ko‬‬

‫الملف ‪ .zip‬يحتوي ‪:‬‬

‫• الملف التنفيذي للويندوز )إذا كنت تعمل على نظام تشغيل آخر‪ ،‬تكفي إعادة الترجمة(‪.‬‬
‫• الملفات ‪ DLL‬الخاصة بالـ‪ SDL‬و الـ‪.SDL_Image‬‬

‫‪408‬‬

‫مل ّخص و تحسينات‬
‫• كل الصور التي تحتاجها في البرنامج )هي نفسها التي قمت بتحميلها في حزمة ”‪ ”sprites‬أعلاه(‪.‬‬
‫• الملفات المصدر ية الكاملة الخاصة بالبرنامج‪.‬‬

‫• الملف ‪ .cbp‬الخاص بمشروع ‪ .Code::Blocks‬إذا أردت فتح المشروع باستعمال بيئة تطوير ية أخرى‪،‬‬
‫قم بإنشاء مشروع ‪ ،SDL‬أضف إليه يدو يا ًكل الملفات ‪ .h‬و ‪ . .c‬الأمر ليس صعبا ً‪ ،‬سترى‪.‬‬

‫تلاحظ أن المشروع يحوي‪ ،‬بالإضافة إلى الملفات ‪ .h‬و ‪ ، .c‬ملفا مصدر يا ‪ . ressources.rc‬إنه‬
‫ملف يمكن إضافته للمشروع )فقط على الويندوز( و يسمح بإدخال ملفات في الملف التنفيذي‪ .‬هنا‪ ،‬استعنت‬
‫به لإدخال أيقونة في الملف التنفيذي‪ .‬و هذا يسمح بإعطاء أيقونة للملف التنفيذي مرئية في الويندوز‪ ،‬أنظر‬

‫الصورة التالية ‪:‬‬

‫ا ِعترف بذلك‪ ،‬صنع أيقونة من أجل البرنامج أمر أجمل من ترك الأيقونة الافتراضي ّة !‬
‫يمكنك قراءة المزيد عن هذه التقني ّة في درس ”إنشاء أيقونة لبرنامجك” المتوف ّر على هذا الرابط ‪:‬‬

‫‪http://www.siteduzero.com/tutoriel-3-14177-creer-une-icone-pour-son-programme.‬‬
‫‪html‬‬

‫ح ّسن اللعبة !‬

‫ألا ترى بأن هذا البرنامج غير مثالي‪ ،‬و أبعد من أي يكون كذلك ؟‬
‫هل تريد أفكارا ً للتطوير ؟‬

‫• ينقص دليل استعمال‪ ،‬حيث يتم إظهار شاشة قبل انطلاق المرحلة و قبل انطلاق مـ ُنشئ المستو يات‪.‬‬
‫نقوم بشرح الأزرار اللازمة لـكي يستعملها اللاعب‪.‬‬
‫‪409‬‬

‫الفصل ج‪ .5.‬عمل تطبيقي ‪Mario Sokoban :‬‬

‫• في مـ ُنشئ المستو يات‪ ،‬اللاعب لا يعرف أ ّي شيء ُمختار حاليا ً‪ .‬سيكون من الجي ّد أن الشيء المختار حالي ّا‬
‫يتبع مؤش ّر الفأرة‪ .‬هكذا سيعرف المستخدم مال ّذي سيوضع على الخر يطة‪ .‬الأمر سهل للتطبيق‪ ،‬لقد قمنا‬

‫بجعل ‪ Zozor‬يتبع الفأرة في الفصل السابق !‬

‫• يمكن أن نبدأ مستوى ما بوجود بعض الصناديق الموضوعة أساسا ًفوق المناطق المستهدفة ) ‪.( BOX_OK‬‬
‫لقد رأيت كثيرا من المستوبات تبدأ بصناديق في مكان مناسب هذا )لا يعني أن المستوى سهل‪ ،‬فقد‬

‫يكون عليك تحر يك الصندوق من مكانه في مرحلة من مراحل الجولة(‪.‬‬
‫• في مـ ُنشئ المستو يات أيضا ً‪ ،‬يجب أن نمنع المستعمل من أن يضع مو ِضعي إنطلاق لللاعب في نفس‬

‫الخر يطة !‬
‫• حينما ننجح في مُستوى‪ ،‬نرجع مباشرة إلى القائمة الرئيسية‪ .‬هذا أمر ف ّض نوعا ًما‪ ،‬ما رأيك بـإظهار رسالة‬

‫في وسط الشاشة ‪” :‬هنيئا ً‪ ،‬لقد نجحت في المستوى” ؟‬
‫• أخيرا ً‪ ،‬سيكون من الجيد أن يتمكن البرنامج من التحكم في عدة مستو يات في المرة الواحدة‪ .‬سيكون علينا‬
‫بناء رحلة لعب تستمر لـ‪ 20‬مستوى مثلا ً‪ .‬سيكون الأمر أصعب قليلا ً من ناحية البرمجة‪ ،‬لـكن يمكن‬
‫القيام به‪ .‬يجب عليك التعديل في شفرة اللعبة و أيضا ً في شفرة مـ ُنشئ المستو يات‪ .‬أنصحك بأن تضع‬

‫مستوى واحدا ً في السطر الواحد بالملف ‪. levels.lvl‬‬

‫كما وعدتك‪ ،‬هذا ممكن‪ ،‬و لقد فعلته ! لن أعطيك الشفرة المصدر ية الخاصة بهذه التحسينات )أعتقد أن ّي‬
‫أعطيتك الـكثير إلى ح ّد الآن !(‪ ،‬و لـكن ّي سأعطيك مباشرة الملف التنفيذي مُترجما ًللويندوز و اللينكس‪.‬‬

‫اللعبة تحتوي على مغامرة من ‪ 20‬مستوى تختلف صعوبتها )من سهل جدا ً إلى …شديد الصعوبة(‪.‬‬
‫لـكي أتم ّكن من تحقيق بعض المستو يات‪ ،‬احتجت لز يارة موقع شخص مهووس بلعبة ‪: Sokoban‬‬

‫‪http://sokoban.online.fr/‬‬

‫هاهي اللعبة المح ّسنة للويندوز و اللينكس ‪:‬‬

‫_‪Windows : https://openclassrooms.com/uploads/fr/ftp/mateo21/mario_sokoban‬‬
‫)‪setup.exe (656 Ko‬‬

‫_‪Linux : https://openclassrooms.com/uploads/fr/ftp/mateo21/mario_sokoban‬‬
‫)‪linux.tar.gz (64 Ko‬‬

‫لقد استعنت بالبرنامج ‪ Inno Setup‬من أجل صنع برنامج التسطيب‪ ،‬يمكنك القراءة بهذا الخصوص على هذا‬
‫الرابط ‪:‬‬

‫‪http://www.siteduzero.com/tutoriel-3-14171-creer-une-installation.html‬‬

‫‪410‬‬

‫الفصل ج‪6.‬‬

‫تحكّم في الوقت !‬

‫لهذا الفصل أهمية كبيرة ‪ :‬سيعل ّمك كيف تتحكم في الوقت بالـ‪ .SDL‬إنه لمن النادر أن نقوم بإنشاء برنامج ‪SDL‬‬
‫لا تحتاج إلى دوال خاصة بالتحكم في الوقت‪ ،‬بالرغم من أن لعبة ‪ Mario Sokoban‬كانت حالة خاصة‪ .‬رغم‬

‫ذلك‪ ،‬في معظم الألعاب‪ ،‬إدارة الوقت هي شيء أساس ّي‪.‬‬
‫مثلا ً‪ ،‬كيف لك أن ت ٌنشئ لعبة ‪ Tetris‬أو ‪ Snake‬؟ يجب فعلا ًعلى الكُتل أن تتحر ّك كل ‪ X‬ثانية‪ ،‬و هذا ما‬

‫لا تجيد فعله‪ .‬على الأقل‪ ،‬قبل أن تقرأ هذا الفصل‪.‬‬

‫ج‪ 1.6.‬الـ‪ Delay‬و الـ‪Ticks‬‬

‫في بادئ الأمر‪ ،‬سنتعلّم كيف نستعمل دالتين بسيطتين جدا ً ‪:‬‬
‫• ‪ : SDL_Delay‬تسمح بتوقيف البرنامج مؤقتا لعدد من الميل ّي ثواني‪.‬‬
‫• ‪ : SDL_GetTicks‬تقوم بإرجاع عدد الميل ّي ثواني التي مضت منذ إنطلاق تشغيل البرنامج‪.‬‬

‫هاتان الدال ّتان سهلتان جدا ً كما سنرى لـك ّن استعمالهما ليس بسيطا ًكما يبدو الأمر عليه‪.‬‬

‫‪SDL_Delay‬‬

‫كما قل ُت‪ ،‬تقوم هذه الدالة بإيقاف عمل البرنامج لم ّدة مح ّددة‪ .‬حينما يكون البرنامج متوق ّفا‪ ،‬نقول أن ّه ”ينام”‬
‫)‪ : (sleep‬هو لا يستعمل الم ُعالج‪.‬‬

‫يمكن إذا استعمال ‪ SDL_Delay‬للإنقاص من زمن اشتغال الم ُعالج )‪ .(Processor‬لاحظ أن ّني‬
‫سأختصره إلى ‪ CPU‬و هذا الإختصار متداول و يوافق العبارة ”‪ ”Central Processing Unit‬و ال ّتي تعني‬

‫‪411‬‬

‫الفصل ج‪ .6.‬تحكّم في الوقت !‬

‫”وحدة المعالجة المركز ي ّة”‪.‬‬
‫بفضل الـ ‪ SDL_Delay‬يمكنك جعل برامجك أق ّل شراهة لموارد المعالج‪ .‬أي أننا لن نثقل على الحاسوب كثيرا ً‬

‫إذا تم ّ استخدام هذه الدالة بذكاء‪.‬‬

‫هذا كل ّه يعتمد على البرنامج الذي تـ ُنشؤه ‪ :‬أحيانا ً‪ ،‬نجد أنه من المستحسن أن يستعمل البرنامج المعالج بشكل‬
‫أق ّل‪ ،‬يمكن في نفس الوقت أن يقوم الم ُستعمل بشيء آخر مثلما هو الحال بالنسبة لقارئ ‪ MP3‬الذي يشتغل‬

‫في الخلفية ريثما تقوم بالتص ّفح عبر الإنترنت‪.‬‬
‫لـكن أحيانا ً‪ ،‬نحتاج للبرنامج أن يستعمل المعالج بنسبة ‪ ،100%‬و هو الحال بالنسبة لغالبية الألعاب‪.‬‬

‫لنعد إلى الدالة‪ ،‬هذا نموذجها و هو بسيط للغاية ‪:‬‬

‫;)‪1 void SDL_Delay(Uint32 ms‬‬

‫الأمر واضح‪ ،‬تبعث للدالة عدد الميل ّي ثواني التي يجب أن ”ينام” البرنامج خلالها‪.‬‬

‫مثلا ً‪ :‬إذا أردت أن ينام البرنامج لم ّدة ثانية واحدة‪ ،‬يجب عليك كتابة ‪:‬‬

‫;)‪1 SDL_Delay(1000‬‬

‫لا تنس أ ّنها بالميلي ثانية ‪:‬‬

‫• ‪ 1000‬ميل ّي ثانية = ثانية‪.‬‬
‫• ‪ 500‬ميل ّي ثانية = نصف ثانية‪.‬‬

‫• ‪ 250‬ميل ّي ثانية = رُبع ثانية‪.‬‬

‫لا يمكنك فعل أي شيء في البرنامج بينما هو متوق ّف مؤق ّتا ! فالبرنامج ”النائم” لا يمكن له فعل أي شيء‬
‫لأنه ليس مفعّلا بالنسبة للحاسوب‪.‬‬

‫مشكل جزئيّة الوقت‬

‫لا‪ ،‬تأكّد بأنني لن أخوض في درس للفيز ياء الكمي ّة في هذا الفصل حول الـ‪ ! SDL‬و مع ذلك‪ ،‬أرى بأن‬
‫هناك أمورا يجب عليك معرفتها ‪ SDL_Delay :‬ليست دالة ”مثالية”‪ .‬و هذا ليس خطأها‪ ،‬بل هو خطأ نظام‬

‫التشغيل )‪.(… Mac OS X ،GNU/Linux ،Windows‬‬

‫‪412‬‬

‫الـ‪ Delay‬و الـ‪Ticks‬‬

‫لماذا يتدخّل نظام التشغيل هنا ؟ ببساطة لأنه هو الذي يتحكّم في البرامج المشغّلة ! فبرنامجك سيقول للنظام ‪:‬‬
‫”سأنام‪ ،‬أيقظني بعد ثانية”‪ .‬لـكن لن يقوم النظام دائما بإفاقة البرنامج بعد ثانية بالضبط‪.‬‬

‫في الواقع‪ ،‬قد يكون هناك تأ ّخر بسيط )تأخر ‪ 10‬ميل ّي ثانية بالتقريب كمع ّدل‪ ،‬هذا يختلف حسب الحاسوب(‪.‬‬
‫لماذا ؟ لأن ‪ CPU‬لا يمكنه العمل إلا على برنامج واحد في المر ّة الواحدة‪ .‬دور نظام التشغيل يتمث ّل في إخبار ‪CPU‬‬
‫بخصوص ما يجب أن يتم القيام به و لهذا ‪” :‬لم ّدة ‪ 40‬ميل ّي ثانية ستعم ُل على ‪ firefox.exe‬ثم لم ّدة ‪110‬‬
‫ميل ّي ثانية على ‪ ، explorer.exe‬بعد ذلك‪ ،‬لمدة ‪ 80‬ميل ّي ثانية ستعمل على ‪ program_sdl.exe‬ثم عد‬
‫إلى العمل على ‪ firefox.exe‬لم ّدة ‪ 65‬ميل ّي ثانية …” نظام التشغيل هو بالفعل عبارة عن قائد الأوركسترا !‬

‫تخي ّل الآن أنه لثانية من الزمن‪ ،‬يكون برنامج آخر لازال في طور الإشتغال ‪ :‬يجب أن ينتهي عمله حتى يستطيع‬
‫برنامجك ”استعادة التحكّم” على ‪.CPU‬‬

‫ما الذي يجب تذكّره ؟ أن ‪ CPU‬لا يمكنه أن يتحكّم في برنامجـين في نفس آن واحد‪ .‬و لـكي يعطي انطباعا ًبأنه‬
‫ي ُشغّل العديد من البرامج في نفس اللحظة‪ ،‬يقوم بتقسيم الوقت بين هذه البرامج حيث تعم ُل دورا ً بدور‪.‬‬
‫ق َل ّت صح ّة هذا الكلام لأن المعالجات ”ثنائية النوى” لها القدرة على تشغيل برنامجـين في نفس الآن‪.‬‬

‫لـكن هذه التقنية في التحكّم بالبرامج مع ّقدة للغاية و لن نحصل على ضمانات بأ ّن البرنامج الخاص بنا سيتم ّ إيقاظة‬
‫في ثانية بالضبط من الزمن‪.‬‬

‫مع ذلك‪ ،‬يعتمد الأمر دائما ًعلى الحاسوب نفسه كما قل ُت سابقا ً‪ .‬عندي‪ ،‬ألاحظ أ ّن الدال ّة ‪SDL_Delay‬‬
‫دقيقة ج ّدا‪.‬‬

‫بسبب مشكل جزئيّة الوقت‪ ،‬لن تتم ّكن إذا من إيقاف برنامجك مؤق ّتا لوقت قصير ج ّدا من الزمن‪ ،‬أي أنه‬
‫لو استعملت ;)‪ SDL_Delay(1‬ستكون متأكدا ً بأن البرنامج لن ينام لـ‪ 1‬ميلي ثانية و إنما أكثر )حوالي‬

‫‪ 9‬أو ‪ 10‬ميلي ثانية(‪.‬‬

‫الدالة ‪ SDL_Delay‬عملي ّة‪ ،‬لـكن لا ٺثق بها كثيرا ً‪ .‬فهي لا توقف البرنامج بالمقدار الزمني الذي تحدده‬
‫أنت بالضبط‪.‬‬

‫هذا ليس راجعا ً لـكون الدالة غير مُبرمجة جيدا ً‪ ،‬لـكن لأن عمل الجهاز مع ّقد و لا يمكنه أن يكون دقيقا ً من‬
‫هذه الناحية‪.‬‬

‫‪SDL_GetTicks‬‬

‫هذه الدالة ت ُرجع عدد الميلي ثواني التي انقضت منذ بدأ عمل البرنامج‪ .‬و هي عبارة عن مؤشّر للزمن لا يمكن‬
‫الإستغناء عنه‪ .‬ستجد بأنها مفيدة لـكي تضع مراجع في الزمن‪ ،‬سترى ذلك !‬
‫هذا نموذجها ‪:‬‬

‫‪413‬‬

‫الفصل ج‪ .6.‬تحكّم في الوقت !‬
‫;)‪1 Uint32 SDL_GetTicks(void‬‬

‫هذه الدالة لا تنتظر أي معامل‪ ،‬و هي تقوم فقط بإرجاع عدد الثواني المنقضية‪.‬‬
‫هذا العدد يتصاعد مع مضي الزمن‪ .‬لمعلوماتك‪ ،‬التوثيق الخاص بالـ‪ SDL‬يشير إلى أن هذا العدد يصل إلى الحد‬
‫الأقصى ‪ 49‬يوما ثم يبدأ العدد من جديد ! لـكن يجدر بالبرنامج الذي تكتبه ألا يستمر ّ ك ّل هذا الوقت و لهذا‬

‫فلا تقلق من هذه الناحية‪.‬‬

‫إستعمال ‪ SDL_GetTicks‬لإدارة الوقت‬

‫إذا كانت الدالة ‪ SDL_Delay‬سهلة للفهم و للاسعمال‪ ،‬فالأمر ليس عينه بالنسبة لـ ‪. SDL_GetTicks‬‬
‫حان الوقت لنعرف كيف سنستفيد منها‪.‬‬

‫إليك هذا المثال‪ ،‬سنسترجع البرنامج القديم الذي يقوم بإظهار نافدة تحتوي على ‪) Zozor‬الصورة التالية( ‪:‬‬

‫هذه المرة‪ ،‬في عوض التحكّم في حركة ‪ Zozor‬بالفأرة أو بلوحة المفاتيح‪ ،‬سنعتمد فكرة أنه سيقوم بالتحر ّك‬
‫لوحده في الشاشة‪ .‬لبن ّسط الأمور‪ ،‬سنجعله يتحر ّك أفقيا في النافذة‪.‬‬

‫سن ُعيد استعمال نفس الشفرة التي استخدمناها في فصل الأحداث‪ ،‬يجدر بك أن تجيد كتابتها بنفسك دون‬
‫الحاجة لم ُساعدة مني‪ .‬و إلا‪ ،‬إذا احتجتها‪ ،‬يمكنك إستعادتها من الفصول السابقة‪.‬‬
‫‪414‬‬

Ticks‫ و الـ‬Delay‫الـ‬

1 int main(int argc, char *argv[])
2
{

3 SDL_Surface *screen = NULL, *zozor = NULL;
4 SDL_Rect zozorPosition;

5 SDL_Event event;

6 int cont = 1;

7 SDL_Init(SDL_INIT_VIDEO);

8 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE |

SDL_DOUBLEBUF);

9 SDL_WM_SetCaption(”Time management in SDL”, NULL);
10 zozor = SDL_LoadBMP(”zozor.bmp”);
11 SDL_SetColorKey(zozor, SDL_SRCCOLORKEY, SDL_MapRGB(zozor−>

format, 0, 0, 255));

12 zozorPosition.x = screen−>w / 2 − zozor−>w / 2;
13 zozorPosition.y = screen−>h / 2 − zozor−>h / 2;
14 SDL_EnableKeyRepeat(10, 10);
15 while (cont)
16 {
17 SDL_WaitEvent(&event);
18 switch(event.type)
19 {
20 case SDL_QUIT:
21 cont = 0;
22 break;
23 }
24 SDL_FillRect(screen, NULL, SDL_MapRGB(screen−>format,

25 255, 255, 255));
26 SDL_BlitSurface(zozor, NULL, screen, &zozorPosition);
27 SDL_Flip(screen);
28 }
29 SDL_FreeSurface(zozor);
30 SDL_Quit();
31 } return EXIT_SUCCESS;

. SDL_GetTicks ‫ سيكون من الأفضل استعمال‬،‫ من أجل هذا‬.‫ نريد أن نحر ّكه‬.Zozor‫فلنهتم بـ‬
ّ ‫ يستخدمان من أجل تخزين الوقت الذي تم‬. currentTime ‫ و‬previousTime : ‫سنحتاج إلى متغيرين‬

.‫ في لحظات زمنية مختلفة‬SDL_GetTicks ‫إرجاعه من طرف‬
‫ إذا كان هذا‬.‫ لمعرفة الوقت المنقضّي‬previousTime ‫ و‬currentTime ‫يكفي أن نحسب الفرق بين‬

.Zozor ‫ نغيّر إحداثيات‬،‫ ميلي ثانية‬30 ‫الأخير يساوي‬

: ‫لنبدأ إذا بإنشاء هذين المتغيرين‬

1 int previousTime = 0, currentTime = 0;

: ‫ نضيف الشفرة المصدر ية التالية‬،‫ في حلقتنا غير المنتهية‬،‫و الآن‬

415

‫الفصل ج‪ .6.‬تحكّم في الوقت !‬

‫;)(‪1 currentTime = SDL_GetTicks‬‬
‫‪2 if (currentTime − previousTime > 30) // If 30 ms have passed‬‬
‫{‪3‬‬
‫‪4 zozorPosition.x++; // We move Zozor‬‬
‫‪5 previousTime = currentTime; // The current time becomes the‬‬

‫‪previous one.‬‬

‫}‪6‬‬

‫إفهم جي ّدا ما يحصل ‪:‬‬

‫‪ .1‬نحصل على الوقت المنقضي باستعمال ‪. SDL_GetTicks‬‬

‫‪ .2‬نقارن هذه القيمة بالوقت الذي تم تسجيله مسبقا ً‪ .‬إذا كان هناك فرق ‪ 30‬مث على الأقل‪ ،‬إذا…‬

‫‪ .3‬نحر ّك ‪ ،Zozor‬لأننا نريده أن يتحر ّك ك ّل ‪ 30‬مث‪ .‬هنا‪ ،‬نقوم بتحر يكه إلى اليمين ك ّل ‪ 30‬مث‪.‬‬
‫يجب ان نتأكد ما إن كان الوقت المنقضي أكبر من ‪ 30‬مث‪ ،‬و ليس ما إن كان يساوي تلك القيمة‬
‫! لأنه في الواقع سن ُخْب َر ما إن كان الوقت المنقضي يساوي على الأقل ‪ 30‬مث‪ .‬نحن لسنا متأكدين بأنه‬

‫سيتم ّ تنفيذ الأمر ك ّل ‪ 30‬ثانية بالضبط‪.‬‬

‫‪ .4‬ثم‪ ،‬و الأمر الذي لا يجب فعلا ً نسيانه‪ ،‬نضع قيمة الوقت ”الحالي” في الوقت ”السابق”‪ .‬بالفعل‪ ،‬تخي ّل‬
‫الدورة القادمة للحلقة ‪ :‬الوقت الحالي يتغيّر و يمكننا مقارنته بالوقت السابق من جديد‪ ،‬أي سنقارن ما إن‬

‫تم انقضاء ‪ 30‬مث على الأقل ثم نحر ّك ‪.Zozor‬‬

‫و لـكن ماذا يحصل لو أن الحلقة اشتغلت لم ّدة أقل من مث ؟‬

‫إقرأ جيدا ً الشفرة‪ ،‬لا شيء سيحدث !‬
‫لن ندخل في الشرط‪ ،‬يعني أننا لن نقوم بأي شيء‪ .‬ننتظر الدورة القادمة للحلقة أين نقوم من جديد باختبار ما‬

‫إن تم انقضاء ‪ 30‬مث منذ آخر م ّرة قمنا فيها بتحر يك ‪.Zozor‬‬
‫هذه الشفرة قصيرة‪ ،‬لـكن يجب فهمها ! أعد قراءة شرحي بالعدد اللازم من المر ّات لتفهم جيدا ً لأن هذا‬

‫الجزء قد يكون الأهم في هذا الفصل‪.‬‬

‫تغيير في معالجة بالأحداث‬

‫الشفرة المصدر ية الذي كتبناها مثالية إلى ح ّد ما إذ ينقصها تفصيل بسيط ‪ :‬الدالة ‪. SDL_WaitEvent‬‬
‫كانت هذه الدالة عملي ّة إلى ح ّد الآن بما أننا لم نتحكّم في الوقت‪ .‬هذه الدالة توقف البرنامج مؤق ّتا )بنفس طر يقة‬

‫‪ SDL_Delay‬تقريبا ً( ما دام لا يوجد أي حدث‪.‬‬

‫‪416‬‬

Ticks‫ و الـ‬Delay‫الـ‬

.‫ ! إذ يجب عليه التحر ّك لوحده‬Zozor ‫ لسنا مُضطرين إلى انتظار حدث لنقوم بتحر يك‬،‫لـكن هنا‬
SDL_WaitEvent ‫و لا يجب عليك الاستمرار في تحر يك الفأرة فقط لإنتاج أحداث و منه الخروج من الدالة‬

!
. SDL_PollEvent ‫ماهو الح ّل؟‬
‫ ت ُرجع هذه الدالة قيمة سواء كان‬، SDL_WaitEvent ‫ على عكس‬: ‫لقد ق ّدمت لك من قبل هذه الدالة‬
ّ ‫ هي لا توقف البرنامج مؤق ّتا لأن الحلقة غير المنتهية ستستمر‬: ‫ و نقول بأن الدالة غير مـ ُع ّطـ ِلة‬.‫هناك حدث أم لا‬

.‫في العمل طوال الوقت‬

‫الشفرة المصدر ية الكاملة‬

: ‫هذه هي الشفرة النهائية التي بإمكانك تجريبها‬

1 int main(int argc, char *argv[])
2{

3 SDL_Surface *screen = NULL, *zozor = NULL;
4
SDL_Rect zozorPosition;

5 SDL_Event event;

6 int continue = 1;

7 int previousTime = 0, currentTime = 0;

8 SDL_Init(SDL_INIT_VIDEO);

9 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE |

SDL_DOUBLEBUF);

10 SDL_WM_SetCaption(”Time management in SDL”, NULL);
11 zozor = SDL_LoadBMP(”zozor.bmp”);
12 SDL_SetColorKey(zozor,SDL_SRCCOLORKEY, SDL_MapRGB(zozor−>

format, 0, 0, 255));

13 zozorPosition.x = screen−>w / 2 − zozor−>w / 2;
14 zozorPosition.y = screen−>h / 2 − zozor−>h / 2;
15 SDL_EnableKeyRepeat(10, 10);
16 while (cont)
17 {
18 SDL_PollEvent(&event); // We use PollEvent and not

WaitEvent in order not to block the program

19 switch(event.type)
20 {
21 case SDL_QUIT:
22 cont = 0;
23 break;
24 }
25 currentTime = SDL_GetTicks();
26 if (currentTime − previousTime > 30) // If 30ms have

passed since the final loop iteration

27 {
28 zozorPosition.x++; // We move Zozor
29 previousTime = currentTime; // The current

time becomes the previous one for our

future calculation.

417

‫‪30‬‬ ‫الفصل ج‪ .6.‬تحكّم في الوقت !‬
‫‪31‬‬
‫}‬
‫‪32‬‬ ‫‪SDL_FillRect(screen, NULL, SDL_MapRGB(screen−>format,‬‬
‫‪33‬‬
‫‪34‬‬ ‫;))‪255, 255, 255‬‬
‫‪35‬‬ ‫;)‪SDL_BlitSurface(zozor, NULL, screen, &zozorPosition‬‬
‫‪36‬‬ ‫;)‪SDL_Flip(screen‬‬
‫‪37‬‬ ‫}‬
‫} ‪38‬‬ ‫;)‪SDL_FreeSurface(zozor‬‬
‫;)(‪SDL_Quit‬‬
‫;‪return EXIT_SUCCESS‬‬

‫يجدُر بك أن ترى ‪ Zozor‬يهتزّ لوحده على الشاشة‪ .‬هو يتحر ّك نحو اليمين‪.‬‬
‫حاول مثلا ًتغيير الوقت من ‪ 30‬مث إلى ‪ 15‬مث ‪ :‬يجدر بـ‪ Zozor‬أن يتحر ّك إلى اليمين بشكل أسرع بمرتين ! في‬

‫الواقع‪ ،‬هو يتحر ّك م ّرة كل ‪ 15‬مث في عوض م ّرة كل ‪ 30‬مث كالسابق‪.‬‬

‫استهلاك أقل للـ‪CPU‬‬

‫حاليا ً‪ ،‬البرنامج يدور في حلقة غير منتهية بسرعة الضوء )حسنا‪ ،‬تقريبا ً(‪ .‬و لهذا فهو يستهلك ‪ 100%‬من المعالج‪.‬‬
‫لـكي نرى هذا يكفي مثلا ُأن نضغط على ‪) DEL + ALT + CTRL‬في القائمة ‪ (Processes‬في ‪: Windows‬‬

‫كما يمكنك أن ترى‪ ،‬يتم استعمال ‪ CPU‬بنسبة ‪ 100%‬من طرف برنامجنا ‪. testsdl.exe‬‬
‫لقد قلت لك مسب ّقا ً‪ :‬إذا برمجت لعبة )خاصة إذا كانت بنظام شاشة كاملة(‪ ،‬ليس خطيرا ً أن تستعمل المعالج‬
‫بنسبة ‪ .100%‬لـكن إذا كانت لعبة في نافذة مثلا ً‪ ،‬ي ُستحسن استعمال نسبة أقل من ‪ CPU‬لـكي نسمح للمستعمل‬

‫بالقيام بشيء آخر دون أن ُيجهد الحاسوب نفسه‪.‬‬

‫الحل ؟ سنقوم بإعادة الشفرة السابقة‪ ،‬لـكننا سنضيف إليه ‪ SDL_Delay‬من أجل انتظار الوقت اللازم‬
‫لـكي يصل إلى ‪ 30‬مث‪.‬‬

‫يكفي أن نضيف ‪ SDL_Delay‬في ‪: else‬‬

‫;)(‪1 currentTime = SDL_GetTicks‬‬
‫‪2 if (currentTime − previousTime > 30) // If 30ms have passed‬‬
‫{‪3‬‬
‫‪4 zozorPosition.x++; // We move Zozor‬‬
‫‪5 previousTime = currentTime; // The current time becomes the‬‬

‫‪previous one for our future calculation‬‬

‫‪418‬‬

‫الـ‪ Delay‬و الـ‪Ticks‬‬

‫}‪6‬‬ ‫;))‪SDL_Delay(30 − (currentTime − previousTime‬‬
‫‪7 else‬‬
‫{‪8‬‬
‫‪9‬‬
‫} ‪10‬‬

‫كيف تعمل الأمور هذه المرة ؟ الأمر بسيط‪ ،‬هناك احتمالان )حسب الشرط( ‪:‬‬

‫• إما أنه مضت اكثر من ‪ 30‬مث منذ قمنا بتحر يك ‪ ،Zozor‬في هذه الحالة نحر ّكه‪.‬‬

‫• إما أنه مضى وقت أقل من ‪ 30‬مث‪ ،‬في هذه الحالة سينام البرنامج بفضل ‪ SDL_Delay‬ريثما يسمح‬
‫بوصول الـ‪ 30‬مث و بهذا العملية الحسابية التي قم ُت بها )‪. 30 - (currentTime - previousTime‬‬
‫إذا كان الفرق بين الزمن الحالي و الزمن السابق هي ‪ 20‬مث مثلا ً‪ ،‬فسينام البرنامج لـ ‪10 = 20 - 30‬‬

‫مث لـكي تصل الـ‪ 30‬مث‪.‬‬

‫تذكّر بأن ‪ SDL_Delay‬يمكن لها أن تضيف بعض الميل ّي ثواني أكثر من المتوقع‪.‬‬
‫بهذه الشفرة‪ ،‬سينام البرنامج معظم الوقت و بهذا نقلل من استهلاك ‪ .CPU‬لاحظ الصورة التالية ‪:‬‬

‫يستعمل البرنامج كمع ّدل حوالي ‪ 0‬إلى ‪ 1%‬من ‪…CPU‬أحيانا ًيستعمل أكثر بقليل لـكن ّه يعود سر يعا ًإلى ‪.0%‬‬

‫التحكّم في عدد الصور في الثانية‬

‫أنت ٺتساءل حتما ًكيف يمكننا الح ّد من )أو ٺثبيت( عدد الصور في الثانية )غالبا ًما نسمّي هذا ‪ FPS‬إختصارا ً‬
‫لـ‪ (Frames per second‬التي ي ُظهرها الحاسوب‪.‬‬

‫حسنا ً‪ ،‬هذا تماما ًما نحاول فعله ! فهنا نقوم بإظهار صورة جديدة كل ‪ 30‬مث كمع ّدل‪ .‬علما ًأن ثانية واحدة‬
‫تساوي ‪ 1000‬مث‪ ،‬لـكي نجد عدد الـ‪ ،FPS‬يجب أن نقوم بعملية قسمة ‪ 33 = 30 / 1000‬صورة في الثانية‬

‫بالتقر يب‪.‬‬
‫بالنسبة لعين الإنسان‪ ،‬نقول عن التحر ّك أنه رشيق إذا احتوى على الأقل ‪ 25‬صورة في الثانية‪ .‬بـ‪ 33‬صورة‬

‫في الثانية إذا ً فالتحر ّك في البرنامج رشيق تماما و بهذا لن يظهر متشن ّجا‪.‬‬
‫إذا أردنا صورا أكثر في الثانية‪ ،‬يجب إنقاص حدود الوقت بين صورتين‪ .‬انتقل من ‪ 30‬إلى ‪ 20‬مث و‬

‫ستصبح العملية ‪.FPS 50 = 20 / 1000 :‬‬

‫‪419‬‬

‫الفصل ج‪ .6.‬تحكّم في الوقت !‬
‫تمار ين‬

‫التحكّم في الوقت ليس أمرا ً بديهيا ً‪ ،‬سيكون من الجيد لك أن تتمر ّن‪ ،‬ما رأيك ؟ إليك بعض التمارين ‪:‬‬

‫• لح ّد الآن‪ ،‬يتحر ّك ‪ Zozor‬في ك ّل مرة إلى اليمين إلى أن يختفي من الشاشة‪ .‬سيكون من الأفضل حينما‬
‫يصل إلى حافة النافذة أن يعيد التوجّه إلى اليسار‪ .‬سيكون ذلك أفضل أليس كذلك ؟ لأنه سيعطي‬

‫انطباعا ًأنه يرت ّد‪.‬‬
‫أنصحك بإنشاء متغير منطقي ‪ toTheRight‬يحمل القيمة ”صحيح” إذا كان ‪ Zozor‬يتحر ّك نحو اليمين )و‬
‫”خطأ” إذا كان يتحر ّك نحو اليسار(‪ .‬إذا كان المتغير المنطقي يحمل القيمة صحيح‪ ،‬تقوم بتحر يك ‪ Zozor‬إلى‬
‫اليمين‪ ،‬و إلا فستقوم بتحر يكه إلى اليسار‪ .‬لا تنس أن تغيّر قيمة المتغير المنطقي ما إن يصل ‪ Zozor‬إلى‬

‫حافة النافذة و ذلك لـكي ينطلق في الإتجاه الم ُعاكس !‬
‫• بدل أن يقوم ‪ Zozor‬بالإرتداد من اليمين إلى اليسار‪ ،‬فيمكن تحر يكه على قطر النافذة ! يكفيك تغيير‬
‫‪ zozorPosition.x‬و ‪ zozorPosition.y‬في نفس الوقت‪ .‬يمكنك رؤ ية ماذا ي ُعطينا الأمر‬

‫لو نقوم بز يادة قيمة ‪ x‬و إنقاص قيمة ‪ y‬في نفس الوقت‪ ،‬أو إذا قمُنا بز يادة قيمتيهما معا ً‪ ،‬إلخ‪.‬‬
‫• حاول جعل ‪ Zozor‬يتوق ّف عن التحر ّك إذا تم ّ الضغط على الزر ‪ ، P‬و إذا تم الضغط مجددا ً على نفس‬

‫الزر ينطلق ‪ Zozor‬مجددا ً‪ .‬يحتاج الأمر متغيرا منطقيا بسيطا تقوم بتفعيله أو تعطيله‪.‬‬

‫ج‪ 2.6.‬الم ُؤقـ ِتات )‪(Timers‬‬

‫استعمال الم ُؤقـ ِتات أكثر تعقيدا ً قليلا ً لأننا سنعتمد على مبدأ لم نره لح ّد الآن ‪ :‬المؤشّرات نحو الدوال‪.‬‬
‫استعمال الم ُؤقـ ِتات ليس أمرا ً ضرور يا ‪ :‬إذا وجدت بأنها صعبة جدا ً لتستعملها‪ ،‬يمكنك غ ّض النظر عنها‬

‫دون أي مشكل‪.‬‬

‫الم ُؤقـ ِتات تش ّكل طر يقة أخرى لتحقيق ما نحن بصدد رؤيته بالدالة ‪. SDL_GetTicks‬‬
‫هي تقنية خا ّصة نوعا ًما‪ .‬بعض المبرمجـين يجدونها عملي ّة‪ ،‬و آخرون لا‪ .‬هذا يعتمد على ذوقك البرمجي‪.‬‬

‫ماهو الم ُؤقـ ِت ؟‬
‫هو نظام يسمح بالطلب من الـ‪ SDL‬أن تستدعي دالة ما كل ‪ X‬ميل ّي ثانية‪ .‬يمكنك بهذا أن تنشئ دالة‬
‫)(‪ moveEnemy‬تقوم الـ‪ SDL‬باستدعائها تلقائيّا كل ‪ 50‬مث كي يستطيع العد ّو التحر ّك في مجالات معي ّنة‪.‬‬

‫‪420‬‬

‫الم ُؤقـ ِتات )‪(Timers‬‬

‫كما كنت أقول لك الآن‪ ،‬يمكننا القيام بهذا بواسطة ‪ SDL_GetTicks‬باستعمال التقنية التي رأيناها‬
‫أعلاه‪.‬‬

‫ما الفائدة إذا ؟ لنقل أن الم ُؤقـ ِتات تفرض علينا هيكلة برامجنا بشكل أفضل على شكل دوال‪.‬‬

‫تهيئة نظام الم ُؤقـ ِتات‬

‫لـكي نتم ّكن من استعمال الم ُؤقـ ِتات‪ ،‬يجب علينا تهيئة الـ‪ SDL‬أولا ًباستعمال عَـل َم خاص ‪. SDL_INIT_TIMER :‬‬
‫يجب عليك إذا استدعاء الدالة ‪ SDL_Init‬كالتالي ‪:‬‬

‫;)‪1 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER‬‬

‫الـ‪ SDL‬جاهزة الآن لاستعمال الم ُؤقـ ِتات !‬

‫إضافة مُؤقـ ِت‬

‫لنضيف مُؤقـ ِتا ًيجب علينا استدعاء الدالة ‪ . SDL_AddTimer‬إليك نموذجها ‪:‬‬
‫‪1 SDL_TimerID SDL_AddTimer(Uint32 interval, SDL_NewTimerCallback‬‬

‫;)‪callback, void *param‬‬

‫توجد في الواقع دالتان تسمحان بإضافة مُؤقـ ِت في الـ‪ SDL‬هما ‪ SDL_AddTimer :‬و ‪SDL_SetTimer‬‬
‫و هما تقريبا ً متطابقتان‪ .‬و مع ذلك‪ SDL_SetTimer ،‬هي دالة قديمة موجودة دائما ً لأسباب التوافقي ّة‬

‫)‪ .(Compatibility‬حالي ّا‪ ،‬إذا أردنا القيام بالأمر بالشكل الجيد‪ ،‬أنصحك باستعمال ‪. SDL_AddTimer‬‬
‫الدالة تستقبل ثلاثة معاملات ‪:‬‬

‫• المجال الزمني )بالميلي ثانية( بين ك ّل استدعاء للدالة و آخر‪.‬‬
‫• اسم الدالة التي نريد استدعاءها‪ .‬نسمّي هذه بدالة الرد )‪ : (Callback‬يتك ّفل البرنامج باستدعاء الدالة‬

‫بشكل دوري‪.‬‬
‫• المعاملات التي نبعثها لدالة الرد‪.‬‬

‫كيف يمكن لاسم دالة أن يكون معاملا ؟ اعتقد ُت أن المعاملات لا يمكنها أن تكون إلا أسماء متغيرات !‬

‫‪421‬‬

‫الفصل ج‪ .6.‬تحكّم في الوقت !‬

‫في الواقع‪ ،‬يتم أيضا ً ْحفظ الدوال في الذاكرة خلال تحميل البرنامج‪ .‬فهي تملك أيضا ًعناوين خاصة بها‪ .‬لهذا‪،‬‬
‫يمكننا أن نن ُشئ مؤشّرات نحو دوال ! تكفي كتابة اسم الدالة التي نريد استدعاءها للإشارة إلى عنوانها‪ .‬و بهذا‪،‬‬

‫ستعرف الـ‪ SDL‬العنوان بالذاكرة الذي يجب أن تذهب إليه لاستدعاء دالة الرد‪.‬‬
‫إذا أردت معرفة المزيد حول المؤشرات نحو الدوال‪ ،‬أدعوك إلى قراءة الدرس التعليمي المكتوب بواسطة العضو‬

‫‪) mleg‬في الموقع الفرنسي( و الذي يحل ّل هذا الموضوع ‪:‬‬

‫‪http://www.siteduzero.com/tutoriel-3-314203-les-pointeurs-sur-fonctions.‬‬
‫‪html‬‬

‫ت ُرجع الدالة ‪ SDL_AddTimer‬عددا ًخاصا ًبالم ُؤقـ ِت )‪ .(ID‬يجدر بك تخزين هذه النتيجة في متغير من نوع‬
‫‪ . SDL_TimerID‬هذا سيسمح لك لاحقا ًبتعطيل الم ُؤقـ ِت ‪ :‬يكفي أن تشير إلى ‪ ID‬الم ُؤقـ ِت و يستم ّ إيقافه‪.‬‬

‫تسمح لنا الـ‪ SDL‬بتفعيل الـكثير من الم ُؤقـ ِتات في نفس الوقت‪ .‬هذا يشرح الفائدة من تخزين هو ي ّة ك ّل‬
‫مُؤقـ ِت لـكيّ نفرق بينها‪.‬‬

‫سن ُنشئ إذا هو ية الم ُؤقـ ِت ‪:‬‬
‫‪1 SDL_TimerID timer; // A variable to save the number of timer‬‬

‫ثم نقوم بإنشاء الم ُؤقـ ِت ‪:‬‬
‫‪1 timer = SDL_AddTimer(30, moveZozor, &zozorPosition); // Starting the‬‬

‫‪timer‬‬

‫هنا‪ ،‬أنشئ مُؤقـ ِتا يحمل المعاملات التالية ‪:‬‬

‫• يتم استدعاؤه ك ّل ‪ 30‬مث‪.‬‬
‫• يقوم باستدعاء دالة الرد المسمّاة ‪. moveZozor‬‬
‫• يبعث له كمعامل‪ ،‬مؤش ّرا ً نحو وضعية ‪ Zozor‬لـكي يتم ّكن من التعديل عليه‪.‬‬

‫لقد فهمت المبدأ ‪ :‬دور الدالة ‪ moveZozor‬هو تغيير وضعية ‪ Zozor‬كل ‪ 30‬مث‪.‬‬

‫إنشاء دالة الرد‬

‫إحذر ‪ :‬يجب أن تكون حذرا ً هنا‪ .‬يجب أن يكون نموذج دالة الرد هو التالي إجبار يا ‪:‬‬
‫;)‪1 Uint32 functionName(Uint32 interval, void *parameter‬‬

‫‪422‬‬

‫الم ُؤقـ ِتات )‪(Timers‬‬

‫لـكي ننشئ دالة الرد المسمّاة ‪ ، moveZozor‬يجب أن نكتب الدالة كالتالي ‪:‬‬
‫;)‪1 Uint32 moveZozor(Uint32 interval, void *parameter‬‬

‫إليك الشفرة الخاصة بالدالة ‪ ، moveZozor‬إنها مع ّقدة أكثر مما تبدو عليه ‪:‬‬

‫)‪1 // Callback function (Will be called every 30 ms‬‬

‫)‪2 Uint32 moveZozor(Uint32 interval, void *parameter‬‬
‫{‪3‬‬

‫‪4 SDL_Rect* zozorPosition = parameter; // Automatic conversion‬‬

‫‪5‬‬ ‫*‪from void* to SDL_Rect‬‬

‫;‪zozorPosition−>x++‬‬

‫;‪6 return interval‬‬

‫}‪7‬‬

‫يتم استدعاء الدالة ‪ moveZozor‬تلقائيّا كل ‪ 30‬مث بواسطة الـ‪ .SDL‬تقوم هذه الأخيرة ببعث معاملين‬
‫تماما ًللدالة )لا أكثر و لا أقل( ‪:‬‬

‫• المجال الزمني الذي يفر ّق كل استدعائين للدالة )هنا ‪ 30‬مث(‪.‬‬

‫• المعامل ”المخ ّصص” الذي طلبت إعطاءه للدالة ‪.‬لاحظ‪ ،‬و من المهم جدا ً‪ ،‬أن هذا المعامل هو عبارة عن‬
‫مؤش ّر نحو ‪ . void‬هذا يعني أنه مؤش ّر يؤش ّر نحو أي نوع كان ‪ :‬على ‪ ، int‬هيكل مخ ّصص‪ ،‬أو ‪،‬مثل‬

‫هنا‪ ،‬على ‪.( zozorPosition ) SDL_Rect‬‬
‫لاحظ أيضا ًأنه لا يمكن بعث أكثر من معامل مخ ّصص لدالة الرد‪ .‬لحسن الحظ‪ ،‬نحن دائما ًقادرون على‬

‫إنشاء أنواع خاصة بنا )أو جداول( و التي ستكون عبارة عن تجميع لمتغيرات نريد بعثها للدالة‪.‬‬

‫المشكل هو أن هذا المعامل هو مؤشّر من نوع غير معروف ) ‪ ( void‬للدالة‪ .‬يجب إذا أن نقول للحاسوب‬
‫أن هذا المعامل هو *‪) SDL_Rect‬مؤش ّر نحو ‪.( SDL_Rect‬‬

‫لفعل ذلك‪ ،‬أنشئ مؤش ّرا نحو ‪ SDL_Rect‬في دالتي التي تأخذ كمعامل المؤش ّر ‪. parameter‬‬

‫ما الفائدة من إنشاء مؤش ّر ثا ٍن ليحمل نفس العنوان ؟‬

‫الفائدة هي أن ‪ zozorPosition‬من نوع *‪ SDL_Rect‬بعكس المتغير ‪ parameter‬الذي كان‬
‫من نوع *‪. void‬‬

‫يمكننا إذا الوصول إلى ‪ zozorPosition->x‬و ‪. zozorPosition->y‬‬
‫لو قمت بكتابة ‪ parametre->x‬أو ‪ parametre->y‬فالمترجم كان سيرفضها لأ ّن متغي ّرا من نوع ‪void‬‬

‫لا يملك هذه المركّبات‪.‬‬

‫‪423‬‬

‫الفصل ج‪ .6.‬تحكّم في الوقت !‬

‫بعد ذلك‪ ،‬السطر التالي بسيط ‪ :‬نع ّدل قيمة ‪ zozorPosition->x‬لتحر يك ‪ Zozor‬نحو اليمين‪.‬‬

‫آخر شيء )مهم جدا ً( ‪ :‬يجب عليك إرجاع المتغير ‪ . interval‬هذا ي ُشير للـ‪ SDL‬بأننا نريد أن نستمر في‬
‫اعتبار أنه سيتم استدعاء الدالة كل ‪ 30‬مث‪.‬‬

‫إذا كنت تريد تغيير المجال الزمني لاستدعاء الدالة‪ ،‬يكفي أن تبعث قيمة أخرى )في غالب الأحيان لا نفعل‬
‫ذلك(‪.‬‬

‫إيقاف الم ُؤقـ ِت‬

‫لإيقاف الم ُؤقـ ِت‪ ،‬الأمر بسيط ‪:‬‬

‫‪1 SDL_RemoveTimer(timer); // Stopping the timer‬‬

‫يكفي إذا استدعاء ‪ SDL_RemoveTimer‬و ذلك بالإشارة إلى هو ية الم ُؤقـ ِت الذي نريد إيقافه‪.‬‬
‫هنا أوقف الم ُؤقـ ِت مباشرة بعد الحلقة غير المنتهية‪ ،‬في نفس موضع ‪. SDL_FreeSurface‬‬

‫مل ّخص‬

‫• تم ّكن الدالة ‪ SDL_Delay‬من إيقاف البرنامج مؤق ّتا لعدد معين من الميل ّي ثواني‪ .‬هذا يسمح بإنقاص‬
‫نسبة استعمال الم ُعالج الذي لن يكون ”خلال نوم البرنامج” مستعملا ًمن طرف هذا الأخير‪.‬‬

‫• يمكننا معرفة عدد الميل ّي ثواني المنقضية منذ اشتغال البرنامج باستعمال ‪ ، SDL_GetTicks‬بواسطة‬
‫عمليات حسابية بسيطة‪ ،‬يمكننا الاستفادة من هذا لـكي نقوم بمعالجة غير مُع ّطلة للأحداث بواسطة‬

‫‪. SDL_PollEvent‬‬

‫• الم ُؤقـ ِتات تش ّكل نظاما يسمح باستدعاء دوالك )المسمّاة بدوال الرد( على مجالات زمني ّة مح ّددة‪ .‬يمكننا‬
‫التحصل على نفس النتيجة باستعمال ‪ SDL_GetTicks‬لـكن الم ُؤقـ ِتات تساعد على هيكلة البرنامج بشكل‬

‫أفضل و جعله أحسن من ناحية القراءة‪.‬‬

‫‪424‬‬

‫الفصل ج‪7.‬‬

‫كتابة نصوص باستعمال ‪SDL_ttf‬‬

‫يمكنني التكهّن بأن معظم القر ّاء قد طرح هذا السؤال من قبل ‪” :‬و لـكن‪ ،‬ألا توجد أي دالة لـكي تكتب‬
‫نصا ًعلى نافذة ‪ SDL‬؟” حان الوقت لأجيبك ‪ :‬الجواب هو لا‪.‬‬

‫رغم ذلك‪ ،‬توجد طرق لفعل هذا‪ .‬يمكننا فقط …وضع صور للحروف بجانب بعضها البعض على الشاشة‪.‬‬
‫هذا الأمر يعمل لـكن ّه ليس عمليا‪.‬‬

‫لحسن الحظ‪ ،‬يوجد ماهو أبسط ‪ :‬يمكننا استعمال المكتبة ‪ .SDL_ttf‬إنها مكتبة تتم إضافتها إلى الـ‪ SDL‬تماما ً‬
‫مثل الـ‪ .SDL_image‬دورها هو إنشاء مساحة ‪ SDL_Surface‬إنطلاقا من النص الذي نبعثه لها‪.‬‬

‫ج‪ 1.7.‬تسطيب ‪SDL_ttf‬‬

‫يجب أن تعرف أنه‪ ،‬مثل ‪ SDL_ttf ،SDL_image‬هي مكتبة تحتاج إلى أن تكون المكتبة ‪ SDL‬مثب ّتة من‬
‫قبل‪ .‬حسنا ً ‪ :‬إذا كنت إلى ح ّد الآن لم تتم ّكن من تسطيب المكتبة ‪ SDL‬فهذا أمر شنيع و لهذا فسأعتبر أنك‬

‫قمت بذلك !‬
‫تماما مثل ‪ ،SDL_image‬فإن المكتبة ‪ SDL_ttf‬هي واحدة من المكتبات الم ُرتبطة بالـ‪ SDL‬الأكثر شعبية‬
‫)أي أنه يتم تحميلها بكثرة(‪ .‬كما ست ُلاحظ‪ ،‬هذه المكتبة مُبرمجة بشكل جيد‪ .‬ما إن تجيد استعمالها لن يمكنك أن‬

‫ٺتوق ّف عن ذلك !‬

‫كيف تعمل ‪ SDL_ttf‬؟‬

‫‪ SDL_ttf‬لا تقوم بإظهار صور ‪ bitmap‬لتول ّد نصا في مساحات‪ .‬في الحقيقة‪ ،‬هي طر يقة ثقيلة لفعلها و لن يتاح‬
‫لنا استعمال سوى خط واحد‪.‬‬

‫في الواقع‪ ،‬تستدعي المكتبة ‪ SDL_ttf‬مكتبة َ أخرى ‪ .FreeType :‬هي مكتبة قادرة على قراءة ملفات خطوط‬
‫بصيغة ‪ .ttf‬لت ُخرج منها صورة‪ .‬تقوم ‪ SDL_ttf‬باسترجاع هذه الصورة و تحوّلها للـ‪ SDL‬و ذلك بإنشاء مساحة‬

‫‪. SDL_Surface‬‬

‫‪425‬‬

‫الفصل ج‪ .7.‬كتابة نصوص باستعمال ‪SDL_ttf‬‬
‫و بهذا فإن ‪ SDL_ttf‬تحتاج المكتبة ‪ FreeType‬لـكي تشتغل‪ ،‬و إلا فلن تكون قادرة على قراءة ملفات‬

‫الخطوط ‪. .ttf‬‬

‫إذا كنت تعمل بـ‪ Windows‬و تستعمل‪ ،‬مثلما أفعل‪ ،‬النسخة الم ُترجم َة للمكتبة‪ ،‬لن تحتاج إلى تحميل أي شيء‬
‫لأن ‪ FreeType‬مضمّنة من قبل في المكتبة الحي ّة ‪ SDL_ttf.dll‬و لهذا فليس عليك القيام بأي شيء‪.‬‬

‫إذا كنت تعمل بالـ‪ GNU/Linux‬أو ‪ Mac OS X‬فمن اللازم أن تعيد ترجمة المكتبة‪ ،‬فتلزمك ‪FreeType‬‬
‫لتتم الترجمة‪ .‬إذهب إذن إلى صفحة تحميل ‪: FreeType‬‬

‫‪http://www.freetype.org/download.html#stable‬‬

‫لنحمّل الملفات الخاصة بالمطورين‪.‬‬

‫ٺثبيت ‪SDL_ttf‬‬

‫إذهب إلى صفحة تحميل ‪: SDL_ttf‬‬

‫‪http://www.libsdl.org/projects/SDL_ttf/‬‬

‫هنا‪ ،‬اختر الملف اللازم من القسم ”‪.”Binary‬‬

‫في ‪ ،Windows‬لاحظ أنه لا يوجد سوى ملفان بصيغة ‪ .zip‬يحملان في نهاية اسميهما اللاحقتين‬
‫‪ win32‬و ‪ . VC6‬الأولى ) ‪ ( win32‬تحتوي الـ‪ DLL‬التي تحتاج إلى تقديمها مع الملف التنفيذي‪.‬‬

‫يجب عليك أيضا ًوضع هذه الـ‪ DLL‬في مجل ّد المشروع لتستطيع تجريب البرنامج‪ ،‬طبعا‪.‬‬
‫الثانية ) ‪ ( VC6‬تحتوي الملفات ‪ .h‬و الملفات ‪ .lib‬التي تحتاجها للبرمجة‪ .‬يمكننا أن نفكّر من خلال‬
‫الاسم أن هذه المل ّفات تخص ‪ Visual C++‬فقط‪ ،‬لـكن في الحقيقة‪ ،‬و بشكل خاص‪ ،‬الملف ‪.lib‬‬

‫يعمل أيضا ًمع ‪ ،mingw32‬سيشتغل إذن في الـ‪.Code::Blocks‬‬

‫الملف ‪ .zip‬يحتوي كالعادة مجلد ‪ include‬و مجلد ‪ . lib‬قم بوضع محتوى المجلد ‪ include‬في‬
‫المسار ‪ ، mingw32/include/SDL‬و محتوى المجلد ‪ lib‬في المسار ‪. mingw32/lib‬‬

‫يجدر بك نسخ الملف ‪ SDL_ttf.h‬في المجلد ‪ mingw32/include/SDL‬و ليس في المجلد‬
‫‪ mingw32/include‬فقط‪ .‬احذر الخطأ !‬

‫‪426‬‬

‫تسطيب ‪SDL_ttf‬‬

‫تخصيص مشروع من أجل الـ‪SDL_ttf‬‬

‫بقيت لنا مرحلة واحدة أخيرة ‪ :‬تخصيص المشروع لـكي يكون قادرا ً على استعمال ‪ SDL_ttf‬بشكل جيد‪ .‬يجب‬
‫أن يتم التعديل على خصائص محر ّر الروابط لـكي ي ُترجم البرنامج بشكل جيد و ذلك باستعمال ‪.SDL_ttf‬‬

‫لقد تعل ّمت من قبل هذه العملية بالنسبة لـ‪ ،SDL_image‬و لهذا سأسرع قليلا ً‪.‬‬
‫بما أنني أعمل في الـ‪ Code::Blocks‬سأعطيك العملية الخاصة بهذه البيئة التطوير ية‪ .‬بالنسبة لباقي البيئات‪،‬‬

‫فالطر يقة لا تختلف كثيرا ً عن هذه ‪:‬‬

‫• توجّه نحو القائمة ‪. Build Options / Project‬‬

‫• في القسم ‪ Linker‬أنقر على الزر الصغير ‪. Add‬‬

‫• أشر إلى المسار الذي يوجد به الملف ‪) SDL_ttf.lib‬بالنسبة لي هو في‬
‫‪.( C:\Program Files\CodeBlocks\mingw32\lib‬‬

‫• ستظهر لك هذه الرسالة ‪ ”Keep this as a relative path ?” :‬لا يه ّم ما تختاره لأن الأمر سيشتغل في‬
‫كلتا الحالتين‪ .‬أنصحك أن تجيب بالسلب لأن المشروع لن يشتغل لو وضعته في مسار آخر غير المتواجد به‬

‫لو أنك أجبت بالإيجاب‪.‬‬
‫• وافق على التغييرات بالنقر على ‪. OK‬‬

‫ألا نحتاج إلى ربط المكتبة ‪ FreeType‬أيضا ً؟‬

‫كلا‪ ،‬مثلما قل ُت فـ‪ FreeType‬مضمّنة في الـ‪ DLL‬الخاصة بـ‪ .SDL_ttf‬لهذا فلن يكون عليك الإهتمام بها‪،‬‬
‫لأن ‪ SDL_ttf‬تفعل ذلك الآن‪.‬‬

‫الملفات التوثيقية‬

‫و الآن بما أنك أصبحت مبرمجا ًمحن ّكا ًتقريبا ً‪ ،‬يجدر بك أن تطرح التساؤل التالي ‪” :‬لـكن أين هو التوثيق ؟” إن‬
‫لم تطرح هذا السؤال فهذا يعني أن ّك لازلت لم تصبح بعد مبرمجا ًمحن ّكا ً‪.‬‬

‫يوجد بالطبع دروس تف ّصل في كيفية عمل المكتبات‪ ،‬مثل هذا الكتاب‪ ،‬و لـكن ‪:‬‬

‫• لن أستطيع أن أضع لك فصلا ًحول كل المكتبات الموجودة )حتى لو أمضيت حياتي كل ّها في ذلك‪ ،‬لن‬
‫يكفيني الوقت !(‪ .‬و لهذا يجب عاجلا ًأم آجلا قراءة التوثيق و يجدر بك أن ٺتعوّد على ذلك من الآن !‬

‫‪427‬‬

‫الفصل ج‪ .7.‬كتابة نصوص باستعمال ‪SDL_ttf‬‬

‫• من جهة أخرى‪ ،‬في غالب الأحيان تكون المكتبة مع ّقدة نوعا ًما و تحتوي كثيرا ً من الدوال‪ .‬لن أتمكن‬
‫من تقديم ك ّل هذه الدوال في هذا الفصل لأنه سيكون بذلك طو يلا ًجدا ً !‬

‫من الواضح جدا ً أن التوثيق يكون كاملا و يلم ّ بكل حفايا المكتبة‪ ،‬و لهذا أف ّضل أن أعطيك من الآن رابط‬
‫صفحة التوثيق الخاصة بـ‪: SDL_ttf‬‬

‫‪http://sdl.beuc.net/sdl.wiki/SDL_ttf‬‬

‫التوثيق متوف ّر بصيغ مختلفة ‪ HTML :‬على الشبكة‪ HTML ،‬مضغوطة‪ ،PDF ،‬إلخ‪ .‬خذ النسخة التي تناسبك‪.‬‬
‫ستجد بأن ‪ SDL_ttf‬مكتبة بسيطة جدا ً ‪ :‬يوجد بها قليل من الدوال )حوالي ‪ ،50 - 40‬نعم إنها قليلة !(‪.‬‬
‫يجدر بهذا أن تكون إشارة )للمبرمجـين المحن ّكين من ضمن القر ّاء( إلى أن هذه المكتبة سهلة و ستستطيع التعامل‬

‫معها سر يعا ً‪.‬‬
‫هيا‪ ،‬حان الوقت لنتعلّم كيف نستخدم ‪ SDL_ttf‬الآن !‬

‫ج‪ 2.7.‬تحميل ‪SDL_ttf‬‬

‫التضمين‬

‫قبل ك ّل شيء‪ ،‬يجب تضمين الملف الرأسي التالي قبل ك ّل استعمال لهذه المكتبة ‪:‬‬
‫>‪1 #include <SDL/SDL_ttf.h‬‬

‫إذا صادفت أخطاء ترجمة الآن‪ ،‬تأكد بأنك وضعت الملف ‪ SDL_ttf.h‬في المجل ّد ‪mingw32/include/SDL‬‬
‫و ليس في ‪ mingw32/include‬فقط‪.‬‬

‫تشغيل ‪SDL_ttf‬‬

‫تماما مثل الـ‪ ،SDL‬تحتاج ‪ SDL_ttf‬أن ُتشغّل في بداية الشفرة وت ُوق ّف في نهايتها‪.‬‬
‫توجد دالتان تشبهان كثيرا ً الدالتين الخاصتين بالـ‪: SDL‬‬

‫• ‪ : TTF_Init‬تقوم ببدء تشغيل ‪.SDL_ttf‬‬
‫• ‪ : TTF_Quit‬توق ّف ‪.SDL_ttf‬‬

‫‪428‬‬

‫تحميل ‪SDL_ttf‬‬

‫ليس واجبا ًأن يتم بدء تشغيل ‪ SDL‬قبل ‪.SDL_ttf‬‬

‫لـكي تقوم ببدء تشغيل ‪) SDL_ttf‬نقول أيضا ًتهيئة(‪ ،‬يجب أن نستدعي الدالة ‪ . TTF_Init‬هذه الأخيرة‬
‫لا تحتاج إلى أن تستقبل أي معامل و هي تقوم بإرجاع القيمة ‪ −1‬إن حدث أي خطأ‪.‬‬

‫يمكنك البدء في تشغيل ‪ SDL_ttf‬ببساطة كالتالي ‪:‬‬

‫;)(‪1 TTF_Init‬‬

‫إذا أردت أن ٺتأكد ما إن كان قد حدث خطأ أم لا‪ ،‬جرّب الشفرة التالية ‪:‬‬

‫)‪1 if(TTF_Init() == −1‬‬
‫{‪2‬‬
‫‪3 fprintf(stderr, ”Error initializing TTF_Init : %s\n”,‬‬

‫‪4‬‬ ‫;))(‪TTF_GetError‬‬
‫}‪5‬‬ ‫;)‪exit(EXIT_FAILURE‬‬

‫إذا كان هناك خطأ في تشغيل ‪ ،SDL_ttf‬سيتم إنشاء ملف ‪) stderr.txt‬في ‪ Windows‬على الأقل( يحتوي‬

‫على رسالة تشرح الخطأ‪.‬‬
‫للذين يطرحون السؤال ‪ :‬الدالة ‪ TTF_GetError‬تقوم بإرجاع آخر رسالة خطأ للـ‪ ،SDL_ttf‬و لهذا استعملته‬

‫في الـ ‪. fprintf‬‬

‫إيقاف ‪SDL_ttf‬‬

‫لنوق ّف المكتبة‪ ،‬نستدعي الدالة ‪ . TTF_Quit‬هي أيضا ًلا تحتاج أي معامل‪ .‬يمكنك استدعاؤها قبل أو بعد‬
‫‪ SDL_Quit‬هذا لا يهم ‪:‬‬

‫;)(‪1 TTF_Quit‬‬

‫تحميل خط‬

‫حسنا ًكان ك ّل شيء جيدا ً و غير مع ّقدٍ‪ ،‬لـكننا لم نستمتع بعد‪ .‬لننتقل إلى الأه ّم إذا أردت ذلك ‪ :‬و الآن بما‬
‫أنه تم تحميل ‪ ،SDL_ttf‬يجب علينا أن نقوم بتحميل خط ما‪ .‬ما إن يتم هذا الشيء‪ ،‬يمكننا أخيرا ً كتابة النص !‬
‫هنا أيضا ً‪ ،‬توجد دالتان ‪:‬‬
‫• ‪ : TTF_OpenFont‬تفتح ملف خط ) ‪.( .ttf‬‬

‫‪429‬‬

‫الفصل ج‪ .7.‬كتابة نصوص باستعمال ‪SDL_ttf‬‬
‫• ‪ : TTF_CloseFont‬تغلق الملف المفتوح‪.‬‬

‫يجدر بالدالة ‪ TTF_OpenFont‬أن تخز ّن النتيجة في متغير من نوع ‪ . TTF_Font‬لهذا يجب عليك إنشاء‬
‫مؤش ّر من نوع ‪ TTF_Font‬كالتالي ‪:‬‬

‫;‪1 TTF_Font *font = NULL‬‬

‫يحتوي المؤش ّر ‪ font‬إذا على معلومات خاصة بالخط المفتوح‪.‬‬
‫تأخذ الدالة ‪ TTF_OpenFont‬معاملين ‪:‬‬

‫• اسم ملف الخط )بصيغة ‪ ( .ttf‬الذي نريد فتحه‪ .‬الأمثل هو وضع ملف الخط في مجل ّد المشروع‪ .‬مثال‬
‫عن ملف ‪) arial.ttf :‬من أجل الخط ‪.(Arial‬‬

‫• حجم الخط الذي نريد استعماله‪ .‬يمكنك مثلا استعمال حجم ‪.22‬‬
‫إنها نفس الحجوم التي تستعملها في برامج معالجة النصوص مثل ‪.Word‬‬

‫لم يتب ّق لنا سوى إيجاد الخطوط ذات الصيغة ‪ . .ttf‬أنت تملك أصلا ً العديد منها على حاسوبك‪ ،‬لـكن‬
‫يمكنك تنز يلها من الأنترنت كما سنرى الآن‪.‬‬

‫على حاسوبك‬

‫لديك أصلا خطوط على حاسوبك !‬
‫إن كنت تعمل بالويندوز‪ ،‬ستجد الـكثير من هذه الملفات في المجل ّد ‪. C:\Windows\Fonts‬‬

‫ليس عليك سوى نسخ الملف الخاص بالخط الذي يعجبك و لصقه في مجل ّد المشروع‪.‬‬
‫إذا كان اسم الملف يحتوي على حروف ”غريبة” كالفراغات‪ ،‬الحروف ذات العلامات الصوتية )‪(accents‬‬
‫أو حتى الحروف الـكبيرة‪ ،‬أنصحك بإعادة تسمية هذا الملف‪ .‬و لـكي نكون متيقنين من عدم وجود أ ّي مشكل‪،‬‬

‫لا تستعمل سوى الأحرف الصغيرة و تجن ّب الفراغات‪.‬‬

‫• مثال عن اسم خاطئ ‪. TIMES NEW ROMAN.TTF :‬‬
‫• مثال عن اسم صحيح ‪. times.ttf :‬‬

‫‪430‬‬

‫تحميل ‪SDL_ttf‬‬

‫على الأنترنت‬

‫الخيار الآخر ‪ :‬احصل على خ ّط من الأنترنت‪ .‬ستجد الـكثير من المواقع التي تقترح خطوطا مجانية و أصلية‬
‫للتنز يل‪.‬‬

‫أنصحك شخصيا بز يارة الموقع ‪ dafont.com‬لأن ّه مصن ّف بشكل جي ّد و محتواه من ّظم و منوّع‪.‬‬
‫لاحظ الصور التالية التي ستعطيك فكرة عن الخطوط التي ستجدها هناك بسهولة ‪:‬‬

‫تحميل الخط‬

‫أقترح عليك استعمال الخط ‪ (http://www.dafont.com/angelina.font) Angelina‬لبقية الأمثلة‪.‬‬

‫فلنفتح الخط كالتالي ‪:‬‬

‫;)‪1 font = TTF_OpenFont(”angelina.ttf”, 65‬‬

‫الخط المستعمل سيكون ‪ . angelina.ttf‬لقد قمت وضع هذا الخط في مجل ّد المشروع كما قمت بإعادة‬
‫تسميته لـكي يكون كل ّه بحروف صغيرة‪.‬‬

‫سيكون للخط الحجم ‪ .65‬ستبدو الكتابة كبيرة لـكنه خ ّط خاص يستلزم ذلك لـكي يظهر بشكل جيد‪.‬‬

‫الأمر المهم هو أن ‪ TTF_OpenFont‬تخز ّن النتيجة في المتغير ‪ ، font‬ستعيد استعمال هذا المتغير الآن‬
‫بكتابة نص‪ .‬فهي تسمح بالإشارة إلى الخط الذي نريد أن نستعمله لـكي نكتب النص‪.‬‬

‫لا تحتاج إلى فتح الخط في ك ّل مرة تريد فيها الكتابة به ‪ :‬افتحه م ّرة واحدة في بداية البرنامج و أغلقه في‬
‫نهايته‪.‬‬

‫‪431‬‬

‫الفصل ج‪ .7.‬كتابة نصوص باستعمال ‪SDL_ttf‬‬

‫غلق الخط‬

‫يجب التفكير في غلق كل خط قمنا بفتحه قبل استدعاء ‪ . TTF_Quit‬في حالتي‪ ،‬هذا ما تكون عليه الشفرة ‪:‬‬
‫;)(‪1 TTF_CloseFont(font); // Must be before TTF_Quit‬‬
‫;)(‪2 TTF_Quit‬‬

‫هكذا يكون العمل !‬

‫ج‪ 3.7.‬الطرق المختلفة للكتابة‬

‫و الآن‪ ،‬بما أنه تم تحميل ‪ SDL_ttf‬و أن لدينا متغيرا ‪ font‬محم ّل هو الآخر‪ ،‬لن يمنعنا أي شيء و أي شخص‬
‫من كتابة نص في نافذة ‪! SDL‬‬

‫جيد ‪ :‬كتابة النص هو أمر جيد‪ ،‬لـكن بواسطة أي دالة ؟ من خلال التوثيق توجد على الأقل ‪ 12‬دالة لفعل‬
‫ذلك !‬

‫في الواقع‪ ،‬توجد ‪ 3‬طرق مختلفة للـ‪ SDL_ttf‬لـكي ترسم نصا ً‪.‬‬

‫• ‪) Solid‬الصورة ‪ : (1‬هي التقنية الأكثر سرعة‪ .‬ستتم كتابة النص بسرعة في ‪ . SDL_Surface‬ستكون‬
‫المساحة شفافة لـكنها لن تستخدم إلا مستوى واحدا من الشفافية )لقد تعل ّمنا ذلك في الفصول السابقة(‪.‬‬
‫هذا أمر عملي‪ ،‬لـكن النص لن يكون جميلا ً لأنه حوافه لن تكون منحوتة بشكل جيد و خاصة إن كان‬
‫مكتوبا بحجم ضخم‪ .‬استعمل هذه التقنية حينما يكون عليك تغيير النص كثيرا ً‪ ،‬مثلا لإظهار الوقت‬

‫المنقضي أو عدد الـ‪ FPS‬الخاص بلعبة‪.‬‬

‫• ‪) Shaded‬الصورة ‪ : (2‬هذه المرة‪ ،‬سيكون النص جميلا ً‪ .‬فالحروف ستكون مح ّسنة أكثر )هذا يعني أن‬
‫محيط الحواف سيكون مُـل ّطفا بشكل مُريح لعين الإنسان( و سيظهر النص أكثر نعومة‪ .‬يوجد عيب في‬
‫هذه التقنية ‪ :‬يجب أن تكون الخلفية ذات لون واحد موّحد‪ .‬يستحيل جعل خلفية الـ ‪SDL_Surface‬‬

‫شفافة بطر يقة الـ‪.Shaded‬‬
‫• ‪) Blended‬الصورة ‪ : (3‬هي التقنية الأكثر قوّة‪ ،‬لـكنها بطيئة‪ .‬في الواقع‪ ،‬هي تأخذ الوقت اللازم الذي‬
‫تأخذه التقنية ‪ Shaded‬لإنشاء الـ ‪ . SDL_Surface‬الإختلاف الوحيد بينها و بين الـ‪ ،Shaded‬هي أنه‬
‫يمكنك لص ُق النص على صورة و سيتم احترام الشفافية )على عكس ‪ Shaded‬التي تفرض وجود خلفية‬

‫موحّدة اللون(‪ .‬احذر ‪ :‬عملية اللصق بهذه الطر يقة أبطأ من تلك الخاصة بالـ‪.Shaded‬‬

‫‪432‬‬

‫الطرق المختلفة للكتابة‬
‫‪433‬‬

‫الفصل ج‪ .7.‬كتابة نصوص باستعمال ‪SDL_ttf‬‬
‫مل ّخص ‪:‬‬

‫• إذا كان لديك نص يتغير محتواه كثيرا ً‪ ،‬كعداد عكسي‪ ،‬استعمل التقنية ‪.Solid‬‬
‫• إذا كان النص لا يتغير كثيرا ًو أنك تريد لصق النص على خلفية موحدة اللون‪ ،‬استعمل التقنية ‪.Shaded‬‬
‫• إذا كان النص لا يتغير كثيرا ً و لـكنك تريد لصقه على خلفية غير موح ّدة اللون )كصورة مثلا ً( استعمل‬

‫التقنية ‪.Blended‬‬

‫هكذا إذا‪ ،‬يجدر بك أن تكون قد تعوّدت قليلا ًعلى هذه الأساليب الخاصة بـ‪ SDL_ttf‬في الكتابة‪.‬‬

‫لقد قل ُت لك أنه توجد ‪ 12‬دالة لذلك‪.‬‬
‫في الواقع‪ ،‬من أجل ك ّل طر يقة في الكتابة‪ ،‬توجد ‪ 4‬دوال لذلك‪ .‬ك ّل دالة تكتب النص بالاستعانة بمجموعة‬

‫محارف )‪ (Charset‬مختلفة‪ .‬هذه الدوال هي ‪:‬‬

‫• ‪،Latin1‬‬
‫• ‪،UTF8‬‬
‫• ‪،Unicode‬‬
‫• ‪.Unicode Glyph‬‬

‫الأمثل أن تختار ‪ Unicode‬لأنها مجموعة محارف تحوي أغلب الحروف و الإشارات الموجودة على وجه‬
‫الأرض‪ .‬و لـكن‪ ،‬استعمال الـ‪ Unicode‬ليس سهلا دائما ً)محرف واحد يأخذ حجما أكثر من حجم ‪ char‬في‬

‫الذاكرة(‪ ،‬فلن نر َ كيفية استعماله هنا‪.‬‬
‫إذا كان برنامجك مكتوبا ًبالفرنسية فمجموعة ‪ Latin1‬تكفي بإسهاب‪ ،‬يمكنك الاكتفاء بهذه الأخيرة‪.‬‬

‫الدوال الثلاثة التي تستعمل نظام التشفير ‪ Latin1‬هي ‪:‬‬

‫• ‪، TTF_RenderText_Solid‬‬
‫• ‪، TF_RenderText_Shaded‬‬
‫• ‪. TTF_RenderText_Blended‬‬

‫‪434‬‬

‫الطرق المختلفة للكتابة‬

‫مثال عن كتابة نص بطر يقة الـ‪Blended‬‬

‫لـكيّ نختار لونا بـ‪ ،SDL_ttf‬لن نستعمل نفس النوع كما بالـ‪) SDL‬إنشاء متغير من نوع ‪ Uint32‬بالاستعانة‬
‫بالدالة ‪.( SDL_MapRGB‬‬

‫بالعكس‪ ،‬سنستعمل هيكلا جاهزا من طرف الـ‪ SDL‬و هو ‪ . SDL_Color :‬هذا الهيكل يحتوي ثلاثة مركّبات ‪:‬‬
‫كمية الأحمر‪ ،‬الأخضر و الأزرق‪.‬‬

‫إذا أردت إنشاء متغير ‪ ، blackColor‬يجب عليك أن تكتب إذا ‪:‬‬

‫;}‪1 SDL_Color blackColor = {0, 0, 0‬‬

‫احذر لـكي لا تخلط بينها و بين الألوان التي تستعملها عادة ‪! SDL‬‬
‫الـ‪ SDL‬تستعمل متغيرات ‪ Uint32‬يتم إنشاؤها بمساعدة ‪. SDL_MapRGB‬‬

‫بينما ‪ SDL_ttf‬تستعمل متغيرات ‪. SDL_Color‬‬

‫سنقوم بكتابة نص بالأسود في ‪ ، SDL_Surface‬نسميها ‪. text‬‬

‫;)‪1 text = TTF_RenderText_Blended(font, ”Salut les Zér0s !”, blackColor‬‬

‫أنت ترى المعاملات التي بعثتاها بالترتيب ‪ :‬الخط )من نوع ‪ ،( TTF_Font‬النص الذي نريد كتابته و‬
‫أخيرا ً اللون )من نوع ‪.( SDL_Color‬‬

‫يتم تخزين النتيجة في مساحة‪ .‬تحسب ‪ SDL_ttf‬تلفائيا ً الحجم اللازم للمساحة بدلالة حجم النص و عدد الحروف‬
‫التي تريد كتابتها‪.‬‬

‫كما هو الحال بالنسبة لأي مساحة‪ ،‬سيحتوي المؤش ّر ‪ text‬المركّبات ‪ w‬و ‪ h‬التي تشير بالترتيب إلى عرض‬
‫و ارتفاع المساحة‪ .‬إذن فهذه طر يقة جيدة لمعرفة أبعاد النص ما إن تتم كتابة هذا الأخير على المساحة‪ .‬لن يكون‬

‫عليك سوى كتابة ‪:‬‬

‫‪1 text−>w // Gives the width‬‬
‫‪2 text−>h // Gives the height‬‬

‫الشفرة المصدر ية الكاملة لكتابة نص‬

‫أنت تعرف الآن ك ّل ما يجب أن تتم معرفته بخصوص الـ‪ ،SDL_ttf‬فلنرى الشفرة المصدر ية التي تل ّخص كتابة‬
‫نص بطر يقة الـ‪: Blended‬‬

‫‪435‬‬

SDL_ttf ‫ كتابة نصوص باستعمال‬.7.‫الفصل ج‬

1 #include <stdlib.h>

2 #include <stdio.h>

3 #include <SDL/SDL.h>

4 #include <SDL/SDL_image.h>

5 #include <SDL/SDL_ttf.h>

6 int main(int argc, char *argv[])
7
{

8 SDL_Surface *screen = NULL, *text = NULL, *wallpaper = NULL;
9
SDL_Rect position;

10 SDL_Event event;

11 TTF_Font *font = NULL;
12 SDL_Color blackColor = {0, 0, 0};

13 int cont = 1;

14 SDL_Init(SDL_INIT_VIDEO);

15 TTF_Init();

16 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE |

SDL_DOUBLEBUF);

17 SDL_WM_SetCaption(”Managing text with SDL_ttf”, NULL);
18 wallpaper = IMG_Load(”moraira.jpg”);
19 // Loading the font
20 font = TTF_OpenFont(”angelina.ttf”, 65);
21 // Writing the text on the surface with blended mode (the

optimal one)

22 text = TTF_RenderText_Blended(font, ”Salut les Zér0s !”,

blackColor);

23 while (cont)
24 {
25 SDL_WaitEvent(&event);
26 switch(event.type)
27 {
28 case SDL_QUIT:
29 cont = 0;
30 break;
31 }
32 SDL_FillRect(screen, NULL, SDL_MapRGB(screen−>format,

255, 255, 255));

33 position.x = 0;
34 position.y = 0;
35 SDL_BlitSurface(wallpaper, NULL, screen, &position);

// Blitting the wallpaper

36 position.x = 60;
37 position.y = 370;
38 SDL_BlitSurface(text, NULL, screen, &position); //

Blitting the text

39 SDL_Flip(screen);
40 }
41 TTF_CloseFont(font);
42 TTF_Quit();
43 SDL_FreeSurface(text);
44 SDL_Quit();
45 return EXIT_SUCCESS;

436

‫الطرق المختلفة للكتابة‬
‫} ‪46‬‬

‫النتيجة تمث ّل ُها الصورة التالية ‪:‬‬

‫إذا أردت تغيير طر يقة الكتابة للتجريب‪ ،‬لا يوجد سوى سطر للتعديل ‪ :‬السطر الخاص بإنشاء المساحة‬
‫)استدعاء الدالة ‪.( TTF_RenderText_Blended‬‬

‫تأخذ الدالة ‪ TTF_RenderText_Shaded‬معاملا رابعا على عكس الأخرتين‪ .‬هذا المعامل الأخير‬
‫هو لون الخلفية الذي نريد استعماله‪ .‬يجب عليك إذا إنشاء متغير من نوع ‪ SDL_Color‬للإشارة إلى‬

‫لون الخلفية )مثلا أبيض(‪.‬‬

‫خصائص كتابة نص‬

‫يمكن أيضا ًتحديد خصائص الخط‪ ،‬كـغليظ مثلا ً‪ ،‬مائل و مس ّطر‪.‬‬
‫يجب أولا ّ أن يتم تحميل الخط و لهذا يجب أن يتوفر لديك متغير ‪ font‬صحيح‪ .‬و يمكنك إذا استدعاء الدالة‬
‫‪ TTF_SetFontStyle‬التي ستقوم بالتعديل على الخط لـكي يكون غليظا‪ ،‬مائلا أو مس ّطرا حسب الرغبة‪.‬‬

‫الدالة تأخذ معاملين ‪:‬‬

‫• الخط الذي نريد تعديله‪.‬‬

‫‪437‬‬

‫الفصل ج‪ .7.‬كتابة نصوص باستعمال ‪SDL_ttf‬‬
‫• دمج أعلام للإشارة إلى نمط الكتابة الذي نريد إعطاءه ‪ :‬غليظ‪ ،‬مائل أو مس ّطر‪.‬‬

‫بالنسبة للأعلام‪ ،‬يجب عليك استعمال الثوابت التالية ‪:‬‬
‫• ‪ : TTF_STYLE_NORMAL‬عادي‪.‬‬
‫• ‪ : TTF_STYLE_BOLD‬غليظ‪.‬‬
‫• ‪ : TTF_STYLE_ITALIC‬مائل‪.‬‬

‫• ‪ : TTF_STYLE_UNDERLINE‬مس ّطر‪.‬‬
‫بما أنها قائمة من الاعلام‪ ،‬يمكنك الدمج بينها باستعمال الإشارة | كما تعل ّمنا القيام بذلك سابقا ً‪.‬‬

‫فلنجر ّب ‪:‬‬
‫‪1 // Loading the font‬‬
‫;)‪2 font = TTF_OpenFont(”angelina.ttf”, 65‬‬
‫‪3 // The text will be italic and underlined‬‬
‫;)‪4 TTF_SetFontStyle(font, TTF_STYLE_ITALIC | TTF_STYLE_UNDERLINE‬‬
‫‪5 // Writing the text in italic and underlined modes‬‬
‫;)‪6 text = TTF_RenderText_Blended(font, ”Salut les Zér0s !”, blackColor‬‬

‫النتيجة ‪ :‬النص مكتوب بخاصية مائل و مس ّطر ‪:‬‬

‫لإرجاع خط ما إلى حالته العادي ّة‪ ،‬يكفي أن نعيد استدعاء الدالة ‪ TTF_SetFontStyle‬باستعمال العلم‬
‫‪ TTF_STYLE_NORMAL‬هذه المر ّة‪.‬‬

‫‪438‬‬

‫الطرق المختلفة للكتابة‬

‫تمرين ‪ :‬العداد‬

‫سيجمع هذا التمرين بين المفاهيم التي تعل ّمتها في هذا الفصل و فصل التحكّم في الوقت‪ .‬مهمّتك‪ ،‬إن قبلتها‪ ،‬هي‬
‫إنشاء عداد ٺتصاعد قيمته ك ّل أعشار الثانية‪ ،‬أي أنه سي ُظهر بشكل تق ّدمي القيم التالية ‪،300 ،200 ،100 ،0 :‬‬

‫‪… 400‬بعد ثانية‪ ،‬يجدر بالرقم ‪ 1000‬أن يظهر‪.‬‬

‫طر يقة للكتابة في سلسلة محارف‬

‫لـكي تح ّل هذا التمرين‪ ،‬ستحتاج إلى معرفة كيفية الكتابة داخل سلسلة محارف في الذاكرة‪.‬‬
‫في الواقع يجب عليك أن تعطي للدالة ‪ TTF_RenderText‬متغيرا من نوع *‪ char‬لـكن ماهو متوف ّر لديك‬

‫هو عدد )من نوع ‪ int‬مثلا(‪ .‬كيف يمكننا تحو يل عدد إلى سلسلة محارف ؟‬

‫يمكننا أن نستعمل من أجل هذا الدالة ‪. sprintf‬‬
‫إنها تعمل بنفس الطر يقة التي تعمل بها ‪ ، fprintf‬الاختلاف الوحيد هو أنه في عوض الكتابة في ملف‪،‬‬

‫ستتم الكتابة في سلسلة محارف )الحرف ‪ s‬يختصر الكلمة ‪ string‬و التي تعني ”سلسلة محارف” بالإنجليز ي ّة(‪.‬‬
‫أ ّول معامل تق ّدمه سيكون إذا مؤش ّرا نحو جدول من ‪. char‬‬

‫قم بحجز مكان كا ٍف من أجل جدول ‪ char‬إذا أردت ألا تتجاوز في الذاكرة !‬

‫مثال ‪:‬‬

‫;)‪1 sprintf(time, ”Time : %d”, counter‬‬

‫هنا‪ ،‬المتغير ‪ time‬هو جدول محارف )‪ 20‬محرفا(‪ ،‬و ‪ counter‬هو متغير من نوع ‪ int‬يحوي الزمن‪.‬‬
‫بعد هذه التعليمة‪ ،‬سلسلة المحارف ‪ time‬ستحتوي مثلا على ”‪.”Time : 500‬‬

‫هي ّا‪ ،‬حان وق ُت العمل !‬

‫التصحيح‬

‫هذا تصحيح ممكن للتمرين ‪:‬‬

‫‪1‬‬ ‫)][‪int main(int argc, char *argv‬‬
‫‪2‬‬
‫{‬

‫;‪3 SDL_Surface *screen = NULL, *text = NULL‬‬
‫;‪4 SDL_Rect position‬‬

‫;‪5 SDL_Event event‬‬

‫;‪6 TTF_Font *font = NULL‬‬

‫‪439‬‬

SDL_ttf ‫ كتابة نصوص باستعمال‬.7.‫الفصل ج‬

7 SDL_Color blackColor = {0, 0, 0}, whiteColor = {255, 255,

255};

8 int cont = 1;
9 int currentTime = 0, previousTime = 0, counter = 0;
10 char time[20] = ””; // A table of char big enough
11 SDL_Init(SDL_INIT_VIDEO);
12 TTF_Init();
13 screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE |

SDL_DOUBLEBUF);

14 SDL_WM_SetCaption(”Managing text with SDL_ttf”, NULL);
15 // Loading the police
16 font = TTF_OpenFont(”angelina.ttf”, 65);
17 // Time and text initialization
18 currentTime = SDL_GetTicks();
19 sprintf(time, ”Temps : %d”, counter);
20 text = TTF_RenderText_Shaded(font, time, blackColor,

whiteColor);

21 while (cont)
22 {
23 SDL_PollEvent(&event);
24 switch(event.type)
25 {
26 case SDL_QUIT:
27 cont = 0;
28 break;
29 }
30 SDL_FillRect(screen, NULL, SDL_MapRGB(screen−>format,

255, 255, 255));

31 currentTime = SDL_GetTicks();
32 if (currentTime − previousTime >= 100) // If 100ms at

least have passed

33 {
34 counter += 100; // We add 100ms to the

counter

35 sprintf(time, ”Temps : %d”, counter); // We

write in the string ”time” the new time

36 SDL_FreeSurface(text);// We delete the

previous surface

37 text = TTF_RenderText_Shaded(font, time,

blackColor, whiteColor); // We write the

sring ”time” in SDL_Surface

38 previousTime = currentTime; // We update the

previous time

39 }
40 position.x = 180;
41 position.y = 210;
42 SDL_BlitSurface(text, NULL, screen, &position); //

Blitting the text

43 SDL_Flip(screen);
44 }
45 TTF_CloseFont(font);

440

‫الطرق المختلفة للكتابة‬

‫‪46‬‬ ‫;)(‪TTF_Quit‬‬
‫‪47‬‬ ‫;)‪SDL_FreeSurface(text‬‬
‫‪48‬‬ ‫;)(‪SDL_Quit‬‬
‫‪49‬‬ ‫;‪return EXIT_SUCCESS‬‬
‫} ‪50‬‬
‫الصورة التالية تمث ّل النتيجة في غضون ‪ 13,9‬ثانية بالتحديد ‪:‬‬

‫لا تتردد في تنز يل المشروع إذا أردت دراسته بالتفصيل و تحسينه‪ .‬هو ليس مثاليا ًبعد ‪ :‬يمكننا مثلا ًاستعمال‬
‫‪ SDL_Delay‬لتجن ّب استعمال المعالج بنسبة ‪.100%‬‬

‫‪https://openclassrooms.com/uploads/fr/ftp/mateo21/ttf_exercice_temps.‬‬
‫)‪zip (437 Ko‬‬

‫للذهاب بعيدا‬

‫إذا أردت التق ّدم و تحسين هذا البرنامج‪ ،‬يمكنك أن تحاول صنع لعبة أين يجب النقر بالفأرة العدد الأقصى من‬
‫المرات الممكنة في النافذة في وقت محدود حيث تتزايد قيمة العداد بعد ك ّل نقرة‪.‬‬

‫يجب أن يتم إظهار عداد عكسي‪ .‬حينما يصل إلى الصفر‪ ،‬نظهر عدد النقرات التي تم القيام بها و نطلب من‬
‫المستعمل ما إن كان يريد إعادة المحاولة‪.‬‬

‫يمكنك أيضا ًمعالجة أفضل النتائج و تسجيلها في ملف‪ .‬هذا سيساعدك في التدرب من جديد على استخدام‬
‫الملفات في الـ‪.C‬‬
‫حظا ًموفقا !‬

‫‪441‬‬

‫الفصل ج‪ .7.‬كتابة نصوص باستعمال ‪SDL_ttf‬‬

‫تلخيص‬

‫• لا يمكننا أن نكتب نصا ًفي الـ‪ ،SDL‬إلا إن استعملنا تمديدا ً كالمكتبة ‪.SDL_ttf‬‬
‫• تسمح هذه المكتبة بتحميل ملفات خطوط ذات صيغة ‪ .ttf‬بالاستعانة بالدالة ‪. TTF_OpenFont‬‬
‫• توجد ثلاث طرق لكتابة نص‪ ،‬ترتيبها من الأبسط إلى الأكثر تعقيدا ‪ ،Solid :‬ثم ‪ Shaded‬ثم ‪.Blended‬‬

‫• يمكننا الكتابة في ‪ SDL_Surface‬عن طر يق دوال مثل ‪. TTF_RenderText_Blended‬‬

‫‪442‬‬

‫الفصل ج‪8.‬‬

‫تشغيل الصوت بـ‪FMOD‬‬

‫منذ أن اكتشفنا الـ‪ ،SDL‬تعل ّمنا موضعة صور على النافذة‪ ،‬التفاعل مع الم ُستعمل بالفأرة و لوحة المفاتيح‪،‬‬
‫كتابة نصوص‪ ،‬لـكن ينقص أمر بالتأكيد ‪ :‬الصوت !‬

‫سيس ّد هذا الفصل ذلك النقص‪ .‬بما أن الإمكانيّات التي توف ّرها لنا الـ‪ SDL‬من ناحية الصوت محدودة جدا ً‪،‬‬
‫سنكتشف هنا مكتبة متخصصة في الصوت ‪.FMOD :‬‬

‫ج‪ 1.8.‬ٺثبيت ‪FMOD‬‬

‫لماذا ‪ FMOD‬؟‬

‫أنت تعرف ذلك الآن‪ :‬الـ‪ SDL‬ليست فقط مكتبة رسومية‪ .‬هي تسمح أيضا ًبمعالجة الصوت عن طر يق وحدة‬
‫تسمّى ‪ .SDL_audio‬فلماذا إذا ً سنحضّر مكتبة خارجية لا علاقة لها بالـ‪ SDL‬كـ‪ FMOD‬؟‬

‫في الواقع هو اختيار قم ُت به بعد ع ّدة اختبارات‪ .‬كان بإمكاني أن أشرح لك طر يقة معالجة الصوت بالـ‪SDL‬‬
‫لـكن ّي ف ّضلت عدم فعل ذلك‪.‬‬
‫سأشرح موقفي أكثر‪.‬‬

‫لماذا قم ُت بتجن ّب ‪ SDL_audio‬؟‬

‫يعتبر التحكم في الصوت بالـ‪” SDL‬منخفض المستوى”‪ .‬هذا يعني أنه يجب القيام بالعديد من التعامُلات الدقيقة‬
‫كي نستطيع تشغيل الصوت‪ .‬بمعنى آخر‪ ،‬سيكون الأمر صعبا ًو لا أجد ذلك ممتعا ً‪ .‬توجد مكتبات أخرى تسمح‬

‫بتشغيل الصوت بشكل بسيط‪.‬‬

‫‪443‬‬

‫الفصل ج‪ .8.‬تشغيل الصوت بـ‪FMOD‬‬

‫تذكير بسيط ‪ :‬مكتبة ”منخفضة المستوى” هي مكتبة قريبة من الحاسوب‪ .‬يجب أن نتعر ّف إذا على قليل‬
‫من العمل الداخلي للحاسوب كي نستفيد منها و يتطلب الأمر في الواقع وقتا ًأكثر من الوقت اللازم للقيام‬

‫بنفس الشيء مع مكتبة ”عالية المستوى”‪.‬‬
‫لا تنس أ ّن ك ّل شيء نسبيّ ‪ :‬لا توجد مكتبات منخفضة المستوى من جهة و أخرى عالية المستوى من‬
‫جهة أخرى‪ .‬هي فقط أكثر أو أقل من بعضها البعض في المستوى‪ .‬مثلا‪ ،‬المكتبة ‪ FMOD‬عالية المستوى‬

‫مقارنة بالوحدة ‪ SDL_audio‬من الـ‪.SDL‬‬

‫تفصيل آخر مهم‪ ،‬تسمح الـ‪ SDL‬بتشغيل صوت بصيغة ‪ WAV‬فقط‪ .‬صيغة الصوت هذه ليست مضغوطة‪.‬‬
‫أي أن موسيقى من ‪ 3‬دقائق تأخذ عشرات الميغا أوكتي‪ ،‬على عكس الصيغ المضغوطة مثل ‪ MP3‬أو ‪ Ogg‬التي‬

‫تحجز حجم ذاكرة أق ّل بكثير )من ‪ 2‬إلى ‪ 3‬ميغا أوكتي(‪.‬‬

‫في الواقع‪ ،‬لو نفكّر في الأمر جي ًدا ً‪ ،‬كان الأمر مشابها ًبالنسبة للصور‪ ،‬فالـ‪ SDL‬لا ٺتعامل إلا مع الصيغة ‪BMP‬‬
‫) ُصور غير مضغوطة( بشكل مبدئي‪ .‬مما استوجب علينا تسطيب مكتبة إضافية و هي ‪ SDL_image‬لنتم ّكن من‬

‫قراءة صيغ الصور الأخرى كـ‪ ،GIF ،PNG ،JPEG‬إلخ‪.‬‬

‫اعلم أنه هناك مكتبة مكافئة بالنسبة للصوت و هي ‪ .SDL_mixer :‬هي قادرة على قراءة عدد كبير من صيغ‬
‫الصوت‪ ،‬من بينها ‪… Midi ،Ogg ،MP3‬و رغم ذلك‪ ،‬لم أكلّمك عن هذه المكتبة‪ .‬لماذا ؟‬

‫لماذا قم ُت بتجن ّب ‪ SDL_mixer‬؟‬

‫‪ SDL_mixer‬هي مكتبة نضيفها للـ‪ ،SDL‬بطر يقة ‪ .SDL_image‬هي سهلة للاستعمال و تقرأ العديد من صيغ‬
‫الصوت المختلفة‪ .‬لـكن‪ ،‬و بعد الاختبارات التي قم ُت بها‪ ،‬تبې ّن لي أن هذه المكتبة تحتوي عِل َلا مزعجة بالإضافة‬

‫إلى كونها محدودة من ناحية المزايا التي تمنحها‪.‬‬

‫من أجل هذه الأسباب تو ّجهت مباشرة إلى ‪ ،FMOD‬مكتبة لا علاقة لها بالـ‪ SDL‬بالتأكيد لـكن لها الأفضلية‬
‫كونها قو ية و متداولا عليها‪.‬‬

‫تنز يل ‪FMOD‬‬

‫إن كنت قد حكيت لك كل هذا‪ ،‬فهذا فقط لأخبرك بأن اختيار ‪ FMOD‬لم يكن عشوائيا ً‪ .‬ببساطة هي أفضل‬
‫مكتبة مجانية استطعت إيجادها‪.‬‬

‫كما أنها سهلة الإستخدام كـ‪ SDL_mixer‬بأفضلية لا يمكن تجاهلها ‪ :‬لا توجد بها مشاكل برمجية‪.‬‬

‫تسمح ‪ FMOD‬بالقيام بالعديد من الوظائف التي لا تسمح بها ‪ ،SDL_mixer‬كالتأثيرات الصوتيّة ثلاثية‬
‫الأبعاد‪.‬‬

‫‪444‬‬

‫ٺثبيت ‪FMOD‬‬

‫‪ FMOD‬هي مكتبة مجانية لـكن ليست تحت رخصة ‪ LGPL‬على عكس الـ‪ .SDL‬هذا يعني أنه بإمكانك‬
‫أن تستخدمها مادامت لم تحقق بها برامج مدفوعة‪ .‬إذا أردت أن يكون البرنامج غير مجاني‪ ،‬يجب أن تدفع‬

‫رسوما ًلمؤل ّف المكتبة )سأتركك ت ّطلع على الأسعار من خلال الموقع الرسمي لـ‪.(FMOD‬‬
‫كثير من الألعاب التجار ية تستعمل ‪ FMOD‬و من أشهر هذه الألعاب ‪World of ،Starcraft II :‬‬

‫‪ ،Crysis 2 ،Warcraft : Cataclysm‬إلخ‪.‬‬

‫ٺتوفر العديد من نسخ ‪ ،FMOD‬و النسخة الموجهّة إلى الاستعمال في أنظمة التشغيل المألوفة )‪،GNU/Linux‬‬
‫‪ (… ،Mac OS X ،Windows‬ت ُدعى ‪.FMOD Ex Programmers API‬‬

‫نزّل إذا نسخة ‪ FMOD Ex‬المناسبة لنظام التشغيل الخاص بك‪ .‬خذ النسخة المسمّاة ”مستقرة” )‪.(stable‬‬

‫و تأكد بشكل خاص ما إن كان لديك نظام تشغيل ‪ 32 bits‬أو ‪) 64 bits‬في ‪ ،Windows‬قم بنقر يميني على‬
‫جهاز الكمبيوتر )‪ (Computer‬ثم في قسم الخصائص )‪ (Properties‬تجد المعلومة اللازمة(‪.‬‬

‫‪http://www.fmod.org/fmod-downloads.html#FMODExProgrammersAPI‬‬

‫ٺثبيت ‪FMOD‬‬

‫يعمل التثبيت بنفس مبدأ عمل المكتبات السابقة‪ ،‬أي مثل الـ‪.SDL‬‬

‫يجدر بالملف الذي حم ّلته أن يكون ملفا ً تنفيذيا ً )في ‪ ،(Windows‬أو أن يكون أرشيفا ) ‪ .dmg‬في‬
‫‪ Mac OS X‬و ‪ .tar.gz‬في ‪.(GNU/Linux‬‬

‫‪ .1‬ثب ّت ‪ FMOD Ex‬على قرصك الصلب‪ .‬الملفات التي نحتاجها يجب أن ٺتواجد في مجل ّد يشبه هذا ‪:‬‬
‫‪. C:\Program Files\FMOD SoundSystem\FMOD Programmers API Win32\api‬‬
‫‪ .2‬في هذا المجل ّد تجد الـ‪ DLL‬الخا ّص بـ‪ ( fmodex.dll ) FMOD Ex‬و يجب أن يوضع في مجل ّد المشروع‪.‬‬
‫الـ‪ DLL‬الأخرى‪ ،‬أي ‪ fmodexL.dll‬تعمل على تنقيح العلل البرمجية‪ .‬لن نقوم بذلك هنا‪ .‬تذكّر فقط‬

‫بأن الملف ‪ fmodex.dll‬هو الذي يجب أن ت ُعطيه مع الملف التنفيذي للبرنامج‪.‬‬
‫‪ .3‬في المجل ّد ‪ ، api/inc‬تجد الملفات ‪ . .h‬ضعها كل ّها إلى جانب الملفات الرأسية التي هي في مجل ّد البيئة‬
‫التطوير ية‪ .‬مثلا ‪) Code Blocks/mingw32/include/fmodex :‬لقد أنشأت مجل ّدا خصيصا ً‬

‫لأجل ‪ FMOD‬كما مثل الـ‪.(SDL‬‬
‫‪ .4‬في المجل ّد ‪ ، api/lib‬استرجع الملف الموافق للمترجم‪ .‬يجدر بملف نصّي أن يشير إلى أي ملف يجب أن‬

‫نأخذ‪.‬‬

‫‪445‬‬

‫الفصل ج‪ .8.‬تشغيل الصوت بـ‪FMOD‬‬

‫• إذا كنت تستعمل ‪ ،Code::Blocks‬فالمترجم هو ‪ .mingw‬أنسخ الملف ‪ libfmodex.a‬في‬
‫المجل ّد ‪ lib‬للبيئة التطوير ية‪.‬‬

‫في ‪ ،Code::Blocks‬إنه المجل ّد ‪. CodeBlocks/mingw32/lib‬‬
‫• إذا كنت تستعمل ‪ ،Visual C++‬استرجع الملف ‪. fmodex_vc.lib‬‬

‫‪ .5‬أخيرا ً‪ ،‬الشيء الأكثر أهمية رب ّما‪ ،‬يوجد مجل ّد ‪ documentation‬في المجل ّد ‪ .FMOD Ex‬من المفروض‬
‫أن تتم إضافة اختصارات إلى قائمة ”إبدأ” نحو هذه الملفات التوجيهية‪ .‬أبق نظرك عليها لأننّا لا يمكن ألا‬
‫نكتشف ك ّل ميزات ‪ FMOD Ex‬في هذا الفصل‪ .‬ستحتاج إلى هذه الملفات في أقرب الآجال بالتأكيد‪.‬‬
‫يبقى أن نخصص المشروع‪ .‬هنا أيضا ًو مثل ك ّل مرة ‪ :‬افتح المشروع بواسطة البيئة التطوير ية المف ّضلة و‬

‫أضف الملف ‪) .a‬أو ‪ ( .lib‬إلى قائمة الملفات التي يجب أن يسترجعها محرر الروابط‪.‬‬
‫في ‪) Code::Blocks‬يخالجني شعور بأنني أقوم بالتكرار(‪ ،‬إذهب إلى قائمة ‪Build Options / Project‬‬
‫ثم قسم ‪ ، Linker‬أنقر على ‪ Add‬و أشر إلى المسار الذي يوجد به الملف ‪ .a‬إذا ظهرت لك الرسالة ‪:‬‬
‫”? ‪ ،”Keep as a relative path‬أنصحك بأن تجيب بالسلب لـكن يجدر بالأمور أن تشتغل في كلتا‬

‫الحالتين‪.‬‬
‫تم ٺثبيت ‪ ،FMOD Ex‬فلن َر َ بسرعة مما هي مُشَ َ ّكل َة‪.‬‬

‫ج‪ 2.8.‬تهيئة و تحرير غرض نظامي‬

‫المكتبة ‪ FMOD Ex‬متوف ّرة من أجل اللغتين ‪ C‬و ‪.C++‬‬
‫الشيء الخاص فيها هو أن مطوّري هذه المكتبة احتفظوا ببعض التناسق في ”تركيب الكلمات” )‪ (syntax‬بين‬
‫اللغتين‪ .‬الميزة الأولى هي أنه إذا تعل ّمت التعامل مع ‪ FMOD Ex‬في لغة الـ‪ C‬ستتمكن من فعل ذلك في الـ‪C++‬‬

‫بنسبة ‪.95%‬‬

‫تضمين الملف الرأسي‬

‫قبل ك ّل شيء‪ ،‬يلزمك أن تقوم بتضمين الملف الرأسي الخاص بـ‪ .FMOD‬لابأس في التذكير بكتابته ‪:‬‬
‫>‪1 #include <fmodex/fmod.h‬‬

‫لقد وضعت هذا الملف في المجل ّد الداخلي ‪ . fmodex‬ع ّدل على هذا السطر من الشفرة على حسب المسار‬
‫الذي يتواجد به الملف عندك‪.‬‬

‫إذا كنت تعمل على ‪ ،GNU/Linux‬يجدر بالتسطيب أن يتم تلقائيّا في المجل ّد ‪. fmodex‬‬

‫‪446‬‬

‫تهيئة و تحرير غرض نظامي‬

‫إنشاء و تهيئة غرض نظامي‬

‫الغرض النظامي هو عبارة عن متغير نستفيد منه على طول البرنامج لـكي نعر ّف معاملات المكتبة‪.‬‬
‫تذكّر أنه بالـ‪ SDL‬مثلا ً‪ ،‬كان يجب أن نهي ّئ المكتبة بشكل مباشر بواسطة دالة‪ .‬هنا‪ ،‬دليل الاستعمال مختلف‬

‫قليلا ً‪ :‬في عوض تهيئة ك ّل المكتبة‪ ،‬لن نعمل إلا بغرض )‪ (Object‬دوره تعر يف سلوك هذه الأخيرة‪.‬‬

‫لـكي ننشئ غرضا نظاميا‪ ،‬يكفي أن نعر ّف مؤش ّرا من نوع ‪ . FMOD_SYSTEM‬مثلا ‪:‬‬
‫;‪1 FMOD_SYSTEM *system‬‬

‫لـكي نحجز مكانا ً في الذاكرة من أجل هذا الغرض النظامي‪ ،‬نستعمل الدالة ‪FMOD_System_Create‬‬
‫و التي نموذجها هو الآتي ‪:‬‬

‫;)‪1 FMOD_RESULT FMOD_System_Create(FMOD_SYSTEM ** system‬‬

‫لاحظ أن هذه الدالة تأخذ مؤش ّرا نحو مؤش ّر يؤش ّر نحو ‪ . FMOD_SYSTEM‬القر ّاء الأكثر حرصا ً كانوا قد‬
‫لاحظوا أنه لدى تعر يف المؤش ّر ‪ ، FMOD_SYSTEM‬لم يتم حجزه بواسطة ‪ malloc‬أو أي دالة أخرى‪ .‬لهذا‬

‫السبب تماما ًتأخذ الدالة ‪ FMOD_SYSTEM‬معاملا من ذلك النوع لـكي تحجز مكانا ًللمؤش ّر النظامي‪.‬‬

‫بعد تعر يف الغرض النظامي ‪ ،‬تكفي كتابة ‪:‬‬

‫‪1‬‬ ‫;‪FMOD_SYSTEM *system‬‬
‫‪2‬‬
‫;)‪FMOD_System_Create(&system‬‬

‫هكذا إذا‪ ،‬بما أننا نتوف ّر الآن على الغرض النظامي‪ ،‬لم يتب ّق علينا سوى تهيئته‪ .‬لفعل هذا‪ ،‬نستعمل الدالة‬
‫‪ FMOD_System_Init‬ذات النموذج ‪:‬‬

‫(‪1 FMOD_RESULT FMOD_System_Init‬‬

‫‪2‬‬ ‫‪FMOD_SYSTEM * system,‬‬
‫‪3‬‬
‫‪int maxchannels,‬‬

‫‪4 FMOD_INITFLAGS flags,‬‬

‫‪5‬‬ ‫‪void * extradriverdata‬‬
‫;) ‪6‬‬

‫• المعامل ‪ system‬هو المعامل الذي يهمّنا أكثر‪ ،‬لأنه المؤش ّر الذي سنقوم بتهيئته‪.‬‬

‫• المعامل ‪ maxchannels‬يمث ّل العدد الأقصى للقنوات التي يجب أن تديرها ‪ . FMOD‬بمعنى آخر‪ ،‬هو‬
‫العدد الأقصى للأصوات التي يمكن أن يتم تشغيلها في نفس الوقت‪ .‬هذا يعتمد على قوة بطاقة الصوت‬
‫لديك ‪ .‬ننصح عادة بقيمة ‪) 32‬قيمة كافية من أجل معظم الألعاب البسيطة(‪ .‬لمعلوماتك‪ ،‬يمكن نظر يا ً‬

‫لـ ‪ FMOD‬إدارة ‪ 1024‬قناة مختلفة‪ ،‬لـكن بهذا المستوى ستخاطر بجعل حاسوبك يشتغل كثيرا !‬

‫‪447‬‬

‫الفصل ج‪ .8.‬تشغيل الصوت بـ‪FMOD‬‬

‫• المعامل ‪ flag‬لا يهمّنا كثيرا ً في هذا الدرس‪ ،‬سنكتفي بإعطائها القيمة ‪. FMOD_INIT_NORMAL‬‬
‫• المعامل ‪ extradriverdata‬لا يهمّنا أيضا ً‪ ،‬سنعطيه القيمة ‪. NULL‬‬

‫مثلا‪ ،‬لـكي نعر ّف‪ ،‬نحجز‪ ،‬و نهي ّئ غرضا نظاميا‪ ،‬نقوم بكتابة التالي ‪:‬‬

‫‪1‬‬ ‫;‪FMOD_SYSTEM *system‬‬
‫‪2‬‬
‫;)‪FMOD_System_Create(&system‬‬

‫;)‪3 FMOD_System_Init(system, 2, FMOD_INIT_NORMAL, NULL‬‬

‫نتوف ّر الآن على غرض نظامي جاهز للإستعمال‪.‬‬

‫;)‪1 FMOD_System_Close(system‬‬ ‫غلق و تحرير غرض نظامي‬
‫;)‪2 FMOD_System_Release(system‬‬
‫نغلق ثم ّ نحرر الغرض النظامي بواسطة دالتين ‪:‬‬

‫هل يجدر بي أن أعل ّق على هذه الشفرة ؟‬

‫ج‪ 3.8.‬الأصوات القصيرة‬

‫فلنبدأ بدراسة الأصوات قصيرة الم ّدة‪.‬‬
‫”الصوت القصير” كما أسم ّيه‪ ،‬هو صوت يستمر ّ غالبا ًبضعة ثوا ٍن )أحيانا ًأقل من ثانية( و غالبا ًما ي ُوجه للاستعمال‬

‫المنتظم‪.‬‬

‫أمثلة عن أصوات قصيرة ‪:‬‬
‫• صوت إطلاق رصاصة‪.‬‬

‫• صوت مشي اللاعب‪.‬‬
‫• صوت ‪) tic-tac‬لـكيّ نوتّر اللاعب قبل انتهاء العد العكسي(‪.‬‬

‫• صوت التصفيق‪.‬‬
‫• إلخ‪.‬‬

‫باختصار‪ ،‬كل صوت لا يعتبر موسيقى‪.‬‬
‫بشكل عام‪ ،‬هذه الأصوات قصيرة المدة إلى درجة أنه لا نحتاج إلى ضغطها‪ .‬نجدها إذا في غالب الأحيان بصيغة‬

‫‪ WAV‬غير مضغوطة‪.‬‬

‫‪448‬‬

‫الأصوات القصيرة‬

‫إيجاد الأصوات القصيرة‬

‫قبل أن نبدأ‪ ،‬سيكون من الجيد أن نتعر ّف على بعض المواقع التي تقترح بنوكا ًمن الأصوات‪ .‬بالفعل‪ ،‬لا أحد‬
‫يريد أن يبدأ في تسجيل الأصوات بنفسه في المنزل‪.‬‬

‫سيكون الأمر جيدا ً فالأنترنت تقترح أصواتا قصيرة‪ ،‬غالبا ًبصيغة ‪.WAV‬‬
‫أين نجدها ؟ قد يبدو الأمر سخيفا ً‪ ،‬لا يجب علينا أن نفكّر في ذلك )مع أنه لازم(‪ ،‬لـكن ‪ Google‬صديقنا‪.‬‬
‫بشكل عشوائي‪ ،‬أكتب ‪ ”Free Sounds” :‬و التي تعني ”أصوات مجانية” بالإنجليز ي ّة‪ ،‬ستظهر لي ملايين النتائج‪.‬‬

‫لا شيء تحتاجه أكثر من الصفحة الأولى للبحث‪ ،‬ستجد ضال ّتك هناك‪.‬‬
‫شخصيا ًحفظت الموقع ‪ ،FindSounds.com‬محر ّك بحث متخصص في الأصوات‪ .‬لا أدري إن كان الأفضل‪،‬‬

‫لـكن على أي حال هو موقع كامل‪.‬‬

‫إذا لم تعرف ماهي الكلمات المفتاحية التي تستعملها في البحث‪ ،‬توجّه إلى الصفحة الخاصة بأمثلة عن‬
‫الكلمات المفتاحية للبحث‪ .‬يجب عليك أن تجيد بعض الكلمات الإنجليز ية بالتأكيد )لـكن على أي حال‬

‫إن كنت تريد أن تصبح مبرمجا ً‪ ،‬كيف ستفعل لو أنك لا تجيد على الأقل اللغة الإنجليز ية ؟(‪.‬‬

‫بالبحث عن كلمة ”‪ ،”gun‬سنجد أطنان من أصوات إطلاق النار بالبندقية‪ ،‬لو نكتب ”‪ ”door‬سنجد أصوات‬
‫تحر ّك الباب )الصورة التالية(‪ ،‬إلخ‪.‬‬

‫‪449‬‬

‫الفصل ج‪ .8.‬تشغيل الصوت بـ‪FMOD‬‬

‫الخطوات التي يجب إتّباعها لتشغيل الصوت‬

‫الخطوة الأولى تن ّص على تحميل الصوت الذي نريد تشغيله في الذاكرة‪.‬‬
‫أنصحك بتحميل ك ّل الأصوات التي ترى أنك ستستعملها كثيرا ً منذ بداية البرنامج‪ .‬تقوم بتحريرها في النهاية‪ .‬في‬

‫الواقع‪ ،‬ما إن يتم تحميل الصوت في الذاكرة‪ ،‬ستكون قراءته سر يعة جدا ً‪.‬‬
‫المؤش ّر‬

‫الخطوة الأولى ‪ :‬إنشاء المؤش ّر من نوع ‪ FMOD_SOUND‬و الذي يمث ّل الصوت‪.‬‬
‫;‪1 FMOD_SOUND *fire = NULL‬‬

‫تحميل الصوت‬
‫الخطوة الثانية ‪ :‬تحميل الصوت بواسطة الدالة ‪ . FMOD_System_CreateSound‬هي تأخذ …خمسة‬

‫معاملات ‪:‬‬
‫‪450‬‬


Click to View FlipBook Version