贪吃蛇

    xiaoxiao2021-03-25  114

    这里主要是食物的防止和蛇的游走,蛇我用QGraphicsPathItem, 然后用QList<QPointF> snake来确定蛇身体坐标 场景坐标为400X400,左上角定点坐标为(0,0),右下角点坐标为(400,400) 每个格子都占据20X20的QRectF,蛇身的坐标为格子的中心 距离场景最左上角的格子坐标(0,0,20,20),如果蛇头位于这个格子 则有*(snake.begin()) == QPointF(10,10) 即蛇身体所处格子的坐标为 (格子场景坐标.x()+10,格子场景坐标.y()+10) 另外在写代码的时候需要很多变量,一般来说使用枚举会直观性,但由于很多变量需要计算,而且我在写代码是经常遇到需要额外加一些,所以我这里使用了int +注释的形式 比如代表蛇运动方向,一般可能会 emun {left,up,down,right};类似这样 我在代码里是 int turns; //这里turns代表蛇的方向,取值-1=左,1=右,-2=上,2=下 这种形式

    食物的坐标同

    开始的时候蛇和食物位置固定,在游戏菜单中, 新游戏:建立一个新游戏 开始:游戏开始(蛇开始运动) 暂停:游戏暂停(蛇停止运动); 开始和暂停选项互斥; 在选项一栏里可以调整游戏选项(蛇的运动间隔,有0.9秒,0.6秒,0.3秒)蛇身体有3个颜色可选 这里当点击 新游戏 后可以通过点击 难度 来调整游戏,当点击开始后,该 难度选项为不可用状态(无论暂停还是开始),只有再次 新游戏 后才能再次可用,这里避免了扫雷游戏中途调整游戏难度导致游戏问题   撞墙或吃到自己游戏判负 游戏胜利的条件是吃满10个食物 其实这蛇在装到强或吃到自己是都会发生一些形状上的变化 ,但由于此时游戏判断结束,所以我用图片掩盖这些 其实2个图片游戏启动时就已经位于场景中,但位于QgraphicsView之外,这里通过信号与槽把图片移动坐标(到view)内,新建游戏时在把他们移到view之外 这里是蛇围绕这食物  这2个图说明碰撞判断符合要求(其实这里蛇环绕食物的碰撞有些问题,所以我使用了坐标碰撞而不是QGraphicsItem的碰撞函数 ) ~~~~ http://pan.baidu.com/s/1o6upbHg 注意这里文件是我在linux(ubuntu14.04)下写的文件,在win系统下编译没问题,但打开可能会出现乱码.如果有这里问题,下面有源代码(PS:源代码里没有.pro文件的源代码) 这里pro文件写好了,编译的时候记得打开编译器的c++11选择(如果你没开的话) ~~~~~以下是源代码~~~~~~

    GameMap.h

    #ifndef GAMEMAP_H_ #define GAMEMAP_H_ #include<QDialog> #include<QPointF> #include<QGraphicsView> #include<QGraphicsScene> #include<QGraphicsRectItem> #include<QGraphicsPathItem> //用于蛇以及周围的墙 #include<QGraphicsEllipseItem> //用于食物 #include<QGraphicsPixmapItem> #include<QTimer> #include<QKeyEvent> class GameMap:public QDialog { Q_OBJECT private: QGraphicsScene* scene; QGraphicsView* view; QGraphicsPixmapItem* winPix; QGraphicsPixmapItem* lostPix; QGraphicsRectItem* upWalls; //用于四周的墙 QGraphicsRectItem* downWalls; QGraphicsRectItem* leftWalls; QGraphicsRectItem* rightWalls; QGraphicsRectItem* food; //食物 qreal food_x; qreal food_y; //这2个坐标存放食物在地图上的坐标,用于蛇吃食物的碰撞判断 bool hasFood; //表示地图上是否有食物 QGraphicsPathItem* snake; //蛇 QList<QPointF> RunPath; //蛇运动时的坐标 QPointF snakeTail; //储存上次运动的尾部,用于吃到食物后蛇身体(在尾部)加一格 QPainterPath snakePath; //蛇自身的坐标 QTimer* times; //时间控制器 int keepGo; //游戏状态,0=可以继续,1=撞墙/吃到自己失败,2=吃满食物胜利 int getFood; //食物数量,吃到10个为胜利 int levels; //通过控制时间长短来控制游戏难度 int color; //定义蛇的颜色,1=白,2=蓝,3=红 int turns; //输入指令的方向 1=上,-1= 下 -2=左 2=右 int running; //蛇正在运动的方向,数值同turns,确保蛇不会反方向运动 void forPath(); //将运动坐标转化为蛇身坐标 protected: void keyPressEvent(QKeyEvent* event); //点击事件,响应键盘的A W S D public: GameMap(QWidget* parent = 0); //构造函数 public slots: void newGame(); //新建游戏(重置蛇,食物等); void startGame(); void stopGame(); //停止游戏,用于蛇吃到自己或撞墙 void newOption(int,int); private slots: void keepRun(); //蛇运动 void giveFood(); //放置一个新的食物 void eating(); //这个函数包括蛇吃到自己,食物,墙,3种情况 signals: void lost(); void finishChange(); }; #endif

    GameMap.cxx

    #include"GameMap.h" #include<QPixmap> #include<QPainter> #include<QBrush> #include<QPainterPath> #include<QHBoxLayout> #include<ctime> #include<QDebug> GameMap::GameMap(QWidget* parent):QDialog(parent) { running = -2;//蛇默认向左移动 turns = -2; levels = 900; //默认为最简单,蛇0.9秒移动一格 color = 1; //默认蛇为白色 keepGo = 0; //默认可以继续游戏 getFood = 0; //默认吃到0个食物 //时间控制 times = new QTimer(this); times->setInterval(levels); connect(times,SIGNAL(timeout()),this,SLOT(eating())); connect(this,SIGNAL(lost()),this,SLOT(stopGame())); scene = new QGraphicsScene; scene->setSceneRect(-3,-3,406,406); //设计地图大小为400X400周围3格的空隙作为墙 //设置图层背景 QPixmap pix(20,20); QPainter pt(&pix); pt.setBrush(QBrush(Qt::gray)); pt.drawRect(0,0,20,20); scene->setBackgroundBrush(QBrush(pix)); view = new QGraphicsView(scene); //放置第一块食物 food = new QGraphicsRectItem(0,0,18,18); food->setBrush(QBrush(Qt::yellow)); scene->addItem(food); food_x = 100; food_y = 100; //食物最初坐标 food->setPos(food_x+1,food_y+1); hasFood = true; //确认有食物 //放置周围的墙 upWalls = new QGraphicsRectItem(0,0,406,2); upWalls->setBrush(QBrush(Qt::black)); scene->addItem(upWalls); upWalls->setPos(-3,-3); leftWalls = new QGraphicsRectItem(0,2,2,404); leftWalls ->setBrush(QBrush(Qt::black)); scene->addItem(leftWalls); leftWalls->setPos(-3,-3); downWalls = new QGraphicsRectItem(2,404,404,2); downWalls->setBrush(QBrush(Qt::black)); scene->addItem(downWalls); downWalls->setPos(-3,-3); rightWalls = new QGraphicsRectItem(404,2,2,402); //墙的路径,为401X401外围2格子 rightWalls->setBrush(QBrush(Qt::black)); scene->addItem(rightWalls); rightWalls->setPos(-3,-3); //放置最初的蛇 snake = new QGraphicsPathItem; scene->addItem(snake); for(int i = 0 ; i < 5 ; ++i) { QPointF A((10+i)*20+10,10*20+10); RunPath.append(A); } forPath(); snakeTail = RunPath.last(); //先储存蛇. snake->setPos((*RunPath.begin()).x(),(*RunPath.begin()).y()); snake->setPath(snakePath); snake->setBrush(QBrush(Qt::white)); //设置游戏结束(胜利or失败)后的图片 QPixmap winner(tr(":/images/win.jpg")); winPix = new QGraphicsPixmapItem(winner.scaled(406,406)); scene->addItem(winPix); winPix->setPos(0,500); //确保游戏过程中图片在view的外面 QPixmap los(tr(":/images/fail.jpg")); lostPix = new QGraphicsPixmapItem(los.scaled(406,406)); scene->addItem(lostPix); lostPix->setPos(500,0); //确保游戏过程中图片在view的外面 //安装 QHBoxLayout* main_layout = new QHBoxLayout; main_layout->addWidget(view); setLayout(main_layout); } void GameMap::forPath() { QPainterPath A; snakePath = A; //QPainterPath找不到清空的函数,只能用这个本办法。。 //这个函数作用是将scene上的坐标,转化为蛇的坐标 int disX = (*(RunPath.begin())).x(); int disY = (*(RunPath.begin())).y(); for(auto A : RunPath) { QPointF mid(A.x()-disX , A.y()-disY); snakePath.addRect(mid.x()-10,mid.y()-10,20,20); } } void GameMap::newGame() { times->stop(); winPix->setPos(0,500); lostPix->setPos(500,0); //先一开图片 keepGo = 0; getFood = 0; //设置正常游戏状态,获得食物数量为0 turns = -2; running = -2; //每次新游戏开始蛇的运动方向固定向左 times->setInterval(levels); //设置新的时间间隔 //设置颜色 if(color == 1) snake->setBrush(QBrush(Qt::white)); else if(color == 2) snake->setBrush(QBrush(Qt::blue)); else if(color == 3) snake->setBrush(QBrush(Qt::red)); if(!(RunPath.isEmpty())) RunPath.clear(); //如果坐标不为空,先清空坐标 for(int i = 0 ; i < 5 ; ++i) { QPointF A((10+i)*20+10,10*20+10); RunPath.append(A); } forPath(); snake->setPos((*RunPath.begin()).x(),(*RunPath.begin()).y());//先放置一条初始状态的蛇 snake->setPath(snakePath); //放置一块食物 hasFood = true; food_x = 100; //新游戏第一个食物出现位置固定 food_y = 100; food->setPos(food_x+1,food_y+1); } void GameMap::keepRun() { snakeTail = RunPath.last(); //先储存蛇. qreal x = (*(RunPath.begin())).x(); qreal y = (*(RunPath.begin())).y(); //获取头部坐标 if(running+turns != 0) running = turns; //通过数字大小判断方向,如果操作的方向和原来的运动放不完全相反,则响应操作 if(running == 1) //如操作与蛇运动方向完全相反,如蛇运动为1(即朝左边),而操作为-1(即朝右边) y = y - 20; //此时蛇的运动方向不变 if(running == -1) y = y + 20; if (running == -2) x = x - 20; if(running == 2) x = x + 20; QPointF head(x,y); RunPath.push_front(head); RunPath.pop_back(); forPath(); snake->setPos((*RunPath.begin()).x(),(*RunPath.begin()).y()); snake->setPath(snakePath); } void GameMap::keyPressEvent(QKeyEvent* event) { if(event->key() == Qt::Key_W) turns = 1; if(event->key() == Qt::Key_S) turns = -1; if(event->key() == Qt::Key_A) turns = -2; if(event->key() == Qt::Key_D) turns = 2; QDialog::keyPressEvent(event); } void GameMap::giveFood() { srand(unsigned(time(0))); //生成随机数种子 QPainterPath mid; qreal x = 0; qreal y = 0; for(int i ; ; ++i) { int agains = 0; x = rand(); y = rand(); for(auto A : RunPath) //确保食物出现在蛇身体周围外至少一格 { if(x*20+10 == A.x() and y*20+10 == A.y()) ++agains; } if(agains == 0) //表示没有相同坐标 break; else continue; } food_x = x*20; food_y = y*20; food->setPos(food_x+1,food_y+1); } void GameMap::eating() { keepRun(); //先让蛇走一步 //首先判断有没有撞墙 if(snake->collidesWithItem(upWalls) or snake->collidesWithItem(downWalls) or snake->collidesWithItem(leftWalls) or snake->collidesWithItem(rightWalls)) { keepGo = 1; emit lost(); return; } //然后判断有木有吃到自己 QPointF head = *(RunPath.begin()); int i = 0; for(auto A : RunPath) { if(head == A) ++i; } if(i == 2) { keepGo = 1; emit lost(); return; } //判断有没有吃到食物,吃到了尾部+1 int m = 0; for(auto A : RunPath) { if(A.x() == food_x+10 and A.y() == food_y+10) //坐标相同就说明吃到食物了 { ++m; break; } } if(m == 1) { ++getFood; hasFood = false; RunPath.append(snakeTail); forPath(); snake->setPos((*RunPath.begin()).x(),(*RunPath.begin()).y()); snake->setPath(snakePath); //在刷新一次蛇,是身体(在尾部)多一格 } if(hasFood == false) { hasFood = true; giveFood(); //重新放置一块食物 } if(getFood == 10) //胜利条件,获得10个食物 { keepGo = 2; emit lost(); } } void GameMap::stopGame() { times->stop(); if(keepGo == 1) lostPix->setPos(-3,-3); //显示失败图片 if(keepGo == 2) winPix->setPos(-3,-3); //显示胜利图片 } void GameMap::startGame() { if(keepGo == 0) //判断是否可以继续游戏 times->start(); } void GameMap::newOption(int le , int co) { levels = le; color = co; emit finishChange(); //完成设置更改 }

    GameOption.h #ifndef GAMEOPTION_H_ #define GAMEOPTION_H_ #include<QDialog> #include<QRadioButton> #include<QGroupBox> #include<QPushButton> #include<QCloseEvent> class GameOption:public QDialog { Q_OBJECT private: int levels; int color; // 保存选项数值 QGroupBox* level_box; QRadioButton* easy_button; QRadioButton* mid_button; QRadioButton* hard_button; QGroupBox* color_box; QRadioButton* white_button; QRadioButton* blue_button; QRadioButton* red_button; //用于颜色和游戏难度 QPushButton* ok_button; public: GameOption(QWidget* parent = 0); public slots: void changeOption(); signals: void newOption(int,int); protected: void closeEvent(QCloseEvent*); //重写关闭事件,配合主窗体中的模态调用 }; #endif GameOption.cxx #include"GameOption.h" #include<QHBoxLayout> #include<QVBoxLayout> GameOption::GameOption(QWidget* parent):QDialog(parent) { levels = 1; color = 1; //默认为简单难度,白色蛇体,和游戏窗体对应 //安装布局难度框 easy_button = new QRadioButton(tr("简单")); easy_button->setChecked(true); //默选为简单 mid_button = new QRadioButton(tr("一般")); hard_button = new QRadioButton(tr("困难")); level_box = new QGroupBox(tr("游戏难度")); QVBoxLayout* level_layout = new QVBoxLayout; level_layout->addWidget(easy_button); level_layout->addWidget(mid_button); level_layout->addWidget(hard_button); level_box->setLayout(level_layout); //安装颜色选择框 white_button = new QRadioButton(tr("白色")); white_button->setChecked(true); //默选白色 blue_button = new QRadioButton(tr("蓝色")); red_button = new QRadioButton(tr("红色")); color_box = new QGroupBox(tr("颜色选择")); QVBoxLayout* color_layout = new QVBoxLayout; color_layout->addWidget(white_button); color_layout->addWidget(blue_button); color_layout->addWidget(red_button); color_box->setLayout(color_layout); //下方确定按钮布局 ok_button = new QPushButton(tr("确定")); connect(ok_button,SIGNAL(clicked()),this,SLOT(hide())); //点击确定,影藏窗体 connect(ok_button,SIGNAL(clicked()),this,SLOT(changeOption())); QHBoxLayout* button_layout = new QHBoxLayout; button_layout->addStretch(); button_layout->addWidget(ok_button); //整体布局 QHBoxLayout* option_layout = new QHBoxLayout; option_layout->addWidget(level_box); option_layout->addWidget(color_box); QVBoxLayout* main_layout = new QVBoxLayout; main_layout->addLayout(option_layout); main_layout->addLayout(button_layout); setLayout(main_layout); setWindowTitle(tr("游戏选项")); main_layout->setSizeConstraint(QLayout::SetFixedSize); //固定大小 } void GameOption::changeOption() { //难度 if(easy_button->isChecked()) levels = 900; if(mid_button->isChecked()) levels = 600; if(hard_button->isChecked()) levels = 300; //蛇每次移动的事件间隔(毫秒) //颜色 if(white_button->isChecked()) color = 1; if(blue_button->isChecked()) color = 2; if(red_button->isChecked()) color = 3; emit newOption(levels,color); } void GameOption::closeEvent(QCloseEvent* event) { changeOption(); hide();//关闭该窗口只是影藏 QDialog::closeEvent(event); }

    GameWindow.h

    #ifndef GAMEWINDOW_H_ #define GAMEWINDOW_H_ #include<QMainWindow> #include<QMenu> #include<QMenuBar> #include<QAction> #include"GameMap.h" #include"GameOption.h" #include"GameWord.h" class GameWindow:public QMainWindow { Q_OBJECT private: GameMap* game_map; GameOption* options; GameWord* words; QMenuBar* menuBars; //菜单栏 QMenu* game_menu; QMenu* option_menu; QAction* newgame_action; QAction* start_action; QAction* stop_action; QAction* close_action; QAction* option_action; QAction* word_action; void create_action(); void create_menu(); //创建菜单 public: GameWindow(QWidget* parent = 0); private slots: void coustom(); void goStop(); void goStart(); public slots: void goOption(); }; #endif

    GameWindow.cxx #include<QLayout> #include"GameWindow.h" GameWindow::GameWindow(QWidget* parent):QMainWindow(parent) { game_map = new GameMap; setCentralWidget(game_map); //设置中心窗体 connect(game_map,SIGNAL(finishChange()),this,SLOT(goOption())); options = new GameOption(this); //设置选项对话框 connect(options,SIGNAL(newOption(int,int)),game_map,SLOT(newOption(int,int))); words = new GameWord(this); create_action(); create_menu(); setWindowTitle(tr("贪吃蛇")); setWindowIcon(QIcon(tr(":/images/read.png"))); layout()->setSizeConstraint(QLayout::SetFixedSize); //固定大小 } void GameWindow::create_action() { newgame_action = new QAction(tr("新建游戏"),this); newgame_action->setStatusTip(tr("建立新游戏")); newgame_action->setIcon(QIcon(tr(":/images/begin.png"))); connect(newgame_action,SIGNAL(triggered()),options,SLOT(changeOption())); start_action = new QAction(tr("开始"),this); start_action->setIcon(QIcon(tr(":/images/snake.png"))); connect(start_action,SIGNAL(triggered()),game_map,SLOT(startGame())); connect(start_action,SIGNAL(triggered()),this,SLOT(goStart())); stop_action = new QAction(tr("暂停"),this); stop_action->setStatusTip(tr("你可以点击此选项来暂停游戏")); stop_action->setEnabled(false); //游戏未开始时无法使用暂停按钮 connect(stop_action,SIGNAL(triggered()),game_map,SLOT(stopGame())); connect(stop_action,SIGNAL(triggered()),this,SLOT(goStop())); close_action = new QAction(tr("关闭"),this); close_action->setStatusTip(tr("关闭游戏")); connect(close_action,SIGNAL(triggered()),this,SLOT(close())); option_action = new QAction(tr("自定义"),this); option_action->setStatusTip(tr("设置游戏难度以及外观")); connect(option_action,SIGNAL(triggered()),this,SLOT(coustom())); word_action = new QAction(tr("说明"),this); connect(word_action,SIGNAL(triggered()),words,SLOT(exec())); } void GameWindow::create_menu() { game_menu = new QMenu(tr("游戏"),this); game_menu->addAction(newgame_action); game_menu->addAction(start_action); game_menu->addAction(stop_action); game_menu->addSeparator(); game_menu->addAction(close_action); option_menu = new QMenu(tr("选项"),this); option_menu->addAction(option_action); option_menu->addAction(word_action); menuBars = menuBar(); menuBars->addMenu(game_menu); menuBars->addMenu(option_menu); statusBar(); //建立状态栏 } void GameWindow::coustom() { options->exec(); } void GameWindow::goStart() //这2个槽确保 开始 暂停 这2个菜单在同一时间只有1个可用 { start_action->setEnabled(false); stop_action->setEnabled(true); option_action->setEnabled(false); //一旦游戏开始后就无法更改设置 } void GameWindow::goStop() { start_action->setEnabled(true); stop_action->setEnabled(false); } void GameWindow::goOption() { start_action->setEnabled(true); stop_action->setEnabled(false); option_action->setEnabled(true); //只有新建游戏有才能进行游戏难度选择 game_map->newGame(); //建立新游戏 }

    GameWord.h

    #ifndef GAMEWORD_H_ #define GAMEWORD_H_ #include<QDialog> #include<QTextEdit> #include<QCloseEvent> class GameWord:public QDialog { private: QTextEdit* texts; public: GameWord(QWidget* parent = 0); protected: void closeEvent(QCloseEvent*); }; #endif

    GameWord.cxx

    #include"GameWord.h" #include<QHBoxLayout> GameWord::GameWord(QWidget* parent):QDialog(parent) { texts = new QTextEdit; texts->setFixedSize(250,250); QString A(tr("1 在自定义选项中可以调节难度")); QString B(tr("2 新建游戏后,必须点击“开始”后才能开始游戏.")); QString C(tr("3 吃满10个食物即可获得胜利.")); QString D(tr("4 使用| W.上 S.下 A.左 D.右 |键来操作方向")); texts->append(A); texts->append(B); texts->append(C); texts->append(D); texts->setReadOnly(true); //设置为不可用,放置修改内容 QHBoxLayout* main_layout = new QHBoxLayout; main_layout->addWidget(texts); setLayout(main_layout); main_layout->setSizeConstraint(QLayout::SetFixedSize); //固定大小 setWindowTitle(tr("游戏说明")); } void GameWord::closeEvent(QCloseEvent* event) { hide(); QDialog::closeEvent(event); }

    main.cxx

    #include<QApplication> #include"GameWindow.h" int main(int argc , char** argv) { QApplication app(argc,argv); GameWindow A; A.show(); return app.exec(); }

    pix.qrc

    <RCC> <qresource> <file>images/fail.jpg</file> <file>images/win.jpg</file> <file>images/title.png</file> <file>images/begin.png</file> <file>images/read.png</file> <file>images/snake.png</file> </qresource> </RCC>

    转载请注明原文地址: https://ju.6miu.com/read-16066.html

    最新回复(0)