Qt象棋游戏开发:如何用QPainter绘制专业级棋盘(附完整代码)
很多Qt开发者在掌握了基础控件和布局后,都想挑战一些更具表现力的项目,比如游戏开发。象棋,作为一款经典的策略游戏,其棋盘绘制看似简单,实则是对Qt绘图系统理解深度的一次绝佳检验。它不像俄罗斯方块那样依赖动态刷新,也不像3D游戏那样复杂,但它要求精确的几何计算、美观的视觉呈现和良好的代码组织。如果你已经熟悉了Qt的信号槽和基本窗口部件,那么通过亲手绘制一个象棋棋盘,你将能深刻体会到QPainter这个二维绘图引擎的强大与灵活。这不仅仅是画几条线,更是学习如何将数学逻辑、美术审美与代码架构融合的过程。接下来,我将以一个完整的、可复用的项目视角,带你从零开始,用QPainter打造一个不仅功能正确,而且视觉效果专业的象棋棋盘。
1. 项目架构与核心类设计
在动手写第一行绘制代码之前,花点时间思考整体架构是值得的。一个混乱的paintEvent里塞满所有绘制逻辑,虽然能跑,但后期维护和功能扩展会成为噩梦。我们的目标是构建一个清晰、可扩展的棋盘组件。
首先,我们创建一个名为ChessBoardWidget的自定义QWidget类。这个类将专门负责棋盘的视觉呈现。与简单地在主窗口上直接绘制不同,将其独立为组件,有利于实现模块化。例如,未来你可以轻松地将这个棋盘组件嵌入到任何界面中,或者为其添加不同的主题皮肤。
在头文件chessboardwidget.h中,我们需要进行一些关键声明:
#ifndef CHESSBOARDWIDGET_H
#define CHESSBOARDWIDGET_H
#include <QWidget>
#include <QPen>
#include <QBrush>
#include <QColor>
#include <QLinearGradient>
class ChessBoardWidget : public QWidget
{
Q_OBJECT
public:
explicit ChessBoardWidget(QWidget *parent = nullptr);
// 可公开设置的属性
void setBoardSize(int size); // 设置棋盘格数(默认9x10)
void setCellSize(int size); // 设置每个格子的像素尺寸
void setTheme(const QString &themeName); // 设置主题(如“古典”、“木质”)
protected:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override; // 用于自适应调整
private:
// 核心绘制方法分解
void drawBoardBackground(QPainter &painter);
void drawGridLines(QPainter &painter);
void drawSpecialMarkings(QPainter &painter); // “楚河汉界”、九宫、兵炮位
void drawDecoration(QPainter &painter); // 装饰性元素,如角落花纹
// 计算辅助函数
QPointF cellCenter(int row, int col) const; // 计算格子中心坐标
QRectF cellRect(int row, int col) const; // 计算格子矩形区域
// 成员变量
int m_boardRows; // 行数(10)
int m_boardCols; // 列数(9)
int m_cellSize; // 格子边长(像素)
int m_boardMargin; // 棋盘边距
QColor m_lineColor;
QColor m_bgColor1; // 背景色1(如浅色)
QColor m_bgColor2; // 背景色2(如深色,用于渐变或交替)
QLinearGradient m_bgGradient; // 背景渐变
// 预计算的几何点,避免在paintEvent中重复计算
QVector<QLineF> m_gridLines;
QVector<QPointF> m_starPoints; // “米”字格星位点
void calculateGeometry(); // 当尺寸变化时,重新计算所有几何信息
};
#endif // CHESSBOARDWIDGET_H
这种设计将庞大的paintEvent分解为多个语义清晰的私有方法,每个方法负责一个独立的视觉层。calculateGeometry函数是关键,它会在棋盘尺寸变化时,预先计算好所有线条的起点终点、特殊标记的位置,并将它们存储起来。在paintEvent中,我们只需要遍历这些预计算好的几何元素进行绘制,这比每次都实时计算要高效得多,尤其是在需要频繁重绘或棋盘尺寸较大时。
注意:将绘制逻辑分解并预计算几何数据,是提升复杂自定义部件绘制性能的常用技巧。它遵循了“计算与渲染分离”的原则。
2. 绘制基础网格与视觉优化
棋盘的核心是横十竖九的网格线。绘制直线本身很简单,但要让线条看起来专业、清晰且美观,就需要在细节上下功夫。我们首先在构造函数中初始化一些默认参数。
ChessBoardWidget::ChessBoardWidget(QWidget *parent)
: QWidget(parent)
, m_boardRows(10)
, m_boardCols(9)
, m_cellSize(60)
, m_boardMargin(40)
{
// 初始化默认颜色主题
m_lineColor = QColor(0x2C, 0x2C, 0x2C); // 深灰色线条,比纯黑更柔和
m_bgColor1 = QColor(0xF0, 0xE5, 0xD6); // 米黄色背景
m_bgColor2 = QColor(0xE8, 0xD0, 0xB5); // 稍深的米黄,用于渐变
// 设置背景渐变
m_bgGradient.setStart(0, 0);
m_bgGradient.setFinalStop(width(), height());
m_bgGradient.setColorAt(0.0, m_bgColor1);
m_bgGradient.setColorAt(1.0, m_bgColor2);
// 设置组件最小尺寸,并启用反锯齿
setMinimumSize(m_boardCols * m_cellSize + 2 * m_boardMargin,
m_boardRows * m_cellSize + 2 * m_boardMargin);
setAttribute(Qt::WA_OpaquePaintEvent); // 告知系统此部件完全不透明,可优化绘制
calculateGeometry(); // 初始计算几何数据
}
接下来是calculateGeometry<

705

被折叠的 条评论
为什么被折叠?



