今回は、編集者がQtを使って簡単なバックギャモンゲームを実装する方法について、プロの視点から分析・解説した充実した内容となっておりますので、読んで何かを感じていただければ幸いです。
次の図は、ゲームのメイン ウィンドウ ページを示しています。
この記事の特典として、Qt 開発学習教材パッケージ、技術ビデオ (C++ 言語基礎、C++ デザイン パターン、Qt プログラミング入門、QT シグナルとスロット メカニズム、QT インターフェイス開発イメージ描画、QT ネットワーク、QT など) を無料で受け取ることができます。データベースプログラミング、QTプロジェクト実戦、QSS、OpenCV、Quickモジュール、面接での質問など) ↓↓↓↓下記参照↓↓料金受け取りは記事下部をクリック↓↓
最初のステップ:ウィンドウ描画の実装 (QPaintEvent 描画イベントと QMouseEvent マウス イベント)
①マウスイベント(ここではmouseDoubleClickEvent()ダブルクリックイベント)
void GamePage::mouseDoubleClickEvent(QMouseEvent *event)//鼠标双击事件
{
m_dx = event->x();
m_dy = event->y();
//避免乱点时存入坐标 需添加:标志符--》game状态 坐标的界限(点)
if(m_dx < POINT_X_MAX && m_dy < POINT_Y_MAX && m_bRunState == true)
{
//如果点在交叉点周围则设置点在交叉点上(判断点位置)
QPointF newPoint(gainPointPosition(QPointF(m_dx,m_dy)));
if(!m_VectorRedPoint.contains(newPoint) &&
!m_VectorBlackPoint.contains(newPoint))//判断点是否已经存在
{
if(m_iFlagWho == 0)//红棋
{
m_VectorRedPoint.append(newPoint);
m_iFlagWho = 1;
}
else//黑棋
{
m_VectorBlackPoint.append(newPoint);
m_iFlagWho = 0;
}
}
}
}
ここで、ウィンドウグリッドマップはマウスのダブルクリックによる座標の直接描画と選択によってチェスの駒を保存および描画するため、2つの線の交差点になるように点に設定位置関数が実行されます。以下に続きます:
QPointF GamePage::gainPointPosition(QPointF srcPoint)//返回一个处于格子两线交接处的坐标点
{
QPointF tmp;
for(int i = 0;i < 12;i++)
{
if(srcPoint.x() >= 50*i && srcPoint.x() <= (50*i+25))//X判断
{
tmp.setX(50*i);//如果处于50*i ~ 50*i+25)之间则设置点坐标点为50*i
}
else if (srcPoint.x() >= (50*i + 25) && srcPoint.x() <= 50*(i+1))
{
tmp.setX(50*(i+1));//如果处于50*i+25 ~ 50*(i+1)之间则设置点坐标点为50*(i+1)
}
if(srcPoint.y() >= 50*i && srcPoint.y() <= (50*i+25))//Y判断
{
tmp.setY(50*i);//同上
}
else if (srcPoint.y() >= (50*i + 25) && srcPoint.y() <= 50*(i+1))
{
tmp.setY(50*(i+1));//同上
}
}
return tmp;
}
②描画イベント(主に方眼グラフ、黒チェス、赤チェスの描画)
チェスの駒の座標の保存は主に QVector コンテナを通じて実現され、コンテナは反復的に描画されます。実装コードは次のとおりです。
void GamePage::paintEvent(QPaintEvent *event)//绘画事件
{
QPainter *pater = new QPainter(this);
pater->begin(this);
//网格图
pater->setPen(Qt::black);
for(int i = 0;i <= 12;i++)
{
pater->drawLine(0,50*i,600,50*i);
pater->drawLine(50*i,0,50*i,600);
}
//红色棋绘画
QVector<QPointF>::iterator iter;
for(iter = m_VectorRedPoint.begin();iter != m_VectorRedPoint.end();iter++)
{
pater->setBrush(QBrush(Qt::red, Qt::SolidPattern));
pater->setPen(Qt::red);
pater->drawEllipse(*iter,15,15);
}
//黑色棋绘画
QVector<QPointF>::iterator iter1;
for(iter1 = m_VectorBlackPoint.begin();iter1 != m_VectorBlackPoint.end();iter1++)
{
pater->setBrush(QBrush(Qt::black, Qt::SolidPattern));
pater->setPen(Qt::black);
pater->drawEllipse(*iter1,15,15);
}
pater->end();
update();
}
ステップ2:勝敗の計算
上図は計算関係ルールを示しており、次のコードは 3 つの異なる方向の計算を実現するために使用されます。
①横型
bool GamePage::checkXPointF(QVector<QPointF> vector) //检查X轴方向的
{
int num_L= 1;
int num_R = 1;
QVector<QPointF>::iterator iter;
QVector<QPointF>::iterator itertmp;
for(iter = vector.begin();iter != vector.end();iter++)
{
QPointF tmp = *iter;
for(int k = 1;k < 5;k++)//左方向的查找
{
for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
{
qDebug()<<*itertmp<<"X compare"<<tmp;
if((*itertmp).x() - tmp.x() == k*50)
{
num_L ++;
}
}
//qDebug()<<"count:"<<num;
if(num_L == k+1)//寻找过程中找到几个点相连
{
if(num_L == 5)
{
return true;
}
}
else
{
break;
}
}
for(int k = 1;k < 5;k++)//右方向的查找
{
for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
{
qDebug()<<*itertmp<<"X compare"<<tmp;
if((*itertmp).x() - tmp.x() == -k*50)
{
num_R ++;
}
}
//qDebug()<<"count:"<<num;
if(num_R == k+1)//寻找过程中找到几个点相连
{
if(num_R == 5)
{
return true;
}
}
else
{
break;
}
}
if(num_R + num_L == 5+1)//5+1 因为左右方向都是从1开始计算 重复了原点tmp坐标
{
return true;
}
else
{
num_R = 1;
num_L = 1;
}
}
return false;
}
②縦(横と同じ)
bool GamePage::checkYPointF(QVector<QPointF> vector)
{
qDebug()<<"enter Y***************";
int num_U = 1;
int num_D = 1;
QVector<QPointF>::iterator iter;
QVector<QPointF>::iterator itertmp;
for(iter = vector.begin();iter != vector.end();iter++)
{
QPointF tmp = *iter;
for(int k = 1;k < 5;k++)//上
{
for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
{
qDebug()<<*itertmp<<"Y compare"<<tmp;
if((*itertmp).y() - tmp.y() == k*50)
{
num_U ++;
}
}
qDebug()<<"num_U:"<<num_U;
if(num_U == k+1)//寻找过程中找到几个点相连
{
if(num_U == 5)
{
return true;
}
}else{break;}
}
for(int k = 1;k < 5;k++)//下
{
for(itertmp = vector.begin();itertmp != vector.end();itertmp++)
{
qDebug()<<*itertmp<<"Y compare"<<tmp;
if((*itertmp).y() - tmp.y() == -k*50)
{
num_D ++;
}
}
qDebug()<<"num_D:"<<num_D;
if(num_D == k+1)//寻找过程中找到几个点相连
{
if(num_D == 5)
{
return true;
}
}else{break;}
}
if(num_D + num_U == 5 + 1)//减去一个
{
return true;
}
else
{
num_D = 1;
num_U= 1;
}
}
return false;
}
③斜め方向(上図より、座標系を例に4象限に分けて計算・カウントし、要件を満たしているかを判定します)
int GamePage::findSeriesPointF(bool flag, QPointF tmp, QVector<QPointF> vector)
{
bool flag_iter = false;
int forward_count = 1;//一象限的数量
int reverse_count = 1;
int forward_count2 = 1;
int reverse_count2 = 1;
QVector<QPointF>::iterator iter= vector.begin();
while(iter != vector.end())
{
qDebug()<<*iter<<"compare"<<tmp;
switch(forward_count)//一象限
{
case 1:
if((*iter).x() - tmp.x() == 50 && (*iter).y() - tmp.y() == -50)
{
forward_count ++;
flag_iter = true;
}
break;
case 2:
if((*iter).x() - tmp.x() == 50*forward_count && (*iter).y() - tmp.y() == -50*forward_count)
{
forward_count++;
flag_iter = true;
}
break;
case 3:
if((*iter).x() - tmp.x() == 50*forward_count && (*iter).y() - tmp.y() == -50*forward_count)
{
forward_count++;
flag_iter = true;
}
break;
case 4:
if((*iter).x() - tmp.x() == 50*forward_count && (*iter).y() - tmp.y() == -50*forward_count)
{
forward_count++;
flag_iter = true;
}
break;
}
switch(reverse_count)//三象限
{
case 1:
if((*iter).x() - tmp.x() == -50 && (*iter).y() - tmp.y() == 50)
{
reverse_count=2;
flag_iter = true;
}
break;
case 2:
if((*iter).x() - tmp.x() == -50*reverse_count && (*iter).y() - tmp.y() == 50*reverse_count)
{
reverse_count++;
flag_iter = true;
}
break;
case 3:
if((*iter).x() - tmp.x() == -50*reverse_count && (*iter).y() - tmp.y() == 50*reverse_count)
{
reverse_count++;
flag_iter = true;
}
break;
case 4:
if((*iter).x() - tmp.x() == -50*reverse_count && (*iter).y() - tmp.y() == 50*reverse_count)
{
reverse_count++;
flag_iter = true;
}
break;
}
qDebug()<<forward_count<<"+"<<reverse_count;
if(forward_count + reverse_count == 6)//未加上点本身
{
return 5;
}
switch(forward_count2)//2象限
{
case 1:
if((*iter).x() - tmp.x() == -50 && (*iter).y() - tmp.y() == -50)
{
forward_count2++;
flag_iter = true;
}
break;
case 2:
if((*iter).x() - tmp.x() == -50*forward_count2 && (*iter).y() - tmp.y() == -50*forward_count2)
{
forward_count2++;
flag_iter = true;
}
break;
case 3:
if((*iter).x() - tmp.x() == -50*forward_count2 && (*iter).y() - tmp.y() == -50*forward_count2)
{
forward_count2++;
flag_iter = true;
}
break;
case 4:
if((*iter).x() - tmp.x() == -50*forward_count2 && (*iter).y() - tmp.y() == -50*forward_count2)
{
forward_count2++;
flag_iter = true;
}
break;
}
switch(reverse_count2)//4象限
{
case 1:
if((*iter).x() - tmp.x() == 50 && (*iter).y() - tmp.y() == 50)
{
reverse_count2++;
flag_iter = true;
}
break;
case 2:
if((*iter).x() - tmp.x() == 50*reverse_count2 && (*iter).y() - tmp.y() == 50*reverse_count2)
{
reverse_count2++;
flag_iter = true;
}
break;
case 3:
if((*iter).x() - tmp.x() == 50*reverse_count2 && (*iter).y() - tmp.y() == 50*reverse_count2)
{
reverse_count2++;
flag_iter = true;
}
break;
case 4:
if((*iter).x() - tmp.x() == 50*reverse_count2 && (*iter).y() - tmp.y() == 50*reverse_count2)
{
reverse_count2++;
flag_iter = true;
}
break;
}
qDebug()<<forward_count2<<"+"<<reverse_count2;
if(forward_count2 + reverse_count2 == 6)//未加上点本身
{
return 5;
}
if(flag_iter)
{
iter = vector.begin();//目的是返回首个点,重头存货在后 不错过
flag_iter = false;
}
else {
iter++;
}
}
return 0;
}
上記水平、垂直、斜めの3方向の演算は、最も単純で理解しやすいアルゴリズムで実現される。
④タイマーによる赤旗・黒旗の定期検査機能を実現
void GamePage::slotCheckWhetherWin()//定时器检查是否输赢功能
{
m_pVerVector.clear();
m_pVerVectorB.clear();
m_pHerVector.clear();
m_pHerVectorB.clear();
QVector<QPointF>::iterator iterRed;
for(iterRed = m_VectorRedPoint.begin();iterRed != m_VectorRedPoint.end();iterRed++)
{
qDebug()<<*iterRed;
}
QVector<QPointF> tmpRed = m_VectorRedPoint;
//红棋判断
if(m_VectorRedPoint.size() >= 5)
{
for(iterRed = m_VectorRedPoint.begin();iterRed != m_VectorRedPoint.end();iterRed++)
{
QPointF tmp = *iterRed;//获取第一个点
qDebug()<<"tmp:"<<tmp;
QVector<QPointF>::iterator itertmp;
for(itertmp = tmpRed.begin();itertmp != tmpRed.end();itertmp++)
{
qDebug()<<"tmpRed:"<<*itertmp;
//横向连续5个点
if((*itertmp).y() - tmp.y() >= -0.000001 && (*itertmp).y() - tmp.y() <= 0.000001)//先判断y是同一坐标
{
m_pHerVector.append(*itertmp);
}
//纵向连续5个点
if((*itertmp).x() - tmp.x() >= -0.000001 && (*itertmp).x() - tmp.x() <= 0.000001)//先判断y是同一坐标
{
m_pVerVector.append(*itertmp);
}
}
//对容器进行操作
if(checkXPointF(m_pHerVector) || checkYPointF(m_pVerVector))
{
QMessageBox::warning(nullptr,"warning","红方XY赢了!");
m_ptimer->stop();
return;
}
else
{
m_pHerVector.clear();//清空
m_pVerVector.clear();//清空
count = 0;
}
//其他都是斜向
if(findSeriesPointF(true,tmp,m_VectorRedPoint) == 5)
{
QMessageBox::warning(nullptr,"warning","红方斜线赢了!");
m_ptimer->stop();
return;
}
}
}
//黑棋判断
QVector<QPointF>::iterator iterBlack;
QVector<QPointF> tmpBlack = m_VectorBlackPoint;
if(m_VectorBlackPoint.size() >= 5)
{
for(iterBlack = m_VectorBlackPoint.begin();iterBlack != m_VectorBlackPoint.end();iterBlack++)
{
QPointF tmp = *iterBlack;//获取第一个点
qDebug()<<"tmp:"<<tmp;
QVector<QPointF>::iterator itertmp;
for(itertmp = tmpBlack.begin();itertmp != tmpBlack.end();itertmp++)//正向
{
qDebug()<<"tmpRed:"<<*itertmp;
//横向连续5个点
if((*itertmp).y() - tmp.y() >= -0.000001 && (*itertmp).y() - tmp.y() <= 0.000001)//先判断y是同一坐标
{
m_pHerVectorB.append(*itertmp);
}
//纵向连续5个点
if((*itertmp).x() - tmp.x() >= -0.000001 && (*itertmp).x() - tmp.x() <= 0.000001)//先判断y是同一坐标
{
m_pVerVectorB.append(*itertmp);
}
}
//对容器进行操作
if(checkXPointF(m_pHerVectorB) || checkYPointF(m_pVerVectorB))
{
QMessageBox::warning(nullptr,"warning","黑方XY赢了!");
m_ptimer->stop();
return;
}
else
{
m_pHerVectorB.clear();//清空
m_pVerVectorB.clear();//清空
count = 0;
}
//其他都是斜向
if(findSeriesPointF(true,tmp,m_VectorBlackPoint) == 5)
{
QMessageBox::warning(nullptr,"warning","黑方斜线赢了!");
m_ptimer->stop();
return;
}
}
}
}
この記事の特典として、Qt 開発学習教材パッケージ、技術ビデオ (C++ 言語基礎、C++ デザイン パターン、Qt プログラミング入門、QT シグナルとスロット メカニズム、QT インターフェイス開発イメージ描画、QT ネットワーク、QT など) を無料で受け取ることができます。データベースプログラミング、QTプロジェクト実戦、QSS、OpenCV、Quickモジュール、面接での質問など) ↓↓↓↓下記参照↓↓料金受け取りは記事下部をクリック↓↓