حمل الملفات المرفقة مع هذه المقالة
السلام عليكم
سنتناول في هذا المقال الألواح اللونية (palettes)، والتي شاع استخدامها في برمجة الألعاب حتى وقت قريب. ولكي نتمكن من إلقاء نظرة جيدة على الألواح اللونية، سنقسم الموضوع إلى عدة نقاط وهي:
- تعريف الألواح اللونية.
- استعمال برنامج The Gimp للتعامل مع لوح لوني لصورة ما.
- شرح كيفية الاستفادة من الألواح اللونية في برمجة الألعاب.
- الاستفادة من الألواح اللونية برمجياً.
- عيوب الألواح اللونية وإمكانية تجاوزها.
لنبدأ دون إطالة في شرح النقطة الأولى:
تعريف الألواح اللونية
يطلق مصطلح اللوح اللوني على جدول يحتوي 256 خانة مرقمة من الـ 0 إلى 255، كل خانة ترمز إلى لون معين، الصورة أدناه توضح هذا التعريف:
لاحظوا في الصورة المناطق الملونة باللون الأحمر، فهي تمثل اللون الموجود في الخانة وترتيب الخانة، وكما نلاخظ فإن أول لون موجود في الخانة رقم 0، وآخر لون موجود في الخانة 255 توضع بعض هذه الألوان (و أحياناً توضع كلها) في الصورة وفق ترتيب معين يسمح لنا في النهاية بالحصول على صورة معبرة عن شخصية، عن شيئ، أو عن حرف. لنوضح أكثر، يجب أن نعلم أن الصور التي نراها على شاشة الحاسوب تتكون من بكسلات متجاورة، والبكسل هو أصغر وحدة على الشاشة يمكن التعامل معها. لنشاهد معاً الصور التالية لكي نحقق المفهوم:
هذه الصورة تملك الأبعاد التالية:
- الطول: 32 بكسل.
- العرض: 32 بكسل.
- مجموع البكسلات: 32×32=1024 بكسل مكون للصورة.
اللوح اللوني للصورة مع ترتيب البكسلات:
ملاحظة: كل من البكسلات الصفراء والزرقاء في أعلى اليسار تمثل بكسلاً واحداً، وانطلاقاً من تلك البكسلات تستطيعون مقارنة عدد بكسلات الصورة الموجود في كل سطر.
ملاحظة: قمت بوضع لون أخضر فاتح ولون أزرق فاتح على اللوح اللوني وذلك لتوضيح كيفية الترتيب لتشكيل الصورة النهائية. كل نقطة تمثل بكسلاً واحداً.
هام: الألوان المخزنة في الألواح اللونية لا تحتوي على قناة الشفافية ألفا (alpha channel).
كل لون من ألوان اللوح يخزن المعلومات التالية:
قيمة اللون الأحمر (Red).
قيمة اللون الأخضر (Green).
قيمة اللون الأزرق ( Blue).
أو ما يعرف بنظام الـ RGB.
الآن وقد تعرفنا على اللوح اللوني وكيفية تشكيله للصور، لننطلق إلى النقطة التالية.
استعمال برنامج The Gimp للتعامل مع لوح لوني لصورة ما
في هذه النقطة سوف نتعلم كيفية الحصول على لوح لوني لصورة معينة باستعمال برنامج تعديل صور فعال ومجاني :The Gimp.
الخطوة الأولى هي إظهار نافذة الألواح اللونية، وذلك بالضغط على الأزرار بالترتيب التالي:
- Windows
- Dockable Dialogs
- Colormap
ستظهر لك النافذة التالية:
لنقم بفتح صورة ما ولتكن هذه الصورة:
إذا كانت الصورة التي تم فتحها ليس لها لوح لوني فإن النافذة التي أعددناها ستبقى فارغة. لهذا يجب صنع لوح لوني للصورة وذلك بجعلها Indexed Color أي صورة ذات ألوان مرتبة، وبصيغة أخرى ذات لوح لوني، ولتحقيق هذا الغرض نتبع الخطوات التالية:
ستظهر لك تافذة بها عدة اختيارات، لكنها لا تهمنا في هذا الدرس، لذلك لنضغط على تحويل (convert) مباشرة. عند انتهاء عملية التحويل سيظهر اللوح اللوني في النافذة التي أعددناها سابقاً.
أما إذا كانت الصورة المراد فتحها تدعم اللوح اللوني من البداية، فسيظهر اللوح اللوني مباشرة في النافذة المخصصة له (والتي أعددناها في بداية هذه النقطة). الآن إلى الخطوة التالية:
شرح كيفية الاستفادة من الألواح اللونية في برمجة الألعاب
كل ما ذكرناه سابقاً جميل جداً (أم ممل جداً 😜) لكن بصراحة ما فائدة هذا اللوح اللوني؟
حسناً قبل أن نجيب على هذا السؤال، سأقوم بتبرير سبب كتابتي لهذا الدرس، وذلك عن طريق سرد بعض الألعاب التي تستعمل الألواح اللونية في نظام عملها. في الحقيقة كل الألعاب القديمة كانت تعمل بهذا النظام، لكنني سأسرد بعضها:
- لعبة Super Mario Bros، والدليل صورة الشخصية الرئيسية أعلاه.
- لعبة القتال الشهيرة Street Fighter.
- لعبة القتال The King of Fighters. في جميع نسخها عدا الأخيرة (النسخة 11 تم إنتاجها سنة 2007 أي أن هذه التقنية ما زالت مفيدة).
الآن سنبدأ بشرح كيفية الاستفادة من الألواح اللونية في كل من لعبتي القتال: Street Fighter و The King of Fighters.
باسم الله:
انظر معي الصورة التالية:
سوف نركز فقط على صورة الشخصية ولندع الخلفية جانباً.
إذا استخدمنا لوحاً لونياً فإننا سنحصل على شيئ مشابه لهذا:
لاحظ أن كل الألوان المستخدمة في صورة الشخصية موجودة في هذا اللوح. لاحظ أيضاً أن للألوان ترتيب خاص. مثلاً، الأبيض موجود في الأعلى أي الترتيب رقم 0.
الآن إليك بعض الفوائد من هذه التقنية.
قبل أن أبدأ بشرح الفوائد ألا تلاحظ أن الشخصية محاطة بلون أبيض وهذا لن يجعل اللعبة عملية وذات قيمة. إذن لنتخلص من اللون الأبيض. كيف ذلك؟ إليك الخطوات التي يقوم بها محرك M.U.G.E.N والتي تنطبق في المبدأ مع كل الألعاب التي تستخدم هذه التقنية.
- تحميل الصورة في الذاكرة.
- تحميل اللوح اللوني في الذاكرة.
- تطبيق اللوح اللوني على الصورة حتى تاخذ ألوان الصورة الترتيب الذي نراه في اللوح.
هنا يوجد حلان:
- إما تجاهل تطبيق اللون الذي يحمل الترتيب 0 وعدم تطبيقه على الصورة قبل رسم الصورة، و بالتالي فعند الرسم ليس لدينا لون أبيض وها قد حلت المشكلة.
- أو تجاهل رسم النقط البيضاء أثناء عملية الرسم.
وفي النهاية سنحصل على النتيجة التالية بكلا الطريقتين:
أظن هذه المرة أننا نستطيع القول بأنه لدينا على الأقل شخصية تصلح للعبة.
حسناً أرى البعض يكاد ينقض علي ويقول لي إنني قمت بإعداد جيش لقتل ذبابة، إذ أنه وبصيغة صورة تدعم الشفافية نكون قد تخلصنا من هذه المشكلة تماماً. حسناً معكم حق، لكني سأبرر فائدة اللوح اللوني بطريقة أخرى:
سأبدأ الشرح بسؤال: من منكم لم يلعب لعبة القتال الشهيرة Street Fighter؟
أظن أنهم قليلون جداً من لم يفعلو ذلك. أما بالنسبة للذين فعلوا فإليكم هذه الحالة:
ماذا حدث عندما أردت أنت وصديقك اللعب بنفس اللاعب؟ مثال: Ken vs Ken؟
صورة اللاعب المختار هي كالتالي:
تخيل أن تلعب أنت وصديقك بنفس الشخصية، ألن يصيبك الدوار عندما تحتدم المعركة ولن تعرف مقاتلك من مقاتل صديقك؟ لهذا السبب في مثل هذه الألعاب عندما تختار نفس الشخصية من قبل اللاعبين فإن إحدى الشخصيتين ستكون بلون مغاير. هذا سيسهل اللعب أكثر ويريح اللاعب. في مثالنا السابق سيكون لدينا:
و
أول فكرة ممكن اتباعها لحل الموقف ستكون: "نعيد رسم جميع الصور بلون مغاير". وهذا يعني انتحار مجاني!
هذه المرة لن تفيدكم قناة الشفافية في ملفات الصور الأخرى، والحل الأمثل (على الأقل إلى حد قريب) هو استعمال اللوح اللوني. كيف ذلك؟
بعد أن شرحتُ لكم كيفية عمل اللوح اللوني، إليكم الصورة التالية:
ملاحظة: البرنامج في هذه الصورة هو Adobe Photoshop. يمكن الحصول على نفس النتيجة باستعمال برنامج The Gimp.
بعد التدقيق في محتوى الصورة، نجد أن ما كان أحمراً في اللوح اللوني اأول تم استبداله باللون الأزرق، وهذا دون تغيير الصورة، وإنما فقط بتطبيق لوح لوني مختلف، فأصبح لدينا مقاتل بحلة جديدة.
بهذه الطريقة وفرنا على أنفسنا كماً هائلاً من الصور الإضافية. تخيل شخصية هذا المقاتل تكاد تحتوي على أكثر من 1200 صورة باللباس الأحمر. ماذا لو تمت إعادة رسم وتحميل نفس العدد ضرب عدد البدلات المستخدمة؟ حل غير منطقي وغير عملي البتة. لأن حجم أي لوح لوني لا يمكن أن يتجاوز 768 بايت لأن كل لون يٌحفظ في 3 بايتات (1 أحمر 1 أزرق 1 أخضر) والألواح اللونية تحتوي 256 لون. إذن 256*3=768 ..... اقتصادية جداً جداً جداً.
لكن تبقى مشكلة واحدة يجب الانتباه له عند استعمال التقنية التي قمت بشرحها، ألا وهي ترتيب الألوان. أي أنه يجب ان يكون ترتيب الألوان نفسه لجميع الصور. مثلاً في الصور أعلاه، ترتيب اللون الأحمر الخاص باللباس يجب أن يكون نفسه في كل الصور، وإلا فعندما نغير اللون الأحمر إلى الأزرق ولا يكون الترتيب نفسه فيمكن أن نغير شيئاً آخر غير اللباس.
- الصورة 1: الترتيب رقم 4 يحوي لون الشعر.
- الصورة 2: الترتيب رقم 4 يحتوي لون الجلد.
إذن، عندما نقوم بتجهيز لوح لوني انطلاقاً من الصورة 1، حيث نغير لون الشعر من الأصفر (للشخصية المدروسة) إلى الاسود مثلاً، ثم نطبق اللوح اللوني على جميع الصور فإننا سنجد نتائج غريبة أبسطها:
- الصورة 1: شعر أسود جلد عادي.
- الصورة 2: شعر أصفر جلد أسود.
وهذا لأن لون الشعر في الصورة 2 لا يوجد في الترتيب 4. لهذا وجب تعديل ترتيب الألوان في اللوح اللوني قبل الشروع في إنشاء ألواح لونية مختلفة.
هذه بعض الصور التي تظهر ما كنتُ أشرح، وهي صور لكيفية تغير شعر المقاتل، وأهمية الترتيب في ذلك:
لاحظوا الألوان المسطرة بالأخضر. نغيرها لنحصل على:
ما تغير هو لون الشعر، إذن الترتيبات: 11،12،13،14 تحمل لون الشعر، ويجب أن تكون كذلك لكل الصور (الترتيب تم حسابه انطلاقاً من الصورة، أي أن اللون الموجود يساراً في الأعلى هو الترتيب 1، وليس ضرورياً أن يكون الترتيب الذي قدمته صحيحاً، لكن هذا للشرح فقط).
ولكي تعرفوا مدى نجاعة هذه التقنية، إليكم مثالاً آخر بلوح لوني مختلف مطبق على نفس الصورة:
رائع! أليس كذلك؟
طبعاً رائع!! والأروع أننا سنتعرف على كيفية الاستفادة من ذلك برمجياً.
الاستفادة من الألواح اللونية برمجياً
قبل الخوض في الشرح، يرجى عدم أخذ المكتبة المستعملة SDL بعين الاعتبار. إذ أن المفهوم يطبق بأية مكتبة أخرى، وحتى إن لم تكن لديكم أية خلفية. على هذه المكتبة فسابسط الامور حتى يتسنى لكم تطبيقها مع اي مكتبة تتعاملون بها.
البرنامج هنا مع الكود الخاص به: تحميل الملف
آه، قبل أن أنسى... تجاهلوا كل ما له علاقة أو صلة بـ: Sff هو مجرد ملف يحتوي على صور بامتداد pcx (إحدى الصيغ التي تدعم الألواح اللونية) والكود المكتوب هو لقراءة الملف، وهذا ليس موضوعنا. المهم أننا نستخدم الصور الموجودة فيه. أما ملفات الـ Act فهي مجرد صيغة تحوي اللوح اللوني. آخر نقطة: اللغة المستخدمة هي لغة السي. لنبدأ باسم الله:
أول شيئ اللوح اللوني محفوظ في ملف. إذن لنفتح الملف ونحمل ما فيه إلى الذاكرة:
SDL_Color* Pallet_reader(const char* pal_name)
{
FILE* ptr_PaletteFile = NULL ;
ptr_PaletteFile = fopen(pal_name, "rb");
if ( ptr_PaletteFile == NULL) printf("error can't open the paltte file\n");
}
الآن نحتاج إلى متغيرات لتخزين المعلومات. وكما ذكرتُ سابقاً، فإن الألواح اللونية تحتوي على 256 لون، وكل لون مخزن في 3 بايتات. أي أننا سنحتاج إلى 768 بايت. هذا من جهة، أما من جهة أخرى، فإننا تكلمنا سابقاً عن الترتيب، وأفضل حل للترتيب هو إنشاء مصفوفة (أو جدول أو شعاع، كلّ كما يحلو له تسميته). إذن نحتاج لإنشاء مصفوفة من 256 خانة، كل خانة منها تحوي 3 بايتات ترمز إلى اللون. وبما أنه لا يوجد نوع جاهز له 3 بايتات (لأن الـ int 4 byte char 1 byte) فسنصنع نوعنا الخاص بهذه الطريقة:
struct Color {
unsigned char r // 1byte
, g // 1byte
, b; // 1byte
}; //3byte!!
// SDL هذا النوع متوفر في مكتبة الـ
// SDL_Color باسم
حسناً، بما أنني أعمل بمكتبة الـ SDL، فسأستعمل النوع : SDL_Color وهو نفس النوع المذكور أعلاه.
لنلخص ما ذكرناه:
- مصفوفة من 256 خانة من النوع SDL_Color الذي يمكن أن يخزن 3 بايتات فقط. ثم قراءة الملف. سنحصل على:
SDL_Color* Pallet_reader(const char* pal_name)
{
FILE* ptr_PaletteFile = NULL;
ptr_PaletteFile = fopen(pal_name,"rb"); // فتح الملف
// إنشاء مصفوفة بالحجم المطلوب لتخزين الألوان
SDL_Color *pal= (SDL_Color*)malloc( 256*sizeof(SDL_Color));
unsigned char PaletteFileInfo[768]; // لحفظ معلومات الملف
if ( ptr_PaletteFile == NULL) printf("error can't open the paltte file\n");
// PaletteFileInfo تخزين كامل الملف في
fread( &PaletteFileInfo,768 , 1 , ptr_PaletteFile);
}
الآن كل شيئ جاهز وكل شيئ في ذاكرة الحاسوب. لنراجع ماذا لدينا:
- ملف اللوح اللوني مفتوح، ولقد انتهينا منه لذا سنغلقه في الخطوة القادمة.
- مصفوفة ألوان جاهزة لاستقبال ألوان اللوح اللوني بترتيبها الموجود في الملف.
- مصفوفة معلومات بحجم 768 خانة، كل خانة تحتوي على معلومة لأحد الألوان المشكلة للون الموجود في الترتيبة المرادة. مثال على هذه النقطة:
pal[0].r = PaletteFileInfo[0]
pal[0].g = PaletteFileInfo[1]
pal[0].b = PaletteFileInfo[2]
pal[1].r = PaletteFileInfo[3]
pal[1].g = PaletteFileInfo[4]
pal[1].b = PaletteFileInfo[5]
....
pal[255].r = PaletteFileInfo[765]
pal[255].g = PaletteFileInfo[766]
pal[255].b = PaletteFileInfo[767]
انطلاقاً من هذا المفهوم نقوم بتحميل الألوان في مصفوفة الألوان pal انطلاقاً من مصفوفة المعلومات PaletteFileInfo.
لنقم بذلك كالتالي:
SDL_Color* Pallet_reader(const char* pal_name )
{
FILE* ptr_PaletteFile = NULL;
SDL_Color *pal= (SDL_Color*)malloc( 256*sizeof(SDL_Color));
Uint8 PaletteFileInfo[768];
int i = 0;
int j = 767;
ptr_PaletteFile = fopen(pal_name,"rb");
if ( ptr_PaletteFile == NULL) printf("erreur lecture de pallet\n");
fread( &PaletteFileInfo ,768 , 1 , ptr_PaletteFile);
fclose(ptr_PaletteFile); // غلق الملف
for ( i = 0 ; i < 256 ; i++)
{
pal[i].b = PaletteFileInfo[j--];
pal[i].g = PaletteFileInfo[j--];
pal[i].r = PaletteFileInfo[j--];
}
return pal;
}
ستلاحظون أنني قمتُ بوضع آخر خانة في مصفوفة المعلومات في أول خانة في مصفوفة الألوان، عكس ما أشرت له سابقاً. السبب في ذلك هو نوعية تخزين الألوان في صيغة الـ Act إذ أن اللون الأخير هو الذي له الترتيب الأول، وما قبل الأخير له الترتيب الثاني...الخ.
السطر الأخير في الكود هو الاحتفاظ بعنوان المصفوفة اللونية والتي تمثل اللوح اللوني الذي قمنا بقراءته، وهذا حتى يتسنى لنا استرجاعها والعمل بها لاحقاً.
حسناً، بعد أن حملنا اللوح اللوني يمكننا الآن تطبيقه على الصور التي نريد كالآتي:
static int pallet_index = 0;
static int first_time = 0;
int j,i;
static SDL_Color *color[5];
color[0] = Pallet_reader("Robert_01.act");
color[1] = Pallet_reader("Robert_02.act");
color[2] = Pallet_reader("Robert_02.act");
color[3] = Pallet_reader("Robert_03.act");
color[4] = Pallet_reader("Robert_04.act");
....
if (sff_file->spr[i].img != NULL)
SDL_SetColors(sff_file->spr[i].img, color[pallet_index] , 0, 256);
الدالة SDL_SetColors تقوم بتطبيق اللوح على الصورة الموجودة في sff_file->spr[i].img. العدد 0 هو ترتيب اللون الذي تبدأ عنده الدالة، والعدد 256 هو ترتيب اللون الذي تتوقف عنده الدالة.
النتيجة:
لاحظوا يمكن تخزين ألوان عدة صور في لوح لوني واحد (انظروا إلى الوهج وإلى الشخصية لم يتاثر أيهما بألوان الآخر رغم أن لهما نفس اللوح اللوني).
نقائص الألواح اللونية وإمكانية تجاوزها
الشيئ الوحيد الذي أعيبه على هذا النوع من الملفات (الألواح اللونية) هي محدوديتها في عدد الألوان. فأغلب الألواح اللونية لا يتجاوز عدد ألوانها الـ 256 لون، لكن هذا لا يمنع أن ننشئ لوحنا الخاص بطريقتنا الخاصة ويدعم عدد أكبر من الألوان كما يمكن أيضاً تجاوز هذه العقبة بتقسيم الصورة إلى عدة أجزاء، لكل جزء 256 لون وهذا يسمح بتوفير عدد أكبر من الألوان. لكن كل جزء مقسم سيكون له لوحه الخاص. وبالتالي فإن ما تكلمنا عنه فيما يخص ملابس المقاتلين لن يصبح ممكناً.
النهاية
أرجو ألا أكون قد نسيت شيئاً، وأرجو ألا أكون قد أزعجت أحداً، وأتمنى أن يكون الدرس مفيداً.
تحياتي، سلام...
لمزيد من المعلومات
المصطلحات المستخدمة
المصطلح |
معناه |
Alpha Channel |
قناة لونية إضافية في الصورة بجانب قنوات الأحمر والأخضر والأزرق الأساسية. تفسير القيم في هذه القناة متروك للبرنامج الذي يتعامل مع الصورة، إلا أن الاستخدام الأكثر شيوعاً لهذه القناة هو لتحديد مقدار شفافية اللون على الخلفية، فقيمة ألفا 0 تعني اللون شفاف تماماً، و 255 تعني أنه مصمت تماماً. |
Indexed Color |
لون مُرقّم. ببساطة قيمة لونية معرفة في لوح لوني. مثلاً، اللون رقم 10 يعني اللون المحدد في الخانة العاشرة في اللوح اللوني. |
Palette |
لوح لوني. سلسلة مرقمة من الألوان المستخدمة في بناء صورة. كل بكسل من الصورة يقابل أحد الألوان في اللوح اللوني. يتراوح عدد الألوان في اللوح بين الاثنين إلى الـ 256 عادةً. |