مُنـشئ المستو يات
أخيرا ً ،تحميل المستوى من الملف لم يكن معقدا ً .الف ّخ الوحيد الذي وُجب تجن ّبه هو التفكير في تحو يل القيمة
’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