BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #43038同步于 2010/8/31
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

教你用Qt做“乒乓球”游戏

wks
2010/8/31镜像同步17 回复
这是Qt4版。 Qt sdk 2010.02.1版, Qt4.6 MinGW4.4 在裸机上测试过。 可执行文件72K 加上dll,压缩后接近3.9M p.s. C++的mangling很恶心。。。 源代码: 附件(8.5KB) pingpong_qt4-src.rar 二进制: windows版: http://bbs.byr.cn/att/CPP/36078/760
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
zzjin机器人#1 · 2010/8/31
力顶= =膜拜 表示LINUX下编译运行测试一次通过= = ubuntu10.04+GCC+QTSDK1.0 (PS:国球表示压力很大= =)
shenlei机器人#2 · 2010/8/31
赞... 【 在 wks (cloverprince) 的大作中提到: 】 : 这是Qt4版。 : Qt sdk 2010.02.1版, Qt4.6 : MinGW4.4 : ...................
wks机器人#3 · 2010/8/31
这是QtCreator开发环境 这是游戏画面
zzjin机器人#4 · 2010/8/31
【 在 wks 的大作中提到: 】 : 这是QtCreator开发环境 : [upload=1][/upload] : 这是游戏画面 : ................... 你自己写的代码?
wks机器人#5 · 2010/8/31
一个Qt工程呢,核心是一个.pro文件,描述一个工程里面的文件,使用了哪些Qt模块,等等。 我这个工程叫pingpong_qt4。所以,这个文件叫pingpong_qt4.pro # ------------------------------------------------- # Project created by QtCreator 2010-02-24T14:25:45 # ------------------------------------------------- TARGET = pingpong_qt4 TEMPLATE = app SOURCES += main.cpp \ mainwindow.cpp \ pingpong.c \ pingpongarea.cpp HEADERS += mainwindow.h \ pingpong.h \ pingpongarea.h FORMS += mainwindow.ui 看,TARGET指明了我们编译出来的程序叫pingpong_qt4,TEMPLATE表示工程的类型。SOURCES和HEADERS分别是工程里所有的源码和头文件。 FORMS嘛,表示有哪些对话框描述文件。这些文件是XML格式,会被编译成C++代码。 pingpong.ui里面没什么有意思的。为了满足你的好奇心,现在贴出来: <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>MainWindow</class> <widget class="QMainWindow" name="MainWindow"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>400</width> <height>400</height> </rect> </property> <property name="windowTitle"> <string>MainWindow</string> </property> <widget class="PingpongArea" name="centralWidget"/> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> <customwidget> <class>PingpongArea</class> <extends>QWidget</extends> <header>pingpongarea.h</header> <container>1</container> </customwidget> </customwidgets> <resources/> <connections/> </ui>
wks机器人#6 · 2010/8/31
背景历史可以参考这个:http://bbs.byr.cn/article/CPP/36048 【 在 zzjin 的大作中提到: 】 : : 这是QtCreator开发环境 : : [upload=1][/upload] : : 这是游戏画面 : ...................
zzjin机器人#7 · 2010/8/31
【 在 wks 的大作中提到: 】 : 背景历史可以参考这个:http://bbs.byr.cn/article/CPP/36048 : 【 在 zzjin 的大作中提到: 】 : : : 这是QtCreator开发环境 : ................... 大牛啊= =我还是回去种菜吧。。。
wks机器人#8 · 2010/8/31
乒乓球的核心逻辑是用C语言写的。 这是pingpong.h #ifndef __PINGPONG_H__ #define __PINGPONG_H__ #ifdef __cplusplus extern "C" { /* 这是为了应付C语言编译器和C++编译器的一点破事儿 */ #endif typedef struct _pingpong_ctx pingpong_ctx; typedef void* callback; typedef void timeout_handler_callback(pingpong_ctx* ctx); typedef void pingpong_timer_set_callback( int interval, timeout_handler_callback timeout_handler, pingpong_ctx* ctx, void* extra_parameter); typedef void pingpong_force_redraw_callback(pingpong_ctx* ctx); struct _pingpong_ctx { /* Model parts */ double bx, by; // Ball double bvx, bvy; // Ball velocity double p1_x, p2_x; // Player position }; /* 乒乓球所有的状态就这些了。 */ /* 一些常数 */ #define BORDER 0.5 #define DY 0.4 #define PAD_HALF_WIDTH 0.1 #define PAD_HALF_THICK 0.01 #define BALL_RADIUS 0.02 /* 一些函数的原型 */ pingpong_ctx* pingpong_new(void); void pingpong_free(pingpong_ctx* ctx); void pingpong_reset_model(pingpong_ctx* ctx); void pingpong_one_frame(pingpong_ctx* ctx); void pingpong_ai_react(pingpong_ctx* ctx); void pingpong_mouse_move(pingpong_ctx* ctx, double x); #ifdef __cplusplus } #endif #endif 然后,是pingpong.c。这是C语言程序 #include "pingpong.h" #include <math.h> #include <stdlib.h> void on_timeout(pingpong_ctx* ctx); /* Constructors/Destructors */ /* 创建和删除数据结构 */ pingpong_ctx* pingpong_new(void) { pingpong_ctx* ctx = (pingpong_ctx*)malloc(sizeof(pingpong_ctx)); return ctx; } void pingpong_free(pingpong_ctx* ctx) { free(ctx); } /* functions */ /* 还原 */ void pingpong_reset_model(pingpong_ctx* ctx) { ctx->bx = ctx->by = 0.0; ctx->bvx = ctx->bvy = 0.01; ctx->p1_x = ctx->p2_x = 0.0; } /* 乒乓球是一帧一帧模拟的。每一帧,这个函数会更新乒乓球的位置 */ void pingpong_one_frame(pingpong_ctx* ctx) { double nbx,nby; nbx = ctx->bx + ctx->bvx; nby = ctx->by + ctx->bvy; if(ctx->bvx>0.0 && nbx > BORDER) { ctx->bvx = -ctx->bvx; nbx = BORDER*2.0 - nbx; } else if(ctx->bvx<0.0 && nbx < -BORDER) { ctx->bvx = -ctx->bvx; nbx = -BORDER*2.0 - nbx; } if(ctx->bvy>0.0 && ctx->by < DY && nby >= DY && fabs((ctx->bx+nbx)/2.0 - ctx->p1_x)<PAD_HALF_WIDTH) { ctx->bvy = -ctx->bvy; nby = DY * 2.0 - nby; } else if (ctx->bvy<0.0 && ctx->by > -DY && nby <= -DY && fabs((ctx->bx+nbx)/2.0 - ctx->p2_x)<PAD_HALF_WIDTH) { ctx->bvy = -ctx->bvy; nby = -DY * 2.0 - nby; } if(nby > BORDER || nby < -BORDER) { pingpong_reset_model(ctx); } else { ctx->bx = nbx; ctx->by = nby; } } /* 一个简单的电脑玩家。这是完美玩家,总是跟着球,永远不会输 */ void pingpong_ai_react(pingpong_ctx* ctx) { ctx->p2_x = ctx->bx; } /* 处理玩家的移动,移动玩家的拍子 */ void pingpong_mouse_move(pingpong_ctx* ctx, double x) { ctx->p1_x = x; }
wks机器人#9 · 2010/8/31
乒乓球逻辑可以暂且忽略。图形界面才是Qt的重点。 Qt中,一个窗口表示为一个QMainWindow类的实例。这里我们定义这样的一个主窗口。 这是mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QTimer> #include "pingpong.h" namespace Ui { class MainWindow; // 这个类是QtCreater根据mainwindow.ui自动生成的,不必太在意。定义在另一个文件里,不重要。 } class MainWindow : public QMainWindow { // 继承自窗口类 Q_OBJECT // 凡是Qt的类,都有这个宏。Qt程序中,所有的cpp代码都需要预处理,增加“属性”和“事件”这两个C++语言并没有提供的特性。 public: MainWindow(QWidget *parent = 0); ~MainWindow(); protected: void changeEvent(QEvent *e); // 这个自动生成的不重要 virtual void paintEvent(QPaintEvent *event); // 重绘窗口。这是图形的核心 private: Ui::MainWindow *ui; // 里层窗口布局 pingpong_ctx* ctx; // 乒乓球核心逻辑 QTimer *frameTimer; // 帧定时器 friend class PingpongArea; private slots: // slot不是标准C++的语法,这由Qt的MOC预处理器处理,转换成标准的C++。 void onTimeout(); // 这个函数被事件回调,所以定义成slot。每过若干毫秒,这个函数将被调用。 }; #endif // MAINWINDOW_H 下面是mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include "pingpong.h" #include <QPainter> #include <QMouseEvent> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); this->ctx = pingpong_new(); // 主窗口创建时,创建乒乓球核心逻辑 pingpong_reset_model(this->ctx); // 初始化 this->frameTimer = new QTimer(this); // 定时器 connect(this->frameTimer, SIGNAL(timeout()), this, SLOT(onTimeout())); // 连接定时器的“超时“信号以及处理函数 this->frameTimer->start(10); // 定时器超时时间是10毫秒。 } MainWindow::~MainWindow() { pingpong_free(this->ctx); // 主窗口关闭时,释放核心逻辑 delete ui; } void MainWindow::changeEvent(QEvent *e) { QMainWindow::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } } inline int P2W(double pingpongValue, int width) { // 逻辑到物理坐标转换 return int((pingpongValue+0.5) * double(width)); } void MainWindow::paintEvent(QPaintEvent *event) { // 这里是重要的画图部分 QPainter painter(this); int width = this->width(); int height = this->height(); painter.scale(width, height); // 将画布移动、伸展, painter.translate(0.5, 0.5); // 这样,整个窗口的逻辑大小就是1.0*1.0,坐标中心在窗口中心。 painter.setPen(Qt::NoPen); // 选择画轮廓用的”笔“ // Background painter.setBrush(Qt::black); // 选择填充用的”刷“ painter.drawRect(QRectF(-0.5,-0.5,1.0,1.0)); // Foreground painter.setBrush(Qt::white); // P1 画玩家的乒乓球拍 painter.drawRect(QRectF( ctx->p1_x - PAD_HALF_WIDTH, DY - PAD_HALF_THICK, PAD_HALF_WIDTH * 2, PAD_HALF_THICK * 2 )); // P2 画电脑的乒乓球拍 painter.drawRect(QRectF( ctx->p2_x - PAD_HALF_WIDTH, -DY - PAD_HALF_THICK, PAD_HALF_WIDTH * 2, PAD_HALF_THICK * 2 )); // Ball 画球 painter.drawEllipse(QPointF(ctx->bx, ctx->by), BALL_RADIUS, BALL_RADIUS); } void MainWindow::onTimeout() // 每10毫秒调用一次 { pingpong_one_frame(ctx); // 让核心逻辑模拟1帧内球的移动 pingpong_ai_react(ctx); // 模拟电脑的反映 this->repaint(); // 重画窗口 }