[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();
}