هكذا قمنا بعمل دالة تقوم بفحص ما إذا كان المؤشر قد دخل إلى حيز النافذة أم خرج. تستقبل هذه الدالة بارامتر واحد من نوع عدد صحيح. فإذا كان هذا المتغير يساوي الثابت GLUT_LEFTفإن المؤشر قد غادر النافذة. و إذا كان المتغير يساوي الثابت GLUT_ENTEREDفإن المتغير قد دخل إلى النافذة. و في الدالة الرئيسية mainسنخبر مكتبة جلوت بأن تقوم بإستدعاء تلك الدالة إذا ما غادر المؤشر النافذة أو دخل إليها مع تمرير لها متغير من نوع عدد صحيح يعبر عن حالة المؤشر. و إنظر إلى هذا الكود:
و الآن لندخل إلى الكلام عن دوال أكثر تعقيداً. في الدالة السابقة كنا نستطيع أن نعرف هل المؤشر قد دخل في حيز النافذة أم خرج. و هذه المرة سوف نعرف هل المؤشر قد تحرك و إلى أين تحرك و هل تحرك مع ضغط زر أم تحرك بدون أن يكون هناك زر مضغوط. إنظر إلى هذه الدالة التي أنشأناها:
و سوف نقوم بإخبار مكتبة جلوت بأن تقوم بإستدعاء هذه الدالة إذا تحرك المؤشر بدون أن يضغط على أي زر. إنظر الكود:
و كما ترى قد وضعت لك المعادلة التي ذكرتها التي تأخذ إحداثيات بالبكسل و ترجع لك الحداثيات النسبية. فهذه الدالة تقوم بإستقبال بارامتران يعبران عن مكان المؤشر بالبكسل. و نحن قد أخذناهم و حولناهم إلى إحداثيات نسبية. ربما مثلًا نجعل المتغير Mxو Myمتغيرات عامة و نقوم في دالة الرسم برسم شكل يشبه للمؤشر. أو ربما نضع صورة (كما سنرى لحقاً في درس الإكساء) حتى يظهر و كأنه مؤشر. يمكنك أن تقوم بوضع تلك الدالة في برنامج و ترسم مثلث حول النقطة التي بها المؤشر لكي يظهر و كأنه مؤشر بالفعل. و ربما تعمل فيه بعض تدريجات اللوان. و سأترك للقارئ الحرية في عمل تلك الفكار حتى يبدع هو بنفسه. أما الدالة الثانية و التي تستدعى حين يضغط المستخدم على أي زر مع تحريك المؤشر فهي لها نفس البارامترات. يمكنك إنشاء دالتان لهذا الأمر. إنظر إلى هذا الكود:
حيث نضع أوامر التعامل مع مؤشر الفأرة إذا كان يتحرك مع ضغط زر في الدالة الأولى و نضع أوامر التعامل معه إذا كان يتحرك مع عدم ضغط أي شئ في الدالة الثانية. و من ثم يجب أن نخبر مكتبة جلوت متى ستستدعي هذه و متى ستستدعي تلك:
و إذا لم يكن هناك فرق في الكود بين التعامل مع الفأرة في كلتا الحالتين يمكن أن قوم عمل دالة واحدة فقط تستقبل مكان المؤشر في بارامتراتها. و تخبر مكتبة جلوت بأن تقوم بإستدعائها في الحالتين
و لندخل إلى الكلام عن دالة آخرى آخرى من دوال إستقبال مدخلات المؤشر. و هي دالة تستدعى عندما يضغط المستخدم على زر من الأزرار و تستدعى مرة آخرى عندما يرفع إصبعه من على الزر. و يتم التمرير للدالة أربع بارامترات كلها أعداد صحيحة. الأول يعبر عن أي الأزرار تم ضغطه (الزر الأيمن أم الأيسر أو الأوسط). و الثاني يقول لك هل المستخدم قد ضغط أم رفع إصبعه. و أما الثالث و الرابع لمكان المؤشر في محور السينات و الصادات Xو Yطبعاً بإحداثيات البكسل و سنرى الآن كيف نحولها إلى إحداثيات نسبية قبل أن ندخل إلى شرح الدالة. ينبغي أن تعلم أن هناك ثلاث فروق بين نظامي الإحداثيات. الأول هو في نقطة الأصل (0,0) ففي نظام الإحداثيات النسبية تعتبر نقطة الأصل في تمام منتصف النافذة أما في نظام البكسلات فإن نقطة الأصل في أعلى يسار الشاشة لذلك يجب علينا أن نحرك نقطة الأصل إلى المنتصف. كما أن في نظام البكسلات يزيد المحور Yكلما نزلنا إلى الأسفل أما في الإحداثيات النسبية فإن ال Yيزيد كلما صعدنا لذلك يجب علينا أن نقلب قيمة ال .Yأما الفرق الثالث فهو أن في الحداثيات النسبية تحمل قيمة (1,-1) عند أسفل يسار النافذة أما في البكسلت فإنه يحمل قيمة الطول و العرض. و بإختصار يمكننا أن نضع كل هذا الكلام في معادلة للتحويل بين الإحداثيات النسبية و الإحداثيات بالبكسل. حيث سنعتبر أن قيمة Wهي عرض النافذة و Hهو طولها (و قد شرحنا كيف نعرف قيمة الطول و العرض للنافذة). و ال Xو ال Yيعبران عن مكان المؤشر في إحداثيات البكسل. و سوف نقوم بعمل معادلة لكي تقوم بمعرفة مكان المؤشر بالإحداثيات النسبية (مثلً إذا كنت تريد أن ترسم شئ يمشي مع المؤشر أو تريد أن تضع صورة مؤشر آخرى و تلغي السهم التقليدي).
و بعد أن تتأمل المعادلتان ستجد الآتي. بالنسبة للمعادلة الأولى التي ترجع لنا قيمة مكان المؤشر في محور السينات في نظام الإحداثيات النسبية عندنا المتغير Xيعبر عن مكان المؤشر في محور السينات بالبكسل و المتغير Wيعبر عن عرض النافذة أيضاً بالبكسل. و قد إفترضنا أن عرض النافذة في الإحداثيات النسبية هو 2.0 بإفتراض أن المحور عند يسار النافذة و ليس في الوسط لأننا بعد ذلك قمنا بإرجاع المحور إلى الوسط فطرحنا القيمة 1 من الناتج. فبعد أن إفترضنا أن عرض النافذة هو 2 قمنا بتطبيق قانون حاصل ضرب الطرفين يساوي حاصل ضرب الوسطين حيث إعتبرنا أن Wمقسومة على Xتساوي 2 مقسومة على Mxأي النقطة التي نريدها بإعتبار أن النقطة الصفر في اليسار. ثم بعد ذلك طرحنا قيمة 1 حتى نرجع قيمة الصفر البكسل تزيد كلما نزلنا على عكس الإحداثيات النسبية فإنها تقل لذلك قمنا أولً في المعادلة الثانية بقلب قيمتها عن طريق طرح الطول من مكان النقطة.
و الآن لنتكلم عن الدالة التي ذكرناها و التي تقوم بإستقبال الزر و حالة الضغط و المكان. إنظر إلى الكود التالي:
ثم لكي تجعل مكتبة جلوت تقوم بإستدعاء هذه الدالة التي أسميناها MouseClickإذا ضغط المستخدم على أي زر نقول:
طبعاً كما هو ملاحظ فإن قيمة المتغير الأول و الذي أسميناه Buttonتعبر عن الزر الذي تم ضغطه. و قيمته تأخذ إحدى الثلاث ثوابت الآتية:
أما المتغير الثاني الذي أسميناه Stateو الذي يعبر عن حالة ضغط الزر يأخذ قيم إحدى الثوابت الآتية:
و لكي نضع النقاط على الحروف علينا أن نستعمل كل ما سبق في مثال للتوضيح. سيقوم هذا البرنامج بعرض مجموعة من الأزرار على الشاشة. و لكل زر لون معين و إذا ضغطت على أي زر فإن لون الخلفية يتحول إلى لون ذلك الزر. كما أن الزر إذا ضغطت عليه يغمق لونه قليلً. ترى هل يمكن أن نفعل كل هذا بإستخدام ما تعلمناه عن التعامل مع المؤشر ؟؟ سنرى هذا في المثال القادم. إنظر إلى الكود:
#include <GL/glut.h>
#include <GL/gl.h>
int Slct=1,Dn=0,H=600,W=800;
void DisplayTimer(int V)
{
glutTimerFunc(20,DisplayTimer,0);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
if (Slct == 1)
glColor3f(0,0.5,0);
else if (Dn == 1)
glColor3f(0,0.25,0);
else
glColor3f(1,1,1);
glVertex2f(-0.8,0.8);
glVertex2f(-0.2,0.8);
if (Slct == 1)
glColor3f(1,1,1);
else if (Dn == 1)
glColor3f(0.5,0.5,0.5);
else
glColor3f(0,0.5,0);
glVertex2f(-0.2,0.2);
glVertex2f(-0.8,0.2);
///////////////////////////////////////////////
if (Slct == 2)
glColor3f(1,0.5,0);
else if (Dn == 2)
glColor3f(0.5,0.25,0);
else
glColor3f(1,1,1);
glVertex2f(0.8,0.8);
glVertex2f(0.2,0.8);
if (Slct == 2)
glColor3f(1,1,1);
else if (Dn == 2)
glColor3f(0.5,0.5,0.5);
else
glColor3f(1,0.5,0);
glVertex2f(0.2,0.2);
glVertex2f(0.8,0.2);
///////////////////////////////////////////////
if (Slct == 3)
glColor3f(0.8,0,1);
else if (Dn == 3)
glColor3f(0.4,0,0.5);
else
glColor3f(1,1,1);
glVertex2f(0.8,-0.2);
glVertex2f(0.2,-0.2);
if (Slct == 3)
glColor3f(1,1,1);
else if (Dn == 3)
glColor3f(0.5,0.5,0.5);
else
glColor3f(0.8,0,1);
glVertex2f(0.2,-0.8);
glVertex2f(0.8,-0.8);
///////////////////////////////////////////////
if (Slct == 4)
glColor3f(0,0.8,1);
else if (Dn == 4)
glColor3f(0,0.4,0.5);
else
glColor3f(1,1,1);
glVertex2f(-0.8,-0.2);
glVertex2f(-0.2,-0.2);
if (Slct == 4)
glColor3f(1,1,1);
else if (Dn == 4)
glColor3f(0.5,0.5,0.5);
else
glColor3f(0,0.8,1);
glVertex2f(-0.2,-0.8);
glVertex2f(-0.8,-0.8);
glEnd();
glFlush();
glutSwapBuffers();
}
void Mouse(int Btn, int Sts, int x, int y)
{
float Mx = (float)x/W*2-1;
float My = (H-y)/(float)H*2-1;
if (Btn == GLUT_LEFT_BUTTON)
{
if (Mx > -0.8 && Mx < -0.2 && My > 0.2 && My < 0.8)
{
if (Sts == GLUT_UP)
{
glClearColor(0,0.5,0,1);
Dn = 0;
Slct = 1;
}
else if (Sts == GLUT_DOWN)
Dn = 1;
}
else if (Mx < 0.8 && Mx > 0.2 && My > 0.2 && My < 0.8)
{
if (Sts == GLUT_UP)
{
glClearColor(1,0.5,0,1);
Dn = 0;
Slct = 2;
}
else if (Sts == GLUT_DOWN)
Dn = 2;
}
else if (Mx < 0.8 && Mx > 0.2 && My < -0.2 && My > -0.8)
{
if (Sts == GLUT_UP)
{
glClearColor(0.8,0,1,1);
Dn = 0;
Slct = 3;
}
else if (Sts == GLUT_DOWN)
Dn = 3;
}
else if (Mx > -0.8 && Mx < -0.2 && My < -0.2 && My > -0.8)
{
if (Sts == GLUT_UP)
{
glClearColor(0,0.8,1,1);
Dn = 0;
Slct = 4;
}
else if (Sts == GLUT_DOWN)
Dn = 4;
}
}
}
void Resize(int Width, int Height)
{
W = Width;
H = Height;
glViewport(0,0,W,H);
}
void Disp() {}
int main(int argc, char** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(800,600);
glutCreateWindow(“Mouse Move Test”);
glClearColor(0,0.5,0,1);
glutMouseFunc(Mouse);
glutTimerFunc(20,DisplayTimer,0);
glutDisplayFunc(Disp);
glutReshapeFunc(Resize);
glutMainLoop();
return 0;
}
لا تقلق بشأن طول البرنامج , فسطوره كثيرة في الكم فقط لا في الكيف و كل شئ كتبناه في الكود ذكرناه و شرحناه سابقاً. إن هذا البرنامج يقوم بعرض أربع أزرار مختلفة في الألوان. و إذا تم الضغط على أي زر فإن لون الخلفية يتحول إلى لون ذلك الزر و الزر نفسه يتغير شكله ليظهر و كأنه مضغوط. و عندما نضغط بالفأرة على أي زر فإن لونه يغمق قليلًا حتى نرفع إصبعنا من على الزر اليساري للفأرة. و قد إخترنا أربع ألوان للأربع أزرار و هي اللون 0,0.5,0 للزر الأول و الذي في أعلى يسار النافذة و اللون 0,5.0,1 للزر الثاني الذي في أعلى يمين النافذة و اللون 0.8,0,1 للزر الثالث الذي في أسفل يمين النافذة و اللون 0,0.8,1 للزر الرابع الذي في أسفل يسار النافذة. و نبدأ بشرح البرنامج. في الدالة التي يتم إستدعائها كل 20 ميلي ثانية لكي تقوم بعملية الرسم قمنا برسم الأربع أزرار. و تأمل كيف يرسم الزر الواحد. إنظر إلى هذه القطعة من الكود:
if (Slct == 1)
glColor3f(0,0.5,0);
else if (Dn == 1)
glColor3f(0,0.25,0);
else
glColor3f(1,1,1);
glVertex2f(-0.8,0.8);
glVertex2f(-0.2,0.8);
if (Slct == 1)
glColor3f(1,1,1);
else if (Dn == 1)
glColor3f(0.5,0.5,0.5);
else
glColor3f(0,0.5,0);
glVertex2f(-0.2,0.2);
glVertex2f(-0.8,0.2);
إن الزر الواحد يحمل لونين و هما اللون الأبيض و يتدرج بعد ذلك للون الذي يحمله الزر و الذي إذا ضغطت عليه سوف يكسي الخلفية بهذا اللون. فإذا كان الزر مضغوط فإن اللون الأبيض يكون تحت و لون الزر يكون فوق. و إن لم يكن مضغوط و لكن المستخدم مازال يضغط عليه (أي يشير إليه بالمؤشر و لكنه لم يرفع إصبعه من على الزر بعد) فإن الزر سيكون بلون أغمق قليلًا. و قد قمنا بعمل متغيرين عامين في بداية البرنامج و هما Dnو .Slctفأما Slctفيحمل رقم الزر الذي قد ضغط عليه المستخدم. أي رقم الزر المحدد الذي تحمل خلفية النافذة لونه. و أما Dnفيحمل رقم الزر الذي يضغط عليه المستخدم و لم يرفع إصبعه بعد. نحن في البداية نختبر ما إذا كان الزر مضغوط أم لا (لاحظ إختبار قيمة Stctفي كل مرة نرسم فيها زر). فإذا كان كذلك قمنا بإختيار لون الزر. و إذا لم يكن مضغوطاً و لكن المستخدم مازال يضغط عليه اللون الأصلي للزر و لكن أغمق قليلًا (اللون الأصلي للزر الأول الذي نشرح كوده الآن هو 0,0,0.5 كما ذكرنا و لكن كما ترى فإننا قد حددنا لون آخر و هو 0,0.25,0 أي أنها نفس النسب و لكن درجة اللون أقل حتى يكون أغمق). و إن كان لا هذا و لا هذا نحدد اللون الأبيض ثم نرسم أول نقطتين (الذان في أعلى الزر). ثم نقوم برسم النقطتين الأخرتين في أسفل الزر. فنختبر قيمة المتغير Slct فإذا كان الزر مضغوط نحدد اللون الأبيض. و إن لم يكن مضغوطاً فإننا سوف نختبر قيمة المتغير Dnلكي نعرف ما إذا كان المستخدم مازال يضغط على الزر أم لم يضغط عليه فإذا كان كذلك نقوم بتحديد اللون الرصاصي (يمكن أن تعتبره الأبيض الغامق حيث يستعمل نفس نسب اللون الأبيض و لكن أقل في الدرجة). و إن كان الزر ليس مضغوطاً ولا محدداً فإننا نحدد اللون الأصلي للزر. و هكذا فعلنا مع كل الأزرار بعد ذلك. و يتبقى عندنا شئ واحد ألا و هو كيف نعرف ما إذا كان الزر قد تم ضغطه أم لا و هل المستخدم مازال يضغط أم لا ؟ و هذا هو المغزى من المثال. فنحن قد عرفنا أن هناك دالة تستقبل أربع متغيرات من العداد الصحيحة تحمل معلومات عن الزر الذي تم ضغطه و هل المستخدم قد رفع إصبعه أم لا و أين هو المؤشر بإحداثيات البكسل. و نحن قمنا بإنشاء هذه الدالة و أسميناها Mouseثم أخبرنا مكتبة جلوت بأن تقوم بإستدعائها و ذلك عن طريق هذا السطر:
و لنتأمل دالة Mouseالتي جعلناها دالة إستقبال مدخلات مؤشر الفأرة. فقد قلنا بأن البارامتر الأول يعبر عن الزر الذي تم ضغطه و نحن نقوم بإختبار قيمته عن طريق الثوابت التي ذكرناها سابقاً. في البداية قمنا بإيجاد مكان المؤشر بالإحداثيات النسبية عن طريق القانون الذي ذكرناه سابقاً و قمنا بحفظ المكان في متغيرين:
float Mx = (float)x/W*2-1;
float My = (H-y)/(float)H*2-1;
و أما المتغير Hو Wفهما متغيرات عامة يحملان قيمة طول و عرض النافذة. و قد قمنا بعمل دالة تقوم بأخذ القيمة الجديدة و وضعها في هذين المتغيرين. و هي دالة ) Resizeراجع الكلام عن الحداثيات). بعد ذلك قمنا بإختبار قيمة المتغير Btn (الذي كان أول بارامتر في الدالة) لكي نعرف ما هو الزر الذي تم ضغطه. فإذا كان الزر الذي تم ضغطه هو الزر الذي على اليسار نقوم بتنفيذ بقية أوامر الدالة. بعد ذلك نريد أن نعرف المكان الذي تم الضغط فيه على النافذة. أي أننا نريد أن نعرف أي زر تم الضغط عليه أم تم الضغط على الخلفية. قمنا بإختبار مكان Mxو Myهل هما في حيز الزر الأول أم لا. و إذا كان لا فهل هما في حيز الزر الثاني أم لا و هكذا. إنظر إلى هذه القطعة من الكود:
if (Mx > -0.8 && Mx < -0.2 && My > 0.2 && My < 0.8)
{
if (Sts == GLUT_UP)
{
glClearColor(0,0.5,0,1);
Dn = 0;
Slct = 1;
}
else if (Sts == GLUT_DOWN)
Dn = 1;
}
إذا راجعت كود رسم الزر ستجد أننا قد رسمناه في هذا الحيز الذي ذكرناه عندما أردنا إختبار مكان ضغط المؤشر. أي بين القيمة -0.8 و -0.2 في محور السينات و بين 0.2 و 0.8 في محور الصادات. و كما يبدو في الكود أنه إذا كان المؤشر عندما تم الضغط عليه في حيز الزر الأول فإننا نختبر قيمة المتغير ) Stsالبارامتر الثاني الذي يعبر عن حالة الضغط على الزر). فإذا كان المستخدم قد رفع إصبعه من على الزر فإننا نقوم بتغير لون الخلفية لتكون بلون الزر كما ترى عندما إستدعينا دالة تغيير لون الخلفية glClearColorو مررنا لها لون الزر (و البارامتر الرابع في هذه الدالة هو قناة Alphaالتي تعبر عن الشفافية دائماً ما نعطيها قيمة 1). و بما أن المستخدم قد رفع إصبعه من على الزر فإننا نجعل قيمة المتغير Dnتساوي صفر لأن المستخدم لم يعد يضغط على أي زر. ثم أعطينا قيمة المتغير Slctالقيمة 1 التي تدل على أن الزر المحدد هو الزر الأول و لذلك عندما تأتي دالة الرسم لترسم الزر فإنها سترسم اللون البيض في الأسفل و اللون الأصلي للزر في الأعلى. بعد ذلك كانت العبارة elseتأتي إذا لم يكن المستخدم قد رفع إصبعه من على الزر أي أنه مازال يضغط عليه. و حينها سنقوم بإعطاء المتغير Dnقيمة الزر و لذلك عندما تاتي دالة الرسم لترسم الزر فإنها ستجد أن قيمة المتغير Dnتشير إلى الزر الأول لذلك سترسمه بألوان غامقة. و هكذا نكون قد إنتهينا من شرح هذا المثال. و بعد تشغيل البرنامج ستجد أنه بهذا الشكل:

و كما نلاحظ هنا فإن المستخدم قد ضغط على الزر (الثالث) الذي في أسفل يمين الشاشة و أنظر كيف يبدو الزر بلون غامق لأن المستخدم يضغط عليه. و بعد أن رفع المستخدم إصبعه من على الزر إنظر كيف تغير لون الخلفية

و هنا لاحظ كيف تم تغيير لون الخلفية بعد أن ضغطنا على الزر. و لاحظ أن الزر الثالث الذي في أسفل يمين النافذة قد بدا مضغوطاً حيث اللون الأبيض عند أسفله و لونه الأصلي في الأعلى. و لحظ كيف أن الزر الذي بجانبه لم يعد مضغوطاً. و هكذا نكون قد إنتهينا من الكلام عن الدوال التي ترصد تحركات المؤشر و الآن لندخل في الكلم عن دالتان تقومان بالتأثير على المؤشر. أما الدالة الأولى فهي تقوم بتغيير شكل المؤشر. نستدعي هذه الدالة و نقوم بتمرير لها عدد صحيح في شكل إحدى الثوابت المعروفة لكي نقوم بتغيير شكل المؤشر. إنظر إلى هذا السطر:
glutSetCursor(GLUT_CURSOR_HELP);
مثلًا هذا السطر سيقوم بجعل شكل المؤشر عبارة عن علامة إستفهام و ليس السهم المعتاد. و هناك العديد من الثوابت التي نقوم بتمريرها إلى هذه الدالة لتغيير شكل المؤشر. و لدينا العديد من الأشكال للمؤشر مثل علامة إكس تشير إلى خطأ مثلًا و كذلك السهم و علامة الإستفهام و الساعة الرملية التي تدل على أن هناك عملية يقوم بها البرنامج لذلك عليك الإنتظار و غير ذلك من الأشكال. و كل هذا لا يهمنا في برمجة الألعاب. إنما يهمنا فقط شكل واحد من أشكال المؤشر. إنظر إلى هذا السطر:
glutSetCursor(GLUT_CURSOR_NONE);
و هكذا سوف يختفي السهم فإذا مر المؤشر فوق النافذة لن تجد شيئاً. و هذا ما نريده في برمجة اللعاب. فمثلً عندما نكون في لعبة من نوع First Person Shooterفإننا لا نريد أن يكون هناك شكل للمؤشر. إذ أن كل شئ يتم بلوحة المفاتيح و دور المؤشر هو في تغيير الزاوية فقط. أما في أي لعبة إستراتيجية نحتاج إلى وجود مؤشر. مثل أن يكون المؤشر على شكل يد إذا مر فوق سلاح مثلًا أو يكون على شكل سكين إذا مر المؤشر فوق جندي من جنود العدو و هكذا. و طبعاً هذه الأشكال للمؤشر لن نجدها في الدالة التي ذكرناها بل سنجدها فقط في حل واحد ألا و هو أن نقوم بإخفاء المؤشر عن طريق هذه الدالة ثم نقوم بعد ذلك برسم الصورة التي نريد مكان المؤشر عن طريق أخذ مكانه بإحداثيات البكسل ثم تحويلها إلى الحداثيات النسبية كما تعلمنا ثم نرسم ما نريد في هذا المكان.
و الآن لنأتي إلى الدالة الأخيرة. و هذه الدالة مهمة جداً في الألعاب الثلاثية الأبعاد و سوف نشرح بعد ذلك ما هي أهميتها. و لكن كل ما عليك أن تعلمه عنها أننا نمرر لها قيمتين من نوع عدد صحيح فتقوم بنقل المؤشر إلى هذا المكان الذي مررناه لها (بإحداثيات البكسل). إنظر إلى هذا الكود:
glutWarpPointer(100,100);
حيث مررنا لها مكان المؤشر الذي نريد. فإذا تم إستدعاء هذه الدالة فإن المؤشر سينتقل مباشرةً إلى هذا المكان. فتخيل مثلًا لو تم إستدعاء هذه الدالة داخل دالة التوقيت ماذا سيحدث ؟؟ سوف ينحبس المؤشر في تلك النقطة و لن يخرج.
إلى هنا نكون قد إنتهينا من شرح ما يتعلق بمؤشر الفأرة فيما يخصنا في برمجة الألعاب. و نختم هذا الفصل بمثال بسيط نطبق فيه بعض الدوال السابقة. سنقوم بعمل برنامج يجعل المؤشر ينحبس في مربع صغير لا يستطيع أن يخرج منه. كما سنقوم في هذا المثال بإلغاء شكل مؤشر الفأرة و سنقوم برسم مثلث مكانه بالطريقة التي سبق أن وضحناها. و ها هو الكود:
#include <GL/glut.h>
#include <GL/gl.h>
float Mx,My;
int H,W;
GLuint Win;
void DisplayTimer(int V)
{
glutTimerFunc(20,DisplayTimer,0);
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_QUADS);
glColor3f(1,0,0.9);
glVertex2f(0.5,0.5);
glVertex2f(0.5,-0.5);
glVertex2f(-0.5,-0.5);
glVertex2f(-0.5,0.5);
glEnd();
glBegin(GL_TRIANGLES);
glColor3f(1,1,1);
glVertex2f(Mx,My);
glColor3f(0,0.6,1);
glVertex2f(Mx+0.1,My);
glVertex2f(Mx,My-0.1);
glEnd();
glFlush();
glutSwapBuffers();
}
void Mouse(int x, int y)
{
Mx = (float)x/W*2-1;
My = (H-y)/(float)H*2- 1;
if (Mx > 0.5 || Mx < -0.5 || My > 0.5 || My < -0.5)
glutWarpPointer(W/2,H/2);
}
void Resize(int Width, int Height)
{
W = Width;
H = Height;
glViewport(0,0,W,H);
}
void Disp() {}
void Keyboard(unsigned char K, int x, int y)
{
if (K == 27)
glutDestroyWindow(Win);
}
int main(int argc, char** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
glutInitWindowSize(800,600);
Win = glutCreateWindow(“Mouse Move Test”);
glClearColor(0,0.5,0,1);
glutPassiveMotionFunc(Mouse);
glutMotionFunc(Mouse);
glutTimerFunc(20,DisplayTimer,0);
glutSetCursor(GLUT_CURSOR_NONE);
glutDisplayFunc(Disp);
glutKeyboardFunc(Keyboard);
glutReshapeFunc(Resize);
glutMainLoop();
return 0;
}
ببساطة قمنا بعمل متغيرين عامين من نوع كسر floatليعبروا عن مكان المؤشر بالإحداثيات النسبية. ثم في دالة الرسم (أو دالة التوقيت) قمنا برسم مربع في الوسط ثم قمنا برسم مثلث عند النقطة Mxو Myحتى يبدوا هذا المثلث و كأنه المؤشر. و قمنا في الدالة الرئيسية mainبإلغاء شكل المؤشر عن بتمرير GLUT_CURSOR_NONE إلى الدالة .glutSetCursorثم قمنا بعمل دالة تقوم مكتبة جلوت بإستدعائها عندما يتحرك المؤشر:
glutPassiveMotionFunc(Mouse);
glutMotionFunc(Mouse);
ثم قمنا بعد ذلك بأخذ مكان المؤشر في هذه الدالة بإحداثيات البكسل ثم نحولها إلى إحداثيات نسبية و نضعها في المتغيرين العامين Mxو .Myو بعد ذلك نقوم بإختبار قيمتهما فإذا تجاوزت حدود المربع فإننا إذاً نقوم بإستدعاء دالة تغيير مكان المؤشر لكي نعيده إلى منتصف النافذة إنظر الكود:
glutWarpPointer(W/2,H/2);
|حيث قد مررنا لتلك الدالة قيمتين بإحداثيات البكسل. و هما منتصف النافذة بالضبط لننا قد قسمنا الطول و العرض على إثنين حتى نحصل على إحداثيات منتصف النافذة. طبعاً هناك بعض الشياء الآخرى في البرنامج قد تعرضنا لها من قبل مثل عمل دالة Keyboardو جعلها تغلق البرنامج إذا ضغط المستخدم .Escapeإنظر إلى صورة البرنامج بعد تشغيله:

إلى هنا نكون قد إنتهينا من الكلام عن ما يتعلق بنا في التعامل مع مؤشر الفأرة. و كان من أجل أن نفهم كيفية التعامل مع المؤشر كان يجب أن نتكلم عن الإحداثيات. و تذكر أنه كان هناك ستة دوال للتعامل مع المؤشر فضلً عن وجود بارامترات لمكان المؤشر في دوال التعامل مع لوحة المفاتيح. كان عندنا دالة glutEntryFuncالتي نمرر لها دالة لها بارامتر واحد من نوع عدد صحيح. و تقوم مكتبة جلوت بإستدعاء تلك الدالة إذا ما خرج المؤشر من أو دخل إلى النافذة. ثم كان عندنا دالة إسمها glutMotionFuncو الدالة glutPassiveMotionFuncحيث نمرر لهما دالة لها بارامتران من نوع عدد صحيح يعبران عن مكان المؤشر بإحداثيات البكسل. و الدالة الأولى تقوم بإستدعاء الدالة التي مررتها لها إذا ما تحرك المؤشر مع ضغط على أحد الأزرار و الثانية إذا ما تحرك المؤشر بدون ضغط على أزرار. و كان عندنا أيضاً دالة أكثر تعقيداً من هاتان الدالتان و إسمها glutMouseFuncنقوم بتمرير لها دالة لها أربع بارامترات من نوع عدد صحيح. فأما الأول فهو يعبر عن الزر الذي تم ضغطه. و الثاني يعبر عن حالة الضغط هل المستخدم مازال يضغط أم رفع إصبعه من على الزر. و أما البارامتر الثالث و الرابع فهما لمكان المؤشر. و كان عندنا دالتان للتحكم في المؤشر. كان عندنا دالة نقوم بتمرير لها رقمان من الأعداد الصحيحة على أنهم إحداثيات بالبكسل فتقوم بنقل المؤشر إلى هذا المكان و إسم الدالة .glutWarpPointer و أما الدالة الثانية فهي glutSetCursorنقوم بتمرير لها ثابت من الثوابت يعبر عن شكل المؤشر الذي نريد. و قلنا أننا لن نحتاج في برمجة اللعاب إلى هذه الدالة إلا عندما نريد أن نلغي شكل المؤشر لنضع مكانه ما نريد من أشكال بأنفسنا.
و نستطيع القول أننا قد إنتهينا تقريباً من الكلام عن مكتبة جلوت GLUTفي هذا الفصل في ما يخصنا في برمجة الألعاب. فلم يكن هدفنا هو أن نشرح تلك المكتبة و لكن أخذنا فقط ما نحتاج إليه في برمجة اللعاب. و يمكنك أن تذهب إلى المرجع الرئيسي لتلك المكتبة حيث ستجد العديد من الدوال و الثوابت التي تحتاجها في عمل أي برنامج بمكتبة جلوت. و في الفصل القادم إن شاء الله سنتحدث بتفصيل أكثر عن مكتبة الرسوم المفتوحة OpenGLو بنظرة عن قرب أكثر إلا أننا
سنتوقف بعد هذا الفصل لعمل تطبيق على كل ما سبق في لعبة بسيطة من باب إعطاء بعض الأفكار لا أكثر.
المفضلات