返回信息流这是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
这是一条镜像帖。来源:北邮人论坛 / cpp / #43038同步于 2010/8/31
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖
教你用Qt做“乒乓球”游戏
wks
2010/8/31镜像同步17 回复
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
赞...
【 在 wks (cloverprince) 的大作中提到: 】
: 这是Qt4版。
: Qt sdk 2010.02.1版, Qt4.6
: MinGW4.4
: ...................
【 在 wks 的大作中提到: 】
: 这是QtCreator开发环境
: [upload=1][/upload]
: 这是游戏画面
: ...................
你自己写的代码?
一个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>
背景历史可以参考这个:http://bbs.byr.cn/article/CPP/36048
【 在 zzjin 的大作中提到: 】
: : 这是QtCreator开发环境
: : [upload=1][/upload]
: : 这是游戏画面
: ...................
【 在 wks 的大作中提到: 】
: 背景历史可以参考这个:http://bbs.byr.cn/article/CPP/36048
: 【 在 zzjin 的大作中提到: 】
: : : 这是QtCreator开发环境
: ...................
大牛啊= =我还是回去种菜吧。。。
乒乓球的核心逻辑是用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;
}
乒乓球逻辑可以暂且忽略。图形界面才是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(); // 重画窗口
}