>> الصفحة الرئيسية >> مقالات ZetCode >> [11] لعبة تحطيم الطوب

الأقسام الرئيسية

 التنصيب والاعداد التنصيب والاعداد
 دروس متقدمة دروس متقدمة
 سلسلة دروس للمبتدئين سلسلة دروس للمبتدئين
 اساسيات لغة سي++ اساسيات لغة سي++
 مقالات ZetCode مقالات ZetCode
 

جديد الدروس

 الوحدة QtUiTools (واجهة المستخدم ديناميكية التوليد)
التاريخ 04/07/2009 الوحدة QtUiTools (واجهة المستخدم ديناميكية التوليد)
 Qt and Phonon
التاريخ 04/07/2009 Qt and Phonon
 تتمة الرسائل ومربعات الحوار في qt
التاريخ 04/07/2009 تتمة الرسائل ومربعات الحوار في qt
 مدخل الى xml في Qt
التاريخ 04/07/2009 مدخل الى xml في Qt
 الإدخال والإخراج لبيانات الفئات
التاريخ 04/07/2009 الإدخال والإخراج لبيانات الفئات
 

[11] لعبة تحطيم الطوب

في هذا الجزء من سلسلة دروس Qt4 c++ سوف نقوم بصنع لعبة سهلة .
لعبة تحطيم الطوبة هي لعبة أركيد مطورة من قبل شركة أتاري المحدودة وكان ذلك عام 1976 .
في هذه اللعبة سوف يقوم اللاعب بتحريك قاذف الكرة على الشاشة ليقوم بعكس إتجاه الكرة حيث أن هدفه هو تحطيم الطوبة في أعلى الشاشة .
التطوير:-
في لعبتنا هناك قاذف(عاكس) الكرة و كرة و 30 طوبة ولدينا لكل منها صورة في المجلد inkscape . وأستخدمنا حدث المؤقت لإنشاء دورة اللعبة . سوف لن نتعامل مع الزوايا بكل بساة سوف نغير الإتجاهات فقط أعلى أو أسفل او يمين ويسار .وقد ألهمت الفكرة من لعبة pybreakout التي طورة بواسطة مكتبة PyGame بواسطة ناثان دوسن.
شيفرة اللعبة سهلة الفهم حيث أنها لاتحتوي على إضافات أثناء اللعب أو مستويات أو نتائج لذلك هي سهلة الفهم .
paddle.h

  1. #ifndef PADDLE_H
  2. #define PADDLE_H
  3.  
  4. #include <QImage>
  5. #include <QRect>
  6.  
  7. class Paddle
  8. {
  9.  
  10. public:
  11. Paddle();
  12. ~Paddle();
  13.  
  14. public:
  15. void resetState();
  16. void moveLeft(int);
  17. void moveRight(int);
  18. QRect getRect();
  19. QImage & getImage();
  20.  
  21. private:
  22. QImage image;
  23. QRect rect;
  24.  
  25. };
  26.  
  27. #endif

ملف الرأس هذا لكائن القاذف(العاكس).
paddle.cpp

  1. #include "paddle.h"
  2.  
  3.  
  4. Paddle::Paddle()
  5. {
  6. image.load("paddle.png");
  7.  
  8. rect = image.rect();
  9. resetState();
  10. }
  11.  
  12. Paddle::~Paddle()
  13. {
  14. printf("paddle deletedn");
  15. }
  16.  
  17. void Paddle::moveLeft(int left)
  18. {
  19. if (rect.left() >= 2)
  20. rect.moveTo(left, rect.top());
  21. }
  22.  
  23. void Paddle::moveRight(int right)
  24. {
  25. if (rect.right() <= 298)
  26. rect.moveTo(right, rect.top());
  27. }
  28.  
  29. void Paddle::resetState()
  30. {
  31. rect.moveTo(200, 360);
  32. }
  33.  
  34. QRect Paddle::getRect()
  35. {
  36. return rect;
  37. }
  38.  
  39. QImage & Paddle::getImage()
  40. {
  41. return image;
  42. }
يمكن تحريك القاذف(العاكس) يمنى أو يسرى.
  1. Paddle::Paddle()
  2. {
  3. image.load("paddle.png");
  4.  
  5. rect = image.rect();
  6. resetState();
  7. }

في دالة البناء قمنا بتحميل صورة القاذف(العاكس) وحصلنا على مستطيل الصورة وقمنا بنقلها الى موقع المنتصف.
  1. void Paddle::moveLeft(int left)
  2. {
  3. if (rect.left() >= 2)
  4. rect.moveTo(left, rect.top());
  5. }

هذه الدالة لتحريك المستطيل الى جهة اليسار .
brick.h

  1. #ifndef BRICK_H
  2. #define BRICK_H
  3.  
  4. #include <QImage>
  5. #include <QRect>
  6.  
  7. class Brick
  8. {
  9.  
  10. public:
  11. Brick(int, int);
  12. ~Brick();
  13.  
  14. public:
  15. void resetState();
  16. bool isDestroyed();
  17. void setDestroyed(bool);
  18. QRect getRect();
  19. void setRect(QRect);
  20. QImage & getImage();
  21.  
  22. private:
  23. QImage image;
  24. QRect rect;
  25. int position;
  26. bool destroyed;
  27.  
  28. };
  29.  
  30. #endif

ملف الرأس هذا لكائن الطوبة .
brick.cpp

  1. #include "brick.h"
  2.  
  3.  
  4. Brick::Brick(int x, int y)
  5. {
  6. image.load("brickie.png");
  7. destroyed = FALSE;
  8. rect = image.rect();
  9. rect.translate(x, y);
  10. }
  11.  
  12. Brick::~Brick() {
  13.  
  14. printf("Brick deletedn");
  15. }
  16.  
  17. QRect Brick::getRect()
  18. {
  19. return rect;
  20. }
  21.  
  22. void Brick::setRect(QRect rct)
  23. {
  24. rect = rct;
  25. }
  26.  
  27. QImage & Brick::getImage()
  28. {
  29. return image;
  30. }
  31.  
  32. bool Brick::isDestroyed()
  33. {
  34. return destroyed;
  35. }
  36.  
  37. void Brick::setDestroyed(bool destr)
  38. {
  39. destroyed = destr;
  40. }
  41. bool Brick::isDestroyed()
  42. {
  43. return destroyed;
  44. }

لدى كائن الطوبة متغير destroyed فإذا كان المتغير يساوي true فهذا يعني أن الطوبة لن يتم رسمها على الشاشة .
ball.h

  1. #ifndef BALL_H
  2. #define BALL_H
  3.  
  4. #include <QImage>
  5. #include <QRect>
  6.  
  7. class Ball
  8. {
  9.  
  10. public:
  11. Ball();
  12. ~Ball();
  13.  
  14. public:
  15. void resetState();
  16. void moveBottom(int);
  17. void moveTop(int);
  18. void moveLeft(int);
  19. void moveRight(int);
  20. void autoMove();
  21. void setXDir(int);
  22. void setYDir(int);
  23. int getXDir();
  24. int getYDir();
  25. QRect getRect();
  26. QImage & getImage();
  27.  
  28. private:
  29. int angle;
  30. int speed;
  31. int xdir;
  32. int ydir;
  33. bool stuck;
  34. QImage image;
  35. QRect rect;
  36.  
  37. };
  38.  
  39. #endif.

ملف الرأس هذا لكائن الكرة .
ball.cpp

  1. #include "ball.h"
  2.  
  3.  
  4. Ball::Ball()
  5. {
  6.  
  7. xdir = 1;
  8. ydir = -1;
  9.  
  10. image.load("ball.png");
  11.  
  12. rect = image.rect();
  13. resetState();
  14.  
  15. }
  16.  
  17. Ball::~Ball() {
  18. printf("Ball deletedn");
  19. }
  20.  
  21.  
  22. void Ball::autoMove()
  23. {
  24. rect.translate(xdir, ydir);
  25.  
  26. if (rect.left() == 0) {
  27. xdir = 1;
  28. }
  29.  
  30. if (rect.right() == 300) {
  31. xdir = -1;
  32. }
  33.  
  34. if (rect.top() == 0) {
  35. ydir = 1;
  36. }
  37. }
  38.  
  39. void Ball::resetState()
  40. {
  41. rect.moveTo(230, 355);
  42. }
  43.  
  44. void Ball::moveBottom(int bottom)
  45. {
  46. rect.moveBottom(bottom);
  47. }
  48.  
  49. void Ball::moveTop(int top)
  50. {
  51. rect.moveTop(top);
  52. }
  53.  
  54. void Ball::moveLeft(int left)
  55. {
  56. rect.moveLeft(left);
  57. }
  58.  
  59. void Ball::moveRight(int right)
  60. {
  61. rect.moveRight(right);
  62. }
  63.  
  64. void Ball::setXDir(int x)
  65. {
  66. xdir = x;
  67. }
  68.  
  69. void Ball::setYDir(int y)
  70. {
  71. ydir = y;
  72. }
  73.  
  74. int Ball::getXDir()
  75. {
  76. return xdir;
  77. }
  78.  
  79. int Ball::getYDir()
  80. {
  81. return ydir;
  82. }
  83.  
  84. QRect Ball::getRect()
  85. {
  86. return rect;
  87. }
  88.  
  89. QImage & Ball::getImage()
  90. {
  91. return image;
  92. }

الدالة autoMove() تستدعى في كل دورة في اللعبة لتحريك الكرة على الشاشة اذا حدث إنصدام سوف يتغير إتجاه الكرة .
breakout.h

  1. #ifndef BREAKOUT_H
  2. #define BREAKOUT_H
  3.  
  4. #include "ball.h"
  5. #include "brick.h"
  6. #include "paddle.h"
  7. #include <QWidget>
  8. #include <QKeyEvent>
  9.  
  10. class Breakout : public QWidget
  11. {
  12. Q_OBJECT
  13.  
  14. public:
  15. Breakout(QWidget *parent = 0);
  16. ~Breakout();
  17.  
  18. protected:
  19. void paintEvent(QPaintEvent *event);
  20. void timerEvent(QTimerEvent *event);
  21. void keyPressEvent(QKeyEvent *event);
  22.  
  23. void startGame();
  24. void pauseGame();
  25. void stopGame();
  26. void victory();
  27. void checkCollision();
  28.  
  29. private:
  30. int x;
  31. int timerId;
  32. Ball *ball;
  33. Paddle *paddle;
  34. Brick * bricks[30];
  35. bool gameOver;
  36. bool gameWon;
  37. bool gameStarted;
  38. bool paused;
  39.  
  40. };
  41.  
  42. #endif

ملف الرأس هذا لكائن اللعبة تحطيم الطوبة .
  1. int x;
  2. int timerId;

المتغير x يخزن موقع القاذف(العاكس) على محور س. أما المتغير timerId فهو يستخدم ل تعريف كائن المؤقت وهي ضرورية أثناء وضع اللعبة في حالة إيقاف.
breakout.cpp

  1. #include "breakout.h"
  2. #include <QPainter>
  3. #include <QApplication>
  4.  
  5. Breakout::Breakout(QWidget *parent)
  6. : QWidget(parent)
  7. {
  8.  
  9. x = 0;
  10. gameOver = FALSE;
  11. gameWon = FALSE;
  12. paused = FALSE;
  13. gameStarted = FALSE;
  14. ball = new Ball();
  15. paddle = new Paddle();
  16.  
  17.  
  18. int k = 0;
  19. for (int i=0; i<5; i++) {
  20. for (int j=0; j<6; j++) {
  21. bricks[k] = new Brick(j*40+30, i*10+50);
  22. k++;
  23. }
  24. }
  25. }
  26.  
  27. Breakout::~Breakout() {
  28. delete ball;
  29. delete paddle;
  30. for (int i=0; i<30; i++) {
  31. delete bricks[i];
  32. }
  33. }
  34.  
  35. void Breakout::paintEvent(QPaintEvent *event)
  36. {
  37. QPainter painter(this);
  38.  
  39. if (gameOver) {
  40. QFont font("Courier", 15, QFont::DemiBold);
  41. QFontMetrics fm(font);
  42. int textWidth = fm.width("Game Over");
  43.  
  44. painter.setFont(font);
  45. int h = height();
  46. int w = width();
  47.  
  48. painter.translate(QPoint(w/2, h/2));
  49. painter.drawText(-textWidth/2, 0, "Game Over");
  50. }
  51. else if(gameWon) {
  52. QFont font("Courier", 15, QFont::DemiBold);
  53. QFontMetrics fm(font);
  54. int textWidth = fm.width("Victory");
  55.  
  56. painter.setFont(font);
  57. int h = height();
  58. int w = width();
  59.  
  60. painter.translate(QPoint(w/2, h/2));
  61. painter.drawText(-textWidth/2, 0, "Victory");
  62. }
  63. else {
  64. painter.drawImage(ball->getRect(),
  65. ball->getImage());
  66. painter.drawImage(paddle->getRect(),
  67. paddle->getImage());
  68.  
  69. for (int i=0; i<30; i++) {
  70. if (!bricks[i]->isDestroyed())
  71. painter.drawImage(bricks[i]->getRect(),
  72. bricks[i]->getImage());
  73. }
  74. }
  75. }
  76.  
  77. void Breakout::timerEvent(QTimerEvent *event)
  78. {
  79. ball->autoMove();
  80. checkCollision();
  81. repaint();
  82. }
  83.  
  84.  
  85.  
  86. void Breakout::keyPressEvent(QKeyEvent *event)
  87. {
  88. switch (event->key()) {
  89. case Qt::Key_Left:
  90. {
  91. int x = paddle->getRect().x();
  92. for (int i=1; i<=5; i++)
  93. paddle->moveLeft(x--);
  94. break;
  95. }
  96. case Qt::Key_Right:
  97. {
  98. int x = paddle->getRect().x();
  99. for (int i=1; i<=5; i++)
  100. paddle->moveRight(x++);
  101. }
  102. break;
  103. case Qt::Key_P:
  104. {
  105. pauseGame();
  106. }
  107. break;
  108. case Qt::Key_Space:
  109. {
  110. startGame();
  111. }
  112. break;
  113. case Qt::Key_Escape:
  114. {
  115. qApp->exit();
  116. }
  117. break;
  118. default:
  119. QWidget::keyPressEvent(event);
  120. }
  121. }
  122.  
  123. void Breakout::startGame()
  124. {
  125. if (!gameStarted) {
  126. ball->resetState();
  127. paddle->resetState();
  128.  
  129. for (int i=0; i<30; i++) {
  130. bricks[i]->setDestroyed(FALSE);
  131. }
  132. gameOver = FALSE;
  133. gameWon = FALSE;
  134. gameStarted = TRUE;
  135. timerId = startTimer(10);
  136. }
  137. }
  138.  
  139. void Breakout::pauseGame()
  140. {
  141. if (paused) {
  142. timerId = startTimer(10);
  143. paused = FALSE;
  144. } else {
  145. paused = TRUE;
  146. killTimer(timerId);
  147. }
  148. }
  149.  
  150. void Breakout::stopGame()
  151. {
  152. killTimer(timerId);
  153. gameOver = TRUE;
  154. gameStarted = FALSE;
  155. }
  156.  
  157. void Breakout::victory()
  158. {
  159. killTimer(timerId);
  160. gameWon = TRUE;
  161. gameStarted = FALSE;
  162. }
  163.  
  164. void Breakout::checkCollision()
  165. {
  166.  
  167. if (ball->getRect().bottom() > 400)
  168. stopGame();
  169.  
  170. for (int i=0, j=0; i<30; i++) {
  171. if (bricks[i]->isDestroyed()) {
  172. j++;
  173. }
  174. if (j==30)
  175. victory();
  176. }
  177.  
  178. if ((ball->getRect()).intersects(paddle->getRect())) {
  179.  
  180. int paddleLPos = paddle->getRect().left();
  181. int ballLPos = ball->getRect().left();
  182.  
  183. int first = paddleLPos + 8;
  184. int second = paddleLPos + 16;
  185. int third = paddleLPos + 24;
  186. int fourth = paddleLPos + 32;
  187.  
  188. if (ballLPos < first) {
  189. ball->setXDir(-1);
  190. ball->setYDir(-1);
  191. }
  192.  
  193. if (ballLPos >= first && ballLPos < second) {
  194. ball->setXDir(-1);
  195. ball->setYDir(-1*ball->getYDir());
  196. }
  197.  
  198. if (ballLPos >= second && ballLPos < third) {
  199. ball->setXDir(0);
  200. ball->setYDir(-1);
  201. }
  202.  
  203. if (ballLPos >= third && ballLPos < fourth) {
  204. ball->setXDir(1);
  205. ball->setYDir(-1*ball->getYDir());
  206. }
  207.  
  208. if (ballLPos > fourth) {
  209. ball->setXDir(1);
  210. ball->setYDir(-1);
  211. }
  212.  
  213.  
  214. }
  215.  
  216.  
  217. for (int i=0; i<30; i++) {
  218. if ((ball->getRect()).intersects(bricks[i]->getRect())) {
  219.  
  220. int ballLeft = ball->getRect().left();
  221. int ballHeight = ball->getRect().height();
  222. int ballWidth = ball->getRect().width();
  223. int ballTop = ball->getRect().top();
  224.  
  225. QPoint pointRight(ballLeft + ballWidth + 1, ballTop);
  226. QPoint pointLeft(ballLeft - 1, ballTop);
  227. QPoint pointTop(ballLeft, ballTop -1);
  228. QPoint pointBottom(ballLeft, ballTop + ballHeight + 1);
  229.  
  230. if (!bricks[i]->isDestroyed()) {
  231. if(bricks[i]->getRect().contains(pointRight)) {
  232. ball->setXDir(-1);
  233. }
  234.  
  235. else if(bricks[i]->getRect().contains(pointLeft)) {
  236. ball->setXDir(1);
  237. }
  238.  
  239. if(bricks[i]->getRect().contains(pointTop)) {
  240. ball->setYDir(1);
  241. }
  242.  
  243. else if(bricks[i]->getRect().contains(pointBottom)) {
  244. ball->setYDir(-1);
  245. }
  246.  
  247. bricks[i]->setDestroyed(TRUE);
  248. }
  249. }
  250. }
  251.  
  252. }

هنا مطق اللعبة.
  1. int k = 0;
  2. for (int i=0; i<5; i++) {
  3. for (int j=0; j<6; j++) {
  4. bricks[k] = new Brick(j*40+30, i*10+50);
  5. k++;
  6. }
  7. }

دالة بناء كائن لعبة تحطيم الطوبة ,قمنا بإنشاء 30 طوبة .
  1. painter.drawImage(ball->getRect(),
  2. ball->getImage());
  3. painter.drawImage(paddle->getRect(),
  4. paddle->getImage());
  5.  
  6. for (int i=0; i<30; i++) {
  7. if (!bricks[i]->isDestroyed())
  8. painter.drawImage(bricks[i]->getRect(),
  9. bricks[i]->getImage());
  10. }

اذا لم يحدث فوز أو خسارة يتم رسم الكرة وقاذف(عاكس) الكرة و مجموعة الطوب في paintEvent() .
  1. void Breakout::timerEvent(QTimerEvent *event)
  2. {
  3. ball->autoMove();
  4. checkCollision();
  5. repaint();
  6. }

في timerEvent() يتم تحريك الكرة والتأكد ما إذا أصطدمت الكرة مع القاذف(العاكس) او الطوبة ثم يتم إعادة رسم النافذة .
  1. case Qt::Key_Left:
  2. {
  3. int x = paddle->getRect().x();
  4. for (int i=1; i<=5; i++)
  5. paddle->moveLeft(x--);
  6. break;
  7. }

اذا تم الضغط على المفتاح left يتم إستدعاء الدالة moveLeft() من كائن القاذف(العاكس) يتم إستدعاء الدالة لخمسة مرات وبذلك الحركة تكون أكثرة واقعية (نعومة) .
  1. void Breakout::stopGame()
  2. {
  3. killTimer(timerId);
  4. gameOver = TRUE;
  5. gameStarted = FALSE;
  6. }

دالة stopGame() يتم حذف حدث المؤقت ويتم تغيير قيم المتغيرات للشكل المناسب.
  1. if (ball->getRect().bottom() > 400)
  2. stopGame();

اذا ما أصطدمت الكرة بالقاع يتم إيقاف اللعبة (إنهائها).
  1. for (int i=0, j=0; i<30; i++) {
  2. if (bricks[i]->isDestroyed()) {
  3. j++;
  4. }
  5. if (j==30)
  6. victory();
  7. }

يتم التأكد من عدد الطوب الذي تم تحطيمه اذا كان ماتم تحطيمه 30 طوبة فهذا دليل على الفوز .
i
  1. f (ballLPos < first) {
  2. ball->setXDir(-1);
  3. ball->setYDir(-1);
  4. }

اذا صدمت الكرة في الجزء الأعلى من القاذف(العاكس) يتم تغيير الإتجاه الى الشمال الشرقي.
  1. if(bricks[i]->getRect().contains(pointTop)) {
  2. ball->setYDir(1);
  3. }


اذا أصطدمت الكرة بقعر الطوبة يتم تغيير إتجاه الكرة لتتحرك نحو الأسفل.


الشكل: لعبة تحطيم الطوب .

إسم الكاتب تاريخ الإضافة التقييم / المقيمين زيارات الدرس
مصفوفة 21/01/2009 12 / 3 1872

الأكثر زيارة

 دليل تنصيب اطار عمل Qt ، حزمة MinGW ، بيئة التطوير QDevelop
الزيارات 2850 دليل تنصيب اطار عمل Qt ، حزمة MinGW ، بيئة التطوير QDevelop
 اعداد وتنصيب Qt
الزيارات 2627 اعداد وتنصيب Qt
 دليل تنصيب اطار عمل Qt ، حزمة MinGW ، بيئة التطوير Eclipse
الزيارات 2577 دليل تنصيب اطار عمل Qt ، حزمة MinGW ، بيئة التطوير Eclipse
 التطوير السريع للتطبيقات Rapid Application Development
الزيارات 2420 التطوير السريع للتطبيقات Rapid Application Development
 C++ In a Nutshell
الزيارات 2293 C++ In a Nutshell
 

الأكثر تصويتـا

 اعداد وتنصيب Qt
نتيجة التصويت 40 من 5 شخص اعداد وتنصيب Qt
 تخطيط البرامج Program Layout
نتيجة التصويت 40 من 6 شخص تخطيط البرامج Program Layout
 الدرس الاول : كتابة اول برنامج
نتيجة التصويت 29 من 3 شخص الدرس الاول : كتابة اول برنامج
 التطوير السريع للتطبيقات Rapid Application Development
نتيجة التصويت 29 من 4 شخص التطوير السريع للتطبيقات Rapid Application Development
 الدرس الثالث:تعريف slot جديدة
نتيجة التصويت 27 من 3 شخص الدرس الثالث:تعريف slot جديدة
 
 

سكربت story-script v1 برمجة bwady.com تطوير SudaNix