Qt贪吃蛇游戏(2):

[0]:墙类的设计:墙分为2种,可以移动的和不会移动的墙.

#ifndef WALL_H
#define WALL_H
#include "QGraphicsItem"

class Wall : public QGraphicsItem
{
public:
    enum WallType  {
        movewall,//会移动的墙
        normalwall
    };

    Wall(qreal x, qreal y,WallType);//墙的坐标
    QRectF boundingRect() const;//重载这个虚函数,用来返回该对象所在区域
    QPainterPath shape();
   void paint(QPainter *painter,const QStyleOptionGraphicsItem *, QWidget *);
   ~Wall();
protected:
   void advance(int);
private:
   QPointF wall_corrdinate;
   WallType walltype;
   int tickCounter = 0;
};
#endif // WALL_H

//wall.cpp

#include "wall.h"
#include "QPainter"
#include "constant.h"
Wall::Wall(qreal x, qreal y, WallType type):wall_corrdinate(x,y),walltype(type)
{
    setPos(wall_corrdinate);
    setData(GD_Type, GO_Wall);//设置墙的类型
}

Wall::~Wall(){}

QRectF Wall:: boundingRect() const
 {

     return QRectF(-19.9,0.1,59.8,19.8);


 }
//绘出墙的图案
void Wall::paint(QPainter *painter,const QStyleOptionGraphicsItem *, QWidget *)
{
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing);
    if(walltype == Wall::normalwall)
    painter->fillPath(shape(), Qt::gray);//墙的颜色设置为黑色
    else
        painter->fillPath(shape(),Qt::blue);//会移动的墙颜色设置为蓝色
    painter->restore();


}
QPainterPath Wall::shape()//获取墙的形状
{
    QPainterPath path;
    path.addRect(-20,0,60,20);//一个正方形
    return path;
}
void Wall::advance(int step)//控制墙的移动
{
    if(!step)
        return;
    if (++tickCounter % 5 != 0) {//speed越大越简单
        return;
    }
    if(walltype == Wall::movewall)
    {

        if(wall_corrdinate.x() -20<-300)
        wall_corrdinate.setX(300);
        else
            wall_corrdinate.setX(wall_corrdinate.x() -20);
        setPos(wall_corrdinate);

    }


}

[1]:GameWindow类的设计,这个是一个继承自主窗口的类,它的作用是方便添加一些菜单(比如暂停游戏和回到游戏等菜单项),然后再将view显示在整个界面的中央.然后在将场景添加到view当中,要注意直接关掉游戏不能直接退出,应该回到主界面.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "QGraphicsView"

namespace Ui {
class MainWindow;
}

class GameController;
class GameWindow : public QMainWindow
{
    Q_OBJECT

    friend class GameController;
public:
    explicit GameWindow (QWidget *parent = 0);
    void closeEvent(QCloseEvent *event);
    ~GameWindow ();

private:
    Ui::MainWindow *ui;
    void initScene();
    void initSceneBackground();

    QGraphicsScene *scene;
    QGraphicsView *view;
    GameController *game;
};

#endif // MAINWINDOW_H


//GameWindow.cpp
#include "Gamewindow.h"
#include "gamecontroller.h"
#include "QTimer"
#include "QAction"
#include "ui_mainwindow.h"
#include "QPixmap"

GameWindow ::GameWindow (QWidget *parent) :
    QMainWindow(parent),ui(new Ui::MainWindow),scene(new QGraphicsScene(this)),
    view(new QGraphicsView(scene, this)),game(new GameController(scene, *this))
{
    ui->setupUi(this);
    this-> resize(800,800);
    setWindowTitle(("SnakeGameS"));//设置游戏主窗口的名字

    /*********以下为添加菜单pause***************/
      auto openAction = new QAction(QIcon(":/images/colorcircle"),tr("&Pause..."),this);//创建一个QAction对象
      openAction->setShortcut(tr("Ctrl+p"));//定义的该对象的快捷键
      connect(openAction,&QAction::triggered,game,GameController::pause);
      auto file = menuBar()->addMenu(tr("&菜单"));//创建菜单栏并且向菜单栏添加一个菜单项
      file->addAction(openAction);//向这个菜单项添加action对象

      /******************添加菜单resume**************************************/
      openAction = new QAction(QIcon(":/images/colorcircle"),tr("&Resume..."),this);
      openAction->setShortcut(tr("Ctrl+r"));
      connect(openAction,&QAction::triggered,game,GameController::resume);
       file->addAction(openAction);

    /*********添加view和scene******************************/
    view->setMinimumSize(800, 800);  //2者设置成一样说明视图尺寸不能再更改
    view->setMaximumSize(800, 800);
    view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//取消掉滚动条
    view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setCentralWidget(view);//将view显示出在GameWindow中央
    initScene();//初始化游戏场景
    initSceneBackground();//初始化游戏背景
}

GameWindow ::~GameWindow ()
{
    delete ui;
}
void GameWindow::initScene()
{
    scene->setSceneRect(-300,-300,600,600);//设置scene的
}
void GameWindow::initSceneBackground()
{
    QPixmap bg(20,20);
    bg.load(":/images/ground");
    view->setBackgroundBrush(QBrush(bg));//将整个背景bg填充
}
void GameWindow::closeEvent(QCloseEvent *event)
{
 auto start_dlg = static_cast<QMainWindow*>(this->parent());
 start_dlg->show();//让主窗口显示出来,否则整个程序会直接结束
 QMainWindow::closeEvent(event);
}

[2]:游戏控制类的设计:这是整个游戏逻辑的控制区域,它负责添加食物,添加蛇,添加墙,还需要判断添加的对象不能和以前的冲突(这一部分逻辑相对比较复杂,需要利用Qt的碰撞机制),还负责游戏分数的变化,以及游戏结束时根据结束界面选择的判断(回到主界面或者重来一局新的),处理来自键盘的事件(需要判断方向是否走反了)等等功能.

#ifndef GAMECONTROLLER_H
#define GAMECONTROLLER_H

#include <QObject>
#include "food_snake.h"
#include "QGraphicsView"
#include "QTimer"
#include "QEvent"
#include "wall.h"

class GameWindow;
class GameController : public QObject
{
    Q_OBJECT
public:
    explicit GameController(QGraphicsScene *scene, GameWindow& window, QObject *parent = 0);
    ~GameController();
    void pause();//暂停游戏
    void resume();//重新游戏
    void snakeAteFood(Food* food);
    void snakeAteItself();
    void snakeHitItself();
    void addNewFood();
    void addNewWall(Wall::WallType);
    void update_scores();
    void init_scores();//初始化分数文本

protected:
    bool eventFilter(QObject*,QEvent*);
private:
 void CreateAllwalls();
QGraphicsScene *scene;
QGraphicsTextItem *game_score;//分数文本
QTimer timer;
Snake *snake;
GameWindow& window;
void gameOver();
void handlekeyPressed(QKeyEvent* event);
int FoodCount = 0;//定义现存食物的数量
int scores = 0;//定义得分
bool isconnected = true;//定义一个用来判断是否连接的时间信号的变量

};

#endif // GAMECONTROLLER_H


//gamecontroller.cpp
#include "gamecontroller.h"
#include "snake_end_dlg.h"
#include "Gamewindow.h"
#include "QTimer"
#include "QKeyEvent"


GameController::GameController(QGraphicsScene *scene, GameWindow& window,QObject *parent) : QObject(parent),
    scene(scene),snake(new Snake(*this)),window(window)
{
    init_scores();
    timer.start(15);//开启30帧的游戏循环
    connect(&timer,SIGNAL(timeout()),scene,SLOT(advance()));

    scene->addItem(snake);
    CreateAllwalls();
    addNewFood();

    scene->installEventFilter(this);//添加事件过滤器,用来处理键盘事件
}

GameController::~GameController()
{
}
void GameController::init_scores()
{
    scores = 0;
    game_score = new QGraphicsTextItem(0);//文本的父item为对应的场景
    scene->addItem(game_score);
     game_score->setFont(QFont("Times", 30, QFont::Bold));//为文本设置字体
      game_score->setPos(100, -350);//分数在场景中出现的位置
      game_score->setHtml(tr("<font color = red >分数:0</font>"));
      game_score->show();
}

void GameController:: pause()
{
    isconnected = false;
    disconnect(&timer,SIGNAL(timeout()),scene,SLOT(advance()));
//断开scene与timeout信号的连接

}
void GameController::resume()
{
    if(!isconnected)
    {
     isconnected = true;
    connect(&timer, SIGNAL(timeout()), scene, SLOT(advance()));//如果反复
    }
}
//重写游戏控制类的事件过滤器
bool GameController::eventFilter(QObject* obj,QEvent* event)
{
  if(event->type()==QEvent::KeyPress)//来自键盘的事件
  {
 handlekeyPressed(static_cast<QKeyEvent*>(event));
      return true;
  }
  else
      return QObject::eventFilter(obj,event);

}
void GameController::handlekeyPressed(QKeyEvent *event)
{
  auto snake = this->snake;
   if(event->key() == snake->moveDirection)
       return;
//现在方向至少得走一步
   if(!(snake->current_stepcounts))//一步没走,那么现在改变方向的指令无效
       return;
   else
       snake->current_stepcounts = 0;//现在走的方向变成了0
    switch(  event->key())
    {
    case Qt::Key_Left:
            snake->setMoveDirection(Snake::MoveLeft);
        break;
    case Qt::Key_Right:
            snake->setMoveDirection(Snake::MoveRight);
        break;
    case Qt::Key_Up:
            snake->setMoveDirection(Snake::MoveUp);
        break;
    case Qt::Key_Down:
            snake->setMoveDirection(Snake::MoveDown);
        break;
}
}
void GameController::snakeAteFood(Food* food)
{

    if(food->food_type==Food::SpeedFood)
    {
        snake->speedbuf  = 2;
        snake->Left_speeding_time = 60;
        scores += 40;
    }
    scores+=10;//特殊食物+50,一般食物+10
    update_scores();//更新分数
    scene->removeItem(food);
    delete food;
    addNewFood();

}
void GameController::addNewFood()
{
    QList<QGraphicsItem*> collidingitems;
    Food *newfood;
    int x, y;
    do {
        x = (int) (qrand() % 400 - 200) / 20 ;
        y = (int) (qrand() % 400 - 200) / 20 ;
        x *= 20;
        y *= 20;
        /**************试探性的添加食物***************/

        if(!(++FoodCount%5))
        newfood = new Food(x , y,Food::SpeedFood);
        else
        newfood = new Food(x , y,Food::NormalFood);
        scene->addItem(newfood);//先添加食物,再通过碰撞机制判断
        collidingitems = newfood->collidingItems();
         if(!collidingitems.size())//没有碰撞
             break;
         else//有碰撞,将食物移出
         {
         scene->removeItem(newfood);
         delete newfood;
         }
} while (1);
}
void GameController::addNewWall(Wall::WallType type)
{
    //创建一个新的墙
    int x, y;
    do {
        x = (int) (qrand() % 400 - 200) / 20 ;
        y = (int) (qrand() % 400 - 200) / 20 ;
        x *= 20;
        y *= 20;
} while (snake->shape().contains(snake->mapFromScene(QPointF(x+10, y + 10)))
         |snake->shape().contains(snake->mapFromScene(QPointF(x + 30, y + 10)))
        |snake->shape().contains(snake->mapFromScene(QPointF(x - 10, y + 10))));

    scene->addItem(new Wall(x , y,type));

}
void GameController::CreateAllwalls()
{
    for(int i = 1;++i<=5;)
    addNewWall(Wall::normalwall);
    for(int i = 1;++i<=2;)
     addNewWall(Wall::movewall);
}

void GameController::snakeAteItself()
{
    QTimer::singleShot(0, this, gameOver);
}
void GameController::snakeHitItself()
{
    QTimer::singleShot(0, this, gameOver);
}
/**********更新分数的*******************/
void GameController::update_scores()
{
   game_score->setHtml(tr("<font color = red>分数:%1</font>").
              arg(scores));//重新设置分数
}

void GameController::gameOver()
{
    scene->clear();//清除所有动画
    //出现结束界面
    snake_end_dlg end;
    auto ret = end.exec();
    if(ret==QDialog::Rejected)//关闭或者结束
    {
        window.close();
        return;
    }
    /*********重新开始游戏*************/
    auto start_dlg = static_cast<QMainWindow*>(window.parent());
    if(start_dlg->isHidden())
    start_dlg->show();
    snake = new Snake(*this);//添加新的蛇
    scene->addItem(snake);//先加蛇
    CreateAllwalls();//再创建墙
    addNewFood();//最后添加食物,注意添加顺序很重要,因为食物必须放在最后保证不和任何对象冲突
    init_scores();
    start_dlg->hide();
}

[3]:结束界面的设置

#ifndef SNAKE_END_DLG_H
#define SNAKE_END_DLG_H

#include <QDialog>

namespace Ui {
class snake_end_dlg;
}

class snake_end_dlg : public QDialog
{
    Q_OBJECT

public:
    explicit snake_end_dlg(QWidget *parent = 0);
    ~snake_end_dlg();
private slots:
    void processclicked();
private:
    Ui::snake_end_dlg *ui;
};
#endif // SNAKE_END_DLG_H


//snake_end_dlg.cpp
#include "snake_end_dlg.h"
#include "QPushButton"
#include "ui_snake_end_dlg.h"

snake_end_dlg::snake_end_dlg(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::snake_end_dlg)
{
    ui->setupUi(this);
    setWindowTitle(tr("游戏结束界面"));
}

snake_end_dlg::~snake_end_dlg()
{
    delete ui;
}
void snake_end_dlg::processclicked()
{
    auto btn = static_cast<QPushButton*>(sender());
    if(btn->objectName()=="restart")//判断按钮情况
        accept();  //重新开始游戏
    else if(btn->objectName()=="end")
        close();  //退出游戏
}

[4]:常量的设置(食物和墙的类型)

constant.h
#ifndef CONSTANT
#define CONSTANT
enum GameObjectsData {
    GD_Type
};
enum GameObjectTypes {
    GO_Food,
    GO_Wall
};

[5]main.cpp

#include "Gamewindow.h"
#include "QSplashScreen"
#include "snake_start_dlg.h"
#include <QApplication>

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   Snake_Start_Dlg startdlg;
   startdlg.show();
   return a.exec();
}

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值