الدرس الرابع في برمجة الألعاب بإستخدام OpenGL
int W,H;
void Resize(int Width, int Height)
{
W = Width;
H = Height;
}
هنا قمنا بإنشاء دالة تستقبل عددان صحيحان يعبران عن طول و عرض النافذة و وضعناهم في متغيرات عمومية لنستخدمهم في معادلة التحويل بين نظام الإحداثيات بالبكسل و نظام الإحداثيات النسبية. ثم نقوم في الدالة الرئيسية mainبالآتي:
glutReshapeFunc(Resize);
حيث ستقوم مكتبة جلوت بإستدعاء تلك الدالة و تمرير لها الحجم الجديد للنافذة ثم في الدالة التي أنشأناها , نضع البارامترات في متغيرات عمومية و هكذا يصبح عندنا متغيرات تحمل طول و عرض النافذة و من ثم نستطيع أن نحول بين نظامي الإحداثيات.
و الآن جرب أن تقوم بعمل هذا الأمر. و الآن ضع هذه الدالة و ذاك السطر في أي مثال من الأمثلة السابقة و إنظر ماذا سيحدث (لم أطلب منك إستعمال المتغيرات في رسم شئ بعد , فقط ضع الدالة كما هي). و هذا ما سوف تفاجئ به:
glViewport(0,0,W,H);
glViewport(W/2,H/2,W,H);
void MouseEnter(int SS){if (SS == GLUT_LEFT)/* إفعل ما تريد فعله إذا غادر المؤشر النافذة */else if (SS == GLUT_ENTERED)/* إفعل ما تريد فعله إذا دخل المؤشر إلى حيز النافذة */}
glutEntryFunc(MouseEnter);
void MouseMove(int x, int y)
{
float Mx = (float)x/W*2-1;
float My = (H-y)/(float)H*2-1;
/* كود */
}
و سوف نقوم بإخبار مكتبة جلوت بأن تقوم بإستدعاء هذه الدالة إذا تحرك المؤشر بدون أن يضغط على أي زر. إنظر الكود:
glutPassiveMotionFunc(MouseMove);
و كما ترى قد وضعت لك المعادلة التي ذكرتها التي تأخذ إحداثيات بالبكسل و ترجع لك الحداثيات النسبية. فهذه الدالة تقوم بإستقبال بارامتران يعبران عن مكان المؤشر بالبكسل. و نحن قد أخذناهم و حولناهم إلى إحداثيات نسبية. ربما مثلًا نجعل المتغير Mxو Myمتغيرات عامة و نقوم في دالة الرسم برسم شكل يشبه للمؤشر. أو ربما نضع صورة (كما سنرى لحقاً في درس الإكساء) حتى يظهر و كأنه مؤشر. يمكنك أن تقوم بوضع تلك الدالة في برنامج و ترسم مثلث حول النقطة التي بها المؤشر لكي يظهر و كأنه مؤشر بالفعل. و ربما تعمل فيه بعض تدريجات اللوان. و سأترك للقارئ الحرية في عمل تلك الفكار حتى يبدع هو بنفسه. أما الدالة الثانية و التي تستدعى حين يضغط المستخدم على أي زر مع تحريك المؤشر فهي لها نفس البارامترات. يمكنك إنشاء دالتان لهذا الأمر. إنظر إلى هذا الكود:
void MouseMove1(int x, int y)
{
Mx = (float)x/W*2-1;
My = (H-y)/(float)H*2- 1;
/* كود */
}
void MouseMove2(int x, int y)
{
Mx = (float)x/W*2-1;
My = (H-y)/(float)H*2- 1;
/* كود */
}
حيث نضع أوامر التعامل مع مؤشر الفأرة إذا كان يتحرك مع ضغط زر في الدالة الأولى و نضع أوامر التعامل معه إذا كان يتحرك مع عدم ضغط أي شئ في الدالة الثانية. و من ثم يجب أن نخبر مكتبة جلوت متى ستستدعي هذه و متى ستستدعي تلك:
glutMotionFunc(MouseMove1);
glutPassiveMotionFunc(MouseMove2);
و إذا لم يكن هناك فرق في الكود بين التعامل مع الفأرة في كلتا الحالتين يمكن أن قوم عمل دالة واحدة فقط تستقبل مكان المؤشر في بارامتراتها. و تخبر مكتبة جلوت بأن تقوم بإستدعائها في الحالتين
glutMotionFunc(MouseMove);
glutPassiveMotionFunc(MouseMove);
و لندخل إلى الكلام عن دالة آخرى آخرى من دوال إستقبال مدخلات المؤشر. و هي دالة تستدعى عندما يضغط المستخدم على زر من الأزرار و تستدعى مرة آخرى عندما يرفع إصبعه من على الزر. و يتم التمرير للدالة أربع بارامترات كلها أعداد صحيحة. الأول يعبر عن أي الأزرار تم ضغطه (الزر الأيمن أم الأيسر أو الأوسط). و الثاني يقول لك هل المستخدم قد ضغط أم رفع إصبعه. و أما الثالث و الرابع لمكان المؤشر في محور السينات و الصادات Xو Yطبعاً بإحداثيات البكسل و سنرى الآن كيف نحولها إلى إحداثيات نسبية قبل أن ندخل إلى شرح الدالة. ينبغي أن تعلم أن هناك ثلاث فروق بين نظامي الإحداثيات. الأول هو في نقطة الأصل (0,0) ففي نظام الإحداثيات النسبية تعتبر نقطة الأصل في تمام منتصف النافذة أما في نظام البكسلات فإن نقطة الأصل في أعلى يسار الشاشة لذلك يجب علينا أن نحرك نقطة الأصل إلى المنتصف. كما أن في نظام البكسلات يزيد المحور Yكلما نزلنا إلى الأسفل أما في الإحداثيات النسبية فإن ال Yيزيد كلما صعدنا لذلك يجب علينا أن نقلب قيمة ال .Yأما الفرق الثالث فهو أن في الحداثيات النسبية تحمل قيمة (1,-1) عند أسفل يسار النافذة أما في البكسلت فإنه يحمل قيمة الطول و العرض. و بإختصار يمكننا أن نضع كل هذا الكلام في معادلة للتحويل بين الإحداثيات النسبية و الإحداثيات بالبكسل. حيث سنعتبر أن قيمة Wهي عرض النافذة و Hهو طولها (و قد شرحنا كيف نعرف قيمة الطول و العرض للنافذة). و ال Xو ال Yيعبران عن مكان المؤشر في إحداثيات البكسل. و سوف نقوم بعمل معادلة لكي تقوم بمعرفة مكان المؤشر بالإحداثيات النسبية (مثلً إذا كنت تريد أن ترسم شئ يمشي مع المؤشر أو تريد أن تضع صورة مؤشر آخرى و تلغي السهم التقليدي).
Mx = (float)X/W*2-1;
My = (H-Y)/(float)H*2-1;
و بعد أن تتأمل المعادلتان ستجد الآتي. بالنسبة للمعادلة الأولى التي ترجع لنا قيمة مكان المؤشر في محور السينات في نظام الإحداثيات النسبية عندنا المتغير Xيعبر عن مكان المؤشر في محور السينات بالبكسل و المتغير Wيعبر عن عرض النافذة أيضاً بالبكسل. و قد إفترضنا أن عرض النافذة في الإحداثيات النسبية هو 2.0 بإفتراض أن المحور عند يسار النافذة و ليس في الوسط لأننا بعد ذلك قمنا بإرجاع المحور إلى الوسط فطرحنا القيمة 1 من الناتج. فبعد أن إفترضنا أن عرض النافذة هو 2 قمنا بتطبيق قانون حاصل ضرب الطرفين يساوي حاصل ضرب الوسطين حيث إعتبرنا أن Wمقسومة على Xتساوي 2 مقسومة على Mxأي النقطة التي نريدها بإعتبار أن النقطة الصفر في اليسار. ثم بعد ذلك طرحنا قيمة 1 حتى نرجع قيمة الصفر البكسل تزيد كلما نزلنا على عكس الإحداثيات النسبية فإنها تقل لذلك قمنا أولً في المعادلة الثانية بقلب قيمتها عن طريق طرح الطول من مكان النقطة.
و الآن لنتكلم عن الدالة التي ذكرناها و التي تقوم بإستقبال الزر و حالة الضغط و المكان. إنظر إلى الكود التالي:
void MouseClick(int Button, int State, int xx, int yy)
{
Mx = (float)X/W*2-1;
My = (H-Y)/(float)H*2-1;
switch (Button)
{
case GLUT_LEFT_BUTTON:
if (State == GLUT_DOWN)
/* كود */
else if (State == GLUT_UP)
/* كود */
break;
case GLUT_MIDDLE_BUTTON:
if (Mx > 0.5 && Mx < 0.8 && My > 0)
/* كود */
}
}
ثم لكي تجعل مكتبة جلوت تقوم بإستدعاء هذه الدالة التي أسميناها MouseClickإذا ضغط المستخدم على أي زر نقول:
glutMouseFunc(MouseClick);
طبعاً كما هو ملاحظ فإن قيمة المتغير الأول و الذي أسميناه Buttonتعبر عن الزر الذي تم ضغطه. و قيمته تأخذ إحدى الثلاث ثوابت الآتية:
GLUT_LEFT_BUTTON – GLUT_MIDDLE_BUTTON – GLUT_RIGHT_BUTTON
GLUT_UP – GLUT_DOWN
#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ثم أخبرنا مكتبة جلوت بأن تقوم بإستدعائها و ذلك عن طريق هذا السطر:
glutMouseFunc(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);
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و بنظرة عن قرب أكثر إلا أننا سنتوقف بعد هذا الفصل لعمل تطبيق على كل ما سبق في لعبة بسيطة من باب إعطاء بعض الأفكار لا أكثر.





كل ده عشان الماوس تتحرك؟؟؟
سبحان الله
برنننننننننننننننس يا عمر
كل دة إية بس يا عم الحج .. الشرح طويل شوية بس الموضوع بسيط .. لو إنت متابع من الأول حتفهم كل حاجة ..