Qt小游戏开发:贪吃蛇

周末没事,手写小游戏继续~


预览

Qt小游戏开发:贪吃蛇_第1张图片

步骤

1 定义数据结构
逻辑与界面分离,游戏场景是个二维数组区域,贪吃蛇是若干个连续的坐标点集合,用动态链表维护,果实是一个随机坐标点。
const int BLOCK_SIZE=25; //单个方块单元的边长
const int MARGIN=5; //场景边距
const int AREA_ROW=15; //场景行数
const int AREA_COL=15; //场景列数
    QPoint foodPoint; //果实出现坐标
    QList snake; //贪吃蛇结构
const int TIME_INTERVAL=500; //定时器间隔时间

enum Direction
{
    UP,
    DOWN,
    LEFT,
    RIGHT
};

2 添加界面刷新和键盘监听
首先需要定时器来控制贪吃蛇的移动以及界面的刷新,绑定定时器的信号槽
gameTimer=new QTimer(this);
    connect(gameTimer,SIGNAL(timeout()),this,SLOT(SnakeUpdate()));
窗口重绘
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    //绘制游戏场景
    painter.setBrush(Qt::yellow);
    painter.setPen(Qt::blue);
    painter.drawRect(MARGIN,MARGIN,AREA_COL*BLOCK_SIZE,AREA_ROW*BLOCK_SIZE);
    //绘制贪吃蛇
    painter.setBrush(Qt::red);
    painter.setPen(Qt::green);
    for(int i=0;i
键盘监听
void Widget::keyPressEvent(QKeyEvent *event)
{
    //贪吃蛇的方向是个状态机,注意各个方向之间切换有限制
    switch(event->key())
    {
    case Qt::Key_Up:
        if(dir!=DOWN)
            dir=UP;
        break;
    case Qt::Key_Down:
        if(dir!=UP)
            dir=DOWN;
        break;
    case Qt::Key_Left:
        if(dir!=RIGHT)
            dir=LEFT;
        break;
    case Qt::Key_Right:
        if(dir!=LEFT)
            dir=RIGHT;
        break;
    case Qt::Key_P:
        PauseResumeGame();
        break;
    default:
        break;
    }
}

3 贪吃蛇移动、吃果实、得分、游戏结束逻辑
贪吃蛇的移动是个状态机,总是朝着当前的方向前进,移动的逻辑是,不断地消除尾部节点,并添加一个新的头部结点,如果碰到了果实,则不消除尾部结点
每次吃到了果实后会重新随机生成一个果实,并且保证果实不会与贪吃蛇身体重叠。
void Widget::SnakeUpdate()
{
    //贪吃蛇移动的策略是每次删除尾部,然后添加新的头部,维护一个动态链表
    switch(dir)
    {
    case UP:
        snake.push_front(QPoint(snake.front().x(),snake.front().y()-1));
        break;
    case DOWN:
        snake.push_front(QPoint(snake.front().x(),snake.front().y()+1));
        break;
    case LEFT:
        snake.push_front(QPoint(snake.front().x()-1,snake.front().y()));
        break;
    case RIGHT:
        snake.push_front(QPoint(snake.front().x()+1,snake.front().y()));
        break;
    default:
        break;
    }
    //如果吃到了果实,则尾部不删除,否则删除尾部更新头部
    if(snake.contains(foodPoint))
    {
        score+=1; //得分
        GenerateFood(); //重新生成果实
    }
    else
        snake.pop_back();
    //游戏是否结束
    if(IsGameOver())
    {
        GameOver();
        return; //赶在重绘之前跳出函数,防止贪吃蛇真的出界
    }
    update(); //重绘,比repaint函数效果好
}
void Widget::GenerateFood()
{
    //随机产生位置
    foodPoint.setX(rand()%AREA_COL);
    foodPoint.setY(rand()%AREA_ROW);
    //如果与贪吃蛇位置冲突,重新生成
    if(snake.contains(foodPoint))
        GenerateFood();
}
当贪吃蛇头部出界或者撞到了自身则判定为游戏结束
bool Widget::IsGameOver()
{
    int x=snake.front().x();
    int y=snake.front().y();
    //出边界
    if(x<0||x>AREA_COL-1||y<0||y>AREA_ROW-1)
        return true;
    //撞了自己
    for(int i=3;i

4 游戏控制逻辑
游戏初始化
void Widget::InitGame()
{
    //初始化贪吃蛇,初始长度5,注意头部在前,尾部在后
    for(int j=4;j>=0;j--)
        snake.push_back(QPoint(j,0));
    dir=RIGHT;//初始时往右走
    //初始化果实
    srand(time(0));
    GenerateFood();
    //初始化游戏分数
    score=0;
    //初始化暂停变量
    isPause=false;
    //初始化计时器
    gameTimer=new QTimer(this);
    connect(gameTimer,SIGNAL(timeout()),this,SLOT(SnakeUpdate()));
    gameTimer->start(TIME_INTERVAL);
}
游戏暂停和恢复
void Widget::PauseResumeGame()
{
    //暂停和恢复定时器
    if(!isPause)
    {
        isPause=!isPause;
        gameTimer->stop();
    }
    else
    {
        isPause=!isPause;
        gameTimer->start(TIME_INTERVAL);
    }
}
游戏结束
void Widget::GameOver()
{
    gameTimer->stop();
    QMessageBox::information(this,"failed","game over!");
}

源码下载:
csdn: 贪吃蛇
github:  snake



你可能感兴趣的:(Game,Develop,Qt,游戏开发,qt)