الحركة والأصوات في ++C
الحركة والأصوات
البرنامج التالي يستخدم المنهج المتبع في البرنامج السابقة لعمل نموذج لماكينة للعبء , تعمل بقذف قطع النقود بها فتظهر ثلاثة أشكال بصورة عشوائية ويكون المرء فائزا عندما يتصادف تماثل الصور الثلاث ] تسمي هذه الماكينة slot machine [ والأشكال الثلاثة تختار عشوائيا من بين أربعة أشكال دائرتين ومربع ذو خط وتري ومثلث , وكل شكل لونه الخاص .
وينشئ البرنامج فئات مشتقة من الفئة الأساسية shape لإنشاء الدائرة والمستطيل والمثلث لإنشاء شكل ما ولكن لإعطاء إمكانية مسح أي شكل من الأشكال برسم مربع فوقه يخفيه وتشتق فئات من كل من الفئة الخاصة بالشكل وفئة noshape حتي يحتوي كل كائن ينشأ علي إمكانية فنائه .
ثم يشتق فئتين من فئة الدائرة لإنشاء الدائرتين وهما الفئة cherry والفئة grape ويشتق فئة من فئة المستطيل تسمي squere لإنشاء المربع وفئة من فئة المثلث pyramide لإنشاء المثلث .
ثم ينشئ البرنامج فئة أساسية wheel تتولي إنشاء الكائنات التي تختار الأشكال عشوائيا لرسمهما ويستخدم في البرنامج إمكانية التأثير الصوتي لإعطاء شعور بالتشغيل الواقعي للماكينة وإليك صياغة البرنامج :
slot.cpp
// slot.cpp// models a slot machine// UCS Laboratories#include <graphics.h> // for graphics functions#include <stdlib.h> // for rand(), randomize()#include <time.h> // for randomize()#include <conio.h> // for getche()#include <dos.h> // for delay(), sound(), nosound()const int W = 15; // 1/2 width of images
const int MAR = 10; // margin around images
class shape // base class
{ protected:int xCo, yCo; // coordinates of center
int linecolor; // color of outline
int fillcolor; // color of interior
public: shape() // no-arg constructor { xCo=0; yCo=0; linecolor=WHITE; fillcolor=WHITE; }void set(int x, int y, int lc, int fc)
{ xCo=x; yCo=y; linecolor=lc; fillcolor=fc; } void draw() { setcolor(linecolor); // set line color setfillstyle(SOLID_FILL, fillcolor); // set fill color}
};
class ball : public shape
{ public: ball() : shape() // no-arg constructor { } void set(int x, int y, int lc, int fc) // set data
{ shape::set(x, y, lc, fc); }void draw() // draw the ball
{ shape::draw(); // set colors circle(xCo, yCo, W); // draw circle floodfill(xCo, yCo, linecolor); // fill circle}
};
class rect : public shape
{ public: rect() : shape() // no-arg constructor { }void set(int x, int y, int lc, int fc) // set data
{ shape::set(x, y, lc, fc); }void draw() // draw the rectangle
{ shape::draw(); // set colors rectangle(xCo-W, yCo-W, xCo+W, yCo+W); // draw rectangle floodfill(xCo, yCo, linecolor); // fill rectangle line(xCo-W, yCo+W, xCo+W, yCo-W); // draw diagonal}
};
class tria : public shape
{ public: tria() : shape() // no-arg constructor { }void set(int x, int y, int lc, int fc) // set data
{ shape::set(x, y, lc, fc); }void draw(); // draw the triangle
};
void tria::draw() // draw the triangle
{int triarray[] = { xCo, yCo-W, // top
xCo+W, yCo+W, // bottom right xCo-W, yCo+W }; // bottom left shape::draw(); // set colors fillpoly(3, triarray); // draw triangle}
class noshape : public shape
{ public:void erase(); // erase old shape
};
void noshape::erase() // erase old shape
{int border[] = // rectangle to erase
{ xCo-W-MAR, yCo-W-MAR, // upper-left xCo+W+MAR, yCo-W-MAR, // upper-right xCo+W+MAR, yCo+W+MAR, // bottom-right xCo-W-MAR, yCo+W+MAR }; // bottom-left setfillstyle(SOLID_FILL, DARKGRAY); // background color fillpoly(4, border); // fill it}
class Cherry : public ball, public noshape
{ public: Cherry() : ball() // no-arg constructor { }void set(int x, int y) // set data
{ball::set(x, y, WHITE, RED);
noshape::set(x, y, WHITE, RED);
}
void draw() // draw a cherry
{ erase(); ball::draw(); } };
class Grape : public ball, public noshape
{ public: Grape() : ball() // no-arg constructor { }void set(int x, int y) // set data
{ball::set(x, y, WHITE, BLUE);
noshape::set(x, y, WHITE, BLUE);
}
void draw() // draw a grape
{ erase(); ball::draw(); } };
class Square : public rect, public noshape
{ public: Square() : rect() // no-arg constructor { }void set(int x, int y) // set data
{rect::set(x, y, WHITE, CYAN);
noshape::set(x, y, WHITE, CYAN);
}
void draw() // draw a square
{ erase(); rect::draw(); } };
class Pyramid : public tria, public noshape
{ public: Pyramid() : tria() // no-arg constructor { }void set(int x, int y) // set data
{tria::set(x, y, WHITE, GREEN);
noshape::set(x, y, WHITE, GREEN);
}
void draw() // draw a pyramid
{ erase(); tria::draw(); }};
class Wheel // contains four pips
{ private: Cherry ch; // make one pip of each kindGrape gr;
Square sq;
Pyramid py;
int xCo, yCo; // wheel position
public: Wheel() // no-arg constructor { xCo=0; yCo=0; }void set(int x, int y)
{ xCo=x; yCo=y; // set our position ch.set(xCo, yCo); // set four pips to gr.set(xCo, yCo); // our positionsq.set(xCo, yCo);
py.set(xCo, yCo);
}
void draw(); // draw a random pip
};
void Wheel::draw() // draw a random pip
{switch( random(4) ) // random number from 0 to 3
{case 0 : ch.draw(); break; // draw one of the pips
case 1 : gr.draw(); break; // selected randomly
case 2 : sq.draw(); break;
case 3 : py.draw(); break;
}
}
void main()// UCS Laboratories {const int NUMBER = 60; // number of times to cycle
int driver, mode; driver = DETECT; // set to best graphics mode initgraph(&driver, &mode, "\\tc\\bgi"); randomize(); // seed random number generator Wheel w1; // make three wheelsWheel w2;
Wheel w3;
w1.set(100, 100); // position in horizontal roww2.set(160, 100);
w3.set(220, 100);
for(int j=0; j<NUMBER; j++) // spin the wheels
{ w1.draw(); // draw each wheelw2.draw();
w3.draw();
sound(100); delay(20); nosound(); // click delay( j*j/20 ); // delay gets longer and longer}
sound(400); delay(400); // two tones to signal donesound(500); delay(800); nosound();
getche(); // wait for keypress closegraph(); // close graphics system}
عمل البرنامج ينشئ البرنامج ثلاثة كائنات منتمية للفئة wheel , ينشئ كل كائن شكلا في موضع محدد بحسب عدد يتضمنه عشوائيا من 0 إلي 4 وتنشأ الأشكال خلال دوارة تتضمن الدالة المكتبية sound() المسئولة عن إصدار التأثير الصوتي .
إصدار الأصوات
يتطلب إصدار الأصوات في برامج السي ++ ثلاثة خطوات : 1 ) تشغيل الصوت , 2) تحديد المدة , 3) إغلاق الصوت . والدوال الثلاث المسئولة عن هذه الخطوات تتطلب ملف dos.h .
الدالة sound()
تقوم هذه الدالة بتشغيل مولد الصوت بالجهاز , ولها معامل واحد هو تردد الصوت بالهرتز ويتراوح الرقم المدخل من 15 إلي 3000 وتزيد حدة الصوت زيادة الرقم .
الدالة delay()
تتحكم هذه الدالة في مدة تشغيل مولد الصوت , ولها معامل واحد هو مدة التشغيل بالملي ثانية فالرقم 500 يشغل الصوت لنصف ثانية . الرقم 20 المخل من الصفر بحيث يعطي صوت تكة واحدة .
الدالة nosound()
تقوم هذه الدالة بإغلاق مولد الصوت وليس لهذه الدالة معاملات
نظرة تأملية
يلقي البرنامج slot الضوء علي القوة التنظيمية للأسلوب الكائني في البرمجة فبدءا من فئة بسيطة أمكن عن طريق خاصية التوارث التسلسل إلي فئات أكثر عمقا وعن طريق التوارث المتعدد تزداد مرونة هذه الإمكانية . وتصبح العلاقة بين أجزاء البرنامج أكثر قربا من المنطق وتمثيلا للواقع حيث يمكن أن تشتق الفئات علي أساس من المنطق البديهي بينما تكون العلاقات في البرمجة الهيكلية علاقة شكلية بحسب وضع الدوال الفرعية بالنسبة لبعضها البعض .
ويمثل هذا الأسلوب في البرمجة بالتالي أداة قوية في تمثيل الواقع كما نبين في المثال التالي :
تمثيل الحركة
كمثال بسيط لتمثيل الحركة نقدم مثلا يصور حركة الجزيئات داخل إناء , يمكن استخدامه في برامج أكثر تعقيدا للبرامج التي تتعامل مع الفيزياء أو الكيمياء ( من لديه رهبة من المواد العلمية يمكن اعتباره تمثيلا لحركة كرات البلياردو) . تتضمن الكائنات المنشأة للفئة molecule بيان الموضع (عن طريق الإحداثيات مقاسة بالبكسلات ) وسرعة الحركة (مقاسة ببكسلة لكل دورة من الجهاز ) وسوف نقصر البرنامج علي ثمانية جزيئات فقط .
تحدد الحركة بالاتجاه الرأسي , وله قيمتان up, down والاتجاه الأفقي وله قيمتان righ, left وتقوم الدوال المنتمية للبرنامج بمسح الأشكال المرسومة وتحسب موضعا للشكل التالي , ثم ترسمه بهذه الوسيلة يحدث تأثير الحركة , وإليك صياغة البرنامج .
molecule.cpp
// molecule.cpp// moving balls reflect on edges of screen// UCS Laboratories#include <graphics.h> // for graphics functions#include <conio.h> // for getch()const int RAD = 5; // radius of molecules
const int MAX = 6; // maximum speed of molecules
const int DIST = (RAD+MAX); // reflection distance
const int LEFT = 0; // screen coordinates
const int TOP = 0;
const int RIGHT = 639; // standard VGA screen
const int BOTTOM = 479;
enum hdir {hSTOP=0, hRIGHT, hLEFT}; // horizontal direction
enum vdir {vSTOP=0, vDOWN, vUP}; // vertical direction
class molecule // molecule class
{ private:int xCo, yCo; // coordinates of center
int oldx, oldy; // previous coordinates
int speed; // speed: pixels per unit time
hdir horz; // horiz direction: stop, right, left vdir vert; // vert direction: stop, down, up COLORS color; // color public: // constructormolecule(int x, int y, int s, hdir h, vdir v, COLORS c)
{ xCo=x; yCo=y; speed=s; horz=h; vert=v; color=c; }void erase(); // erase old position
void calculate(); // calculate new position
void draw(); // draw new position
};
void molecule::erase() // erase molecule in old position
{ setcolor(DARKGRAY); // draw gray circlecircle(oldx, oldy, RAD);
setfillstyle(SOLID_FILL, BLACK); // fill with blackfloodfill(oldx, oldy, DARKGRAY);
setcolor(BLACK); // erase gray circlecircle(oldx, oldy, RAD);
}
// find molecule's new positionvoid molecule::calculate() { oldx = xCo; // remember old locationoldy = yCo;
switch(horz) // move left or right
{case hSTOP: /* no move */ break;
case hRIGHT: xCo += speed; break;
case hLEFT: xCo -= speed; break;
}
if(xCo<=LEFT+DIST) // if at left edge,
horz = hRIGHT; // go rightelse if(xCo>=RIGHT-DIST) // if at right edge,
horz = hLEFT; // go leftswitch(vert) // move up or down
{case vSTOP: /* no move */ break;
case vDOWN: yCo += speed; break;
case vUP: yCo -= speed; break;
}
if(yCo<=TOP+DIST) // if at top edge,
vert = vDOWN; // go downelse if(yCo>=BOTTOM-DIST) // if at bottom edge,
vert = vUP; // go up}
void molecule::draw() // draw molecule in new position
{setcolor(color);
circle(xCo, yCo, RAD);
setfillstyle(SOLID_FILL, color);
floodfill(xCo, yCo, color);
}
void main() { int driver, mode; driver = DETECT; // set to best graphics mode initgraph(&driver, &mode, "\\tc\\bgi"); // create some moleculesmolecule m1(100, 120, 2, hRIGHT, vUP, BLUE);
molecule m2(150, 220, 2, hLEFT, vUP, GREEN);
molecule m3(200, 140, 3, hRIGHT, vDOWN, CYAN);
molecule m4(250, 240, 3, hLEFT, vDOWN, RED);
molecule m5(300, 160, 3, hRIGHT, vUP, MAGENTA);
molecule m6(350, 260, 4, hLEFT, vUP, LIGHTGREEN);
molecule m7(150, 350, 4, hLEFT, vSTOP, LIGHTGRAY);
molecule m8(150, 350, 4, hSTOP, vUP, BROWN);
// outline screenrectangle(LEFT, TOP, RIGHT, BOTTOM);
while( !kbhit() ) // quit on keypress
{ // move molecules aroundm1.calculate(); m1.erase(); m1.draw();
m2.calculate(); m2.erase(); m2.draw();
m3.calculate(); m3.erase(); m3.draw();
m4.calculate(); m4.erase(); m4.draw();
m5.calculate(); m5.erase(); m5.draw();
m6.calculate(); m6.erase(); m6.draw();
m7.calculate(); m7.erase(); m7.draw();
m8.calculate(); m8.erase(); m8.draw();
}
getche();
closegraph(); // close graphics system}
ينشئ البرنامج ثمانية جزيئات , ويحركها ويبين الشكل الجزيئات ترتد علي جوانب الإناء وللأسف ليس بالإمكان تمثيل الحركة علي الصورة .
وكما في الرسوم المتحركة عادة , ينتج تأثير الحركة عن طريق تحريك الشكل في خطوات صغيرة تتكرر بسرعة . فإذا كان جهازك بطيئا فقد تجد الأشكال تتحرك ببطء , و بإمكانك إزالة بعضا من الأشكال للإسراع في الحركة . أما إذا كان الجهاز أسرع من اللازم فقد تحتاج لإدخال دالة تأخير delay() في الدوارة while .
وكما تلاحظ لكل جزئ اتجاهان للحركة أفقية ورأسية لحساب الموضع الجديد , نضيف أو نطرح لأحد الاتجاهين المتغير speed طبقا للاتجاه . وينعكس الاتجاه إذا ما وصل جزئ لحد من حدود الرسم مما يعطي انطباعا بالارتداد .
كما تلاحظ أن النوع COLOR معرف في ملف graphics.h كمرادف للنوع int ويفضل استخدام هذا النوع للمتغيرات المعبرة عن الألوان .
تعليقات
إرسال تعليق