السبت، 14 أبريل 2012

لعبة الطيور السعيدة بواسطة مكتبة g4c

صنعت منذ مدة مكتبة اسمها g4c لبرمجة الالعاب. هذا درس سريع فيها. تحتاج إلى Visual C++ لتجرب ويمكنك تحميل المكتبة نفسها هنا:


سوف نصنع اليوم لعبة happy bird..البعض يحب الطيور الغاضبة لكننا نسمع دائما نصائح على غرار "كن ايجابيا"، "تفاءل" لذلك لن نقلد هؤلاء :)

تجهيز البرنامج


أولاً، خذ نسخة من الdirectory المسمى g4c empty project وسمها بإسم مشروعك. هذه هي الطريقة الرسمية لعمل مشروع جديد.

ثانياً، افتح المشروع (اسمه ينتهي بـ .sln) في الفجوال سي++.

ثالثاً: اذهب إلى مربع solution explorer في يسار الشاشة، أو اختر view -> solution explorer، افتح جزء source files، واختر empty.cpp

سوف تجد دالة main فارغة..سوف تكون هذه برنامجنا بعد قليل.

الرسومات


ارسم طائراً وتفاحة وضعهما في ملفين بإسم bird.bmp و apple.bmp وخزنهما في مكان سهل الوصول اليه مثل c:\img

لاحظ ان الملف يجب ان يكون من نوع BMP وليس أي نوع آخر.لو كانت مواهبك الفنية ضعيفة يمكنك الحصول على الصور من الكود الكاملة الخاصة بهذا المثال.

وضع الشخصيات على الشاشة


يوجد في g4c مفهوم الsprites (الأشكال الطيفية)؛ وهي صور يمكن تحريكها على الشاشة تمثل شخصيات الالعاب (طائرة، كرة، ثائر، ...). يوجد لديك 30 sprite في البرنامج ارقامهم من 0 إلى 29. لتحميل صورة في احدهم تستخدم الدالة load_sprite.
هذه الدالة تأخذ (1) اسم الملف الذي يحتوي الصورة (2) رقم الsprite المطلوب تحميل الصورة فيه.
void main()
{
 load_sprite("c:/img/bird.bmp", 0);
 load_sprite("c:/img/apple.bmp", 1);
}
لكننا لم نرسم هذه الأطياف بعد! لنفعل ذلك نستخدم الدالة put_sprite، وهي تأخذ رقم الشكل الطيفي وقيم x,y الخاصة بالنقطة التي سيوضع فيها. (x من اليسار، y من الأعلى).
void main()
{
 //write your program ;)
 load_sprite("c:/img/bird.bmp", 0);
 load_sprite("c:/img/apple.bmp", 1);

 int x = rand() % 640;
 int y = rand() % 480;

 put_sprite(0, x, y);
 put_sprite(1, 150, 150);
}


هنا اخترنا قيمة عشوائية من صفر إلى 633 للمتغير x (عن طريق عملية mod)، ومثلها للy من 0 إلى 479 (لأن الشاشة دائما 640×480). ثم نادينا put_sprite لنضع الطيف رقم صفر (الطائر) في المكان العشوائي، بينما التفاحة دائما في المكان 150، 150.

تحريك الطائر


لكي نحرك الطائر سنصنع حلقة متكررة كالآتي:

استمر في الآتي:
اقرأ المفتاح
لو المفتاح المضغوط يمين، حرك الطائر لليمين
لو المفتاح المضغوط يسار، حرك الطائر لليسار

لكي نقرأ المفتاح نستخدم الدالة inkey، وهي تعيد -1 لو لم يكن هناك مفتاح مضغوطا لحظة ندائها، أو تعيد كود لو كان زر مضغوط. ما هي الأكواد؟ هي ثوابت في الويندوز (رقم لكل مفتاح) تجدها هنا

حسناً إذاً...
void main()
{
 load_sprite("c:/img/bird.bmp", 0);
 load_sprite("c:/img/apple.bmp", 1);

 int x = rand() % 640;
 int y = rand() % 480;

 put_sprite(0, x, y);
 put_sprite(1, 150, 150);
 while(true)
 {
  int k = inkey();
  switch(k)
  {
  case VK_UP:
   y -= 3;
   break;
  case VK_DOWN:
   y += 3;
   break;
  case VK_LEFT:
   x -= 3;
   break;
  case VK_RIGHT:
   x += 3;
   break;
  }
  put_sprite(0, x, y);
 }
}
الثوابت VK_... هي الأكواد التي حدثتك عنها. لاحظ ان تغيير قيم الx, y لا تكفي! يجب أن آمر البرنامج ان يعيد رسم الطيف في المكان x,y بواسطة الدالة put_sprite

الآن لو قمنا بتشغيل البرنامج يمكننا تحريك الطائر بالأسهم.
التصادم

كيف نعرف ان الطائر وصل للتفاحة؟ هناك دالة في الg4c اسمها register_sprite_proc، هذه تأخذ اسم دالة اخرى (يجب ان تكون موجودة) وتقوم باستدعاها اوتوماتيكيا في حالة تصادم طيفين مع بعضهما.

يعني لو قلت مثلا
register_sprite_proc(myFunc);


فسوف يقوم الg4c - وليس انت - باستدعاء myFunc في حالة أي تلامس لاشكال sprite أثناء تنفيذ البرنامج. لابد لهذه الدالة ان يكون لها مواصفات محددة:
  • Can have any name
  • Return value always of type int
  • Parameters always of types (int, int, void *)
القيمة من نوع * void يمكن تجاهلها، أما الparameters من نوع int, int فهي ارقام الsprites المتصادمة. يعني لو اثناء تشغيل البرنامج تلامس الاطياف 3، 22 فسوف يتم استدعاء هذه الدالة بحيث يكون اول اثنان من الparameters هما [3، 22] أو [22، 3].

لكننا في لعبتنا لن نحتاج لفحص هذه القيم لأنه في حالة تلامس اطياف فليس لدينا اصلا سوى الطائر والتفاحة!
ها هي الكود الجديدة، لاحظ الاجزاء باللون الأحمر!
int myFunc(int s1, int s2, void *)
{
// حنكتب هنا ايه اللي المفروض يحصل لما الطائر يوصل للتفاحة
 return 0;
}

void main()
{
 //write your program ;)
 load_sprite("c:/img/bird.bmp", 0);
 load_sprite("c:/img/apple.bmp", 1);

 register_sprite_proc(myFunc);

 int x = rand() % 640;
 int y = rand() % 480;

 put_sprite(0, x, y);
 put_sprite(1, 150, 150);
 while(true)
 {
  int k = inkey();
  switch(k)
  {
  case VK_UP:
   y -= 3;
   break;
  case VK_DOWN:
   y += 3;
   break;
  case VK_LEFT:
   x -= 3;
   break;
  case VK_RIGHT:
   x += 3;
   break;
  }
  put_sprite(0, x, y);
 }
}
ماذا سيحدث في الدالة myFunc؟ نريد ان نخبر المستخدم انه فاز في اللعبة. لكي نعرض رسالة على الشاشة لابد من استخدام دالة text_out، وهي تأخذ string + مكان x, y الذي سيظهر فيه الكلام. نعدل الدالة myFucn ونترك باقي البرنامج كما هو


int myFunc(int s1, int s2, void *)
{
 text_out("You won!!", 5, 5);
 return 0;
}
ربما تريد أيضاً قراءة دليل الملتمس إلى استخدام الماوس.

هناك 81 تعليقًا:

ahmed alaa يقول...

dr hwa ana yenfa3 a3mel snake game bil g4c

Mohamed Samy يقول...

@ahmed alaa

لا يوجد ما يمنع :)

Ahmed Atito يقول...

بسم الله ماشاء الله بجد بجد شرح اكثر من رائع شكراً جداً يا دكتور

Mohamed Samy يقول...

@Ahmed Atito

سعيد أنه أعجبك :)

Boshkash يقول...
أزال المؤلف هذا التعليق.
Boshkash يقول...

لو سمحت يا دكتور .. أنا كنت عايز اعرف ايه اللي مكان الـ goto في الـ g4c .. وشكرا مقدما :)

abdalla hazem يقول...

دكتور هو مافيش حاجه تسرع الحركه شويه

Mohamed Samy يقول...

@Abdalla Hazem

G4C was not really designed for speed. Try increasing the velocity or disabling sprite collision for non-essential sprites

elsherbini يقول...

ازاي الكتبه ديه تساعدني في مشروع Typisch Schools Application وشكرا

Mohamed Samy يقول...

@elsherbini

مكتبة g4c مطروحة كما هي لمن يريد استخدامها، ومش دوري أني أقيم ملاءمتها لمشروع معين :)

Unknown يقول...

لو سمحت يادكتور دلوقتى عرفت ازاى اطلع صورة فى مكان معين طيب لو عايز اطلع الصورة على الشاشة كاملة اعملها ازاى؟

Mohamed Samy يقول...

@Ahmed Mohamed

لو قصدك "الشاشة كاملة" بمعنى شاشة الكمبيوتر، فالإجابة أن الإمكانية دي غير موجودة في g4c. البرنامج دائماً بيشتغل في run window له حجم ثابت.

لو قصدك صورة تملأ الrun window بالكامل: اعمل صورة بحجم 640×480 وارسمها في المكان 0، 0.

ملاحظة: لو الصورة معمولة كخلفية ممكن تستخدم disable_sprite_proc(int) معاها وإلا حيحصل collision detection بينها وبين كل الـsprites الثانية على الشاشة، أو ممكن بدل ما ترسمها بـput_sprite تستخدم copy_sprite_image

Unknown يقول...

طيب لو سمحت يادكتور دلوقتى انا معنديش غير من اماكن من 0 الى 29 يعنى معرفش اطلع اكتر من 30 صورة طيب دلوقتى مثلا لو انا محتاج اكتر من 30 صورة اعمل ايه؟

Mohamed Samy يقول...

@Ahmed Mohamed

Do you want them as images or as sprites (with motion & collision)? If you just want to draw images use the copy_sprite_image function, if you want sprites you can:

1- Rework your game to use 30 sprites or less
OR
2- Change the maximum by changing the value of the constant maxSprites defined in g4c.h, but note that this may slow down your game.

Unknown يقول...

if i use copy_sprite_image(,,)
to make imge how can i delete thes capy of image or hide it with aut del or hid the sprite?

Mohamed Samy يقول...

@Mahmoud Rashad

If you use copy_sprite_image then you're not drawing a sprite, you're drawing an image (similar to drawing a line or a circle), and therefore cannot be moved...etc.

You can erase it by using e.g fill_rect to draw a rectangle over it.

Unknown يقول...

لو سمحت يادكتور ممكن حضرتك تكتبلى مثال يوضح اكتر الفنكشن بتاعت التصادم وخاصة الparamenter بتاع الpointer مش عارف استخدامه ازاى معلش يادكتور بتعب حضرتك؟

Mohamed Samy يقول...

@Ahmed Mohamed

Do not use the void pointer, it's not currently used in g4c (but reserved for future use).

The collision callback will be called automatically when two sprites collide. You write the callback the way you want and register it using register_sprite_proc

The arguments passed (by g4c) to the callback will be the numbers of the sprites that collided (in no particular order). You can test those numbers to see if the sprites that collided are the ones you care about.

Did that help?

Unknown يقول...

طيب يا دكتور انا كتبتها وبردك بيطلعلى ايرور register_sprite_proc(onsprite( a,4));
وبرة ال main بخدهم varaible 2عادى
فمعلش يادكتور ممكن حضرتك تكتبلى مثال يوضحها اكتر من كدة

Mohamed Samy يقول...

الهدف مش انك تنادي الـcallback. الهدف انك ترسلها لـregister_sprite_proc

You should not call the callback yourself. You should not supply parameters to it. It will be called for you, with the parameters filled in.

Mohamed Samy يقول...

Here's a quick example on using the mouse with g4c:
_______________________
// put this code after the #include....statements in you empty.cpp
int myMouseProc(int x, int y, bool left, bool right, void *)
{
char buffer[80];
sprintf(buffer, "position =(%d,%d)|left button=%d|right button=%d", x, y, left, right);
text_out(buffer, 100, 100);
}

void main()
{
register_mouse_proc(myMouseProc);
}

Unknown يقول...

طيب دلوقتى يادكتور حضرتك قولتلك هى اللى هتسدعي ال parameters مش هتحتاج انى انا اللى ابعتهلها .....طيب دلوقتى انا مثلا عندى اكتر من اتنين parameters فى اللعبة ازاى هى هتعرف انا عايز انى اتنين فيهم ؟!!!!

Mohamed Samy يقول...

The callback is invoked whenever a mouse action (button press, button release, motion) happens. Try running the example, holding the left button, and moving the mouse.

Unknown يقول...

طيب ده يادكتور الفنكشن اللى انا كتبتها
int onsprite(int a,int k ,void *)
{
return 0;
}
void main
{
register_sprite_proc(onsprite);
}
ودلوقتى انا عايز التصادم بين الصورة رقم 0و 4 يعنى عايز اخلى ال a=0; , وال k=4; او العكس اعملها ازاى

Mohamed Samy يقول...

الإجابة موجودة في مثال الطيور السعيدة؛ أرجو أن تقرأه مرّة أخرى.

Unknown يقول...

فى مثال الطيورة السعيدة ما كانش فى غير صورتين بس ما احنجناش ندى قيم للparameters بس دلوقتى انا عندى اكتر من صورتين ومش عارف ابعت رقم الصور ازاى

Mohamed Samy يقول...

You do not provide values for parameters to the callback. Read the example carefully.

Unknown يقول...

plz doctor i need to take input in text box can u help me in that ?

Mohamed Samy يقول...

g4c Does not support adding text box controls

Unknown يقول...

لو سمحت يا دكتور .. أنا كنت عايز اعرف ايه اللي مكان الـ goto في الـ g4c .

Unknown يقول...
أزال المؤلف هذا التعليق.
Mohamed Samy يقول...

ما المقصود بـgoto؟ لو قصدك طريقة لكتابة نص في مكان معين على الشاشة شوف text_out

Unknown يقول...

طب بالنسبة للعبة الdots يا دكتور نعمل فيها ايه ؟

Unknown يقول...

و يا دكتور ف errors كتير قبل ماكتب اى حاجة فى الملفات.h و كدة اعمل ايه ؟؟؟

Mohamed Samy يقول...

@medo gemy

1- عندك فيها سؤال محدد؟
2- بالنسبة للأخطاء؛ هل اتبعت طريقة عمل المشاريع الموصوفة في أول المقال؟

Unknown يقول...
أزال المؤلف هذا التعليق.
Unknown يقول...

لا ال errors خلاص طلع عيب فى ال visual و صلحته.
الdots: انا عملت sprite على شكل dots 5*5 دلوقتى ارسم الخط ازاى بالماوس ؟؟
واحدد اللون

Mohamed Samy يقول...

جرب. جربت؟

Unknown يقول...

انا مش لاقى حاجة شبهها فى g4c explained غير example mouse callback ف غيرت فيه بدل fire...
كتبت void draw_line(int x1,int y1,int x2,int y2,int clr);
بس فيه error

Unknown يقول...

wla void register_mouse_proc(MouseProc);
انا مش فاهمهم

Mohamed Samy يقول...

Have you tried reading this

http://iamsamy.blogspot.com/2012/04/g4c_27.html

Unknown يقول...

لا اول مرة اقراها,فهمتها.

بس عندى مشكلة فى اتجاه الخط احدد النقط ازاى داخل الsprite ؟؟ يعنى عايزه بين اى نقطتين انا عايزهم اكتب ايه هنا (draw_line(?,?,x,y
x,y دول مكان المؤشر


و ربنا يخليك لينا :)

Unknown يقول...

عملتها يا دكتور عرفت
a=x-1
b=y-1
draw_line(a,b,x,y)
ممكن تعليقك ؟

Unknown يقول...

خليت player 2 يلعب right button
صح ولا ايه ؟

Unknown يقول...
أزال المؤلف هذا التعليق.
Unknown يقول...

انا بس عايز الحتة دى D:
completes closed square gains point
ادينى hints
D:

Mohamed Samy يقول...

You need to store information about the squares: what edges have already been drawn (top, left,...etc). This will allow you to find what squares have been completed.

Unknown يقول...

how to store these information
could you give me an example plz :D

Unknown يقول...

لو سمحت يادكتور ازاى اعمل المتابعة او المطارده فى لعبة الpacman يعنى ازاى اخلى الاعداء يعرفوا مكان ال pacman ويطاردوه ؟!!!!!!!!!!!

Mohamed Samy يقول...

Use what you know (arrays, structs,...etc)

Mohamed Samy يقول...

@Ahmed Mohamed

Learn about the A* algorithm

Unknown يقول...

طب ماالsquares دى جوة الsprite ازاى هاشاور عليها بarray مثلا

Mohamed Samy يقول...

يعني إيه squares جوة sprites؟ مش انت اللي بترسم كل حاجة؟ يبقى عندك المعلومات المطلوبة، وممكن تخزنها.

Unknown يقول...

انا بعت لحضرتك رسالة على الايميلgmail ممكن تقراها.
شكرا لتعبك معايا

Unknown يقول...

دكتور انا مطلوب منى اعمل dots game وعملت ال dots بطريقة دى
for(int i=0;i<10;i++)
{
for(int j=0;j<10;j++)
{
fill_ellipse(60+(i*60),60+(j*60) ,10, 10, 0,7);
}
}
تنفع ولا لا
ولو تنفع لوسمحت كنت عايز اعرف اعمل ازاى خطوط مخفية بينهم بحيث ان اليوزر لما يدوس عليه بالماوس يظهر
وياريت يا دكتور ترد عليا علشان دة project عليا ولازم اسلمو قريب

Mohamed Samy يقول...

@Ahmed

الإجابة الرسمية لمعظم أسئلة "ينفع كذا" هي "جرب وشوف"

بالنسبة للمكان اللي يرسم فيه دي مشكلة برمجية مش حاجة خاصة بمكتبة g4c، المكتبة بتقدم لك مكان مؤشر الماوس (في الـmouse proc) وبتسمح لك ترسم على الشاشة. تعمل إيه بدول ده دور الكود.

Unknown يقول...

لوسمحت يا دكتور ازاى فى لعبة hang man عايزة ابين صورة واخفيها وبعد كده ابين صورة تانية وهكذا وشكرا يا دكتور

Mohamed Samy يقول...

لو الصورة ظهرت بـput_sprite يبقى إخفاؤها بـhide_sprite

ملاحظة: قيم x, y في hide_sprite مش مهمة..أي أرقام تنفع

لو تم إظهارها بـcopy_sprite_image يبقى إخفاؤها برسم مستطيل فوقها

Unknown يقول...

فى حاجة تخلينى اعرف مكان اى نقطة(x,y) فى الwindows application

Mohamed Samy يقول...

مكان أي نقطة؟؟

Unknown يقول...

دكتور لما بعمل run للكود بيظهر اللعبه بس لما لعمل click بالmouse فى اى مكان بيوقف الdebug مع ان الكود صح

Mohamed Samy يقول...

@Rana Hossam

لا يوجد معلومات كافية في السؤال لأقول رأياً.

أيضاً أرجو النظر إلى هذا المقال ربما تكون مشكلتك أحد المشاكل المذكورة فيه:

http://iamsamy.blogspot.com/2013/05/g4c-common-problems-and-frequently.html

Unknown يقول...

يا دكتور هو بعد ما ينفذ الcollision بيرجع لل main ولا لأ

Unknown يقول...

يا دكتور هو بعد ما ينفذ ال collision بيرجع تا ني لل main ولا لأ

Unknown يقول...

كيفية اعادة التصادم لجسم واحد اكثر من مرة يا دكتور لو سمحت؟

Unknown يقول...

كيفية اعادة التصادم لجسم واحد اكثر من مرة يا دكتور لو سمحت؟

Mohamed Samy يقول...

@mohamed omiera

مش فاهم قصدك من السؤال. عموماً الـcallback بتاعة التصادم بتستدعى مادامت الـsprites متلامسة).

Unknown يقول...

الجسم بعد مايخصله تصادم اعمله hide وبعد كدة اظهره فى مكان تانى ينفع بقى بعد كدة لو حصله تصادم تانى اكرر الموضوع ده؟

Unknown يقول...

يعنى فى لعبة snake انا بخلى الطعم بتاع snake يختفى بعد مايحصل تصادم بين snake والطعم وبظهر نفس ال sprite فى احداثيات تانية غير الاولى بس بعد كدة بيجى snake قدام الطعم مش بيعمل تصادم ولا هو لازم لكل مرة اظهر فيها الطعم يكونsprite مختلف عن الى قبله؟

Mohamed Samy يقول...

The collision callback is invoked whenever two sprites collide. It doesn't matter if the sprites had collided before.

Unknown يقول...

اعمل callback ازاى طيب معلش يا دكتور؟

Mohamed Samy يقول...

You have already done it.

Unknown يقول...

int spriteproc(int a,int b,void *data)


{
hide_sprite(b,0,0);
put_sprite(b,5,5);


return 0;
};


deh alfunction

بس التعبان بياكل الطعم اول مرة ويجى المرة التانية مش بيحصل تصادم

Unknown يقول...


معلش يا دكتور علشان مش فاهم يعنى انا الطعم ده بيبقى اكتر من sprite ولا sprite واحد وانا بعمله hide واظهره فى مكان تانى وهكذا ؟

Mohamed Samy يقول...

أعتقد ان عشر دقائق من التجربة حتكون أفضل عشان توضح كل حاجة.

اعمل برنامج وشغله وشوف اللي حيحصل.

Unknown يقول...
أزال المؤلف هذا التعليق.
Unknown يقول...

#include
#include
using namespace std;
void main()
{
string x;
cout<<"Enter the word"<<endl;
getline(cin,x);

for(int i=0;i<x.length();i++)
{
if( x[i]=='a'|| x[i]=='e'|| x[i]=='i'|| x[i]=='o'|| x[i]=='u')
{
if(x[i]!=x[i]) // y3ny a el line dh?? geh m3aya bl 7z w msh fahm el brnamg esht3'l ezaay :D //
{
cout<<x[i+1];
i++;
}
}
else cout<<x[i];
}
}
dh brnamg besheel el vowels

Mohamed Samy يقول...

@medo gemy

Your program is not related to g4c

Unknown يقول...

m5dtsh baly ..srry
fe link tany 3'ir dh ??

Mohamed Samy يقول...

@medo gemy

I don't debug programs online on my blog :)

Unknown يقول...

لو سمحت يا دكتور هو فى حاجة اقدر اخلى بيها ال text_out تاخد (int,int,int) بدل ما بتاخد (string,int,int)