الحركة والأصوات في ++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 kind
Grape 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 position
sq.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 wheels
Wheel w2;
Wheel w3;
w1.set(100, 100); // position in horizontal row
w2.set(160, 100);
w3.set(220, 100);
for(int j=0; j<NUMBER; j++) // spin the wheels
{
w1.draw(); // draw each wheel
w2.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 done
sound(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:
// constructor
molecule(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 circle
circle(oldx, oldy, RAD);
setfillstyle(SOLID_FILL, BLACK); // fill with black
floodfill(oldx, oldy, DARKGRAY);
setcolor(BLACK); // erase gray circle
circle(oldx, oldy, RAD);
}
// find molecule's new position
void molecule::calculate()
{
oldx = xCo; // remember old location
oldy = 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 right
else if(xCo>=RIGHT-DIST) // if at right edge,
horz = hLEFT; // go left
switch(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 down
else 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 molecules
molecule 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 screen
rectangle(LEFT, TOP, RIGHT, BOTTOM);
while( !kbhit() ) // quit on keypress
{ // move molecules around
m1.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 ويفضل استخدام هذا النوع للمتغيرات المعبرة عن الألوان .
تعليقات
إرسال تعليق