1. 程式人生 > >Qt小遊戲開發:貪吃蛇

Qt小遊戲開發:貪吃蛇

週末沒事,手寫小遊戲繼續~

預覽


步驟

1 定義資料結構 邏輯與介面分離,遊戲場景是個二維陣列區域,貪吃蛇是若干個連續的座標點集合,用動態連結串列維護,果實是一個隨機座標點。
const int BLOCK_SIZE=25; //單個方塊單元的邊長
const int MARGIN=5; //場景邊距
const int AREA_ROW=15; //場景行數
const int AREA_COL=15; //場景列數
    QPoint foodPoint; //果實出現座標
    QList<QPoint> 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<snake.size();i++)
        painter.drawRect(MARGIN+snake[i].x()*BLOCK_SIZE,MARGIN+snake[i].y()*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE);
    //繪製果實
    painter.setBrush(Qt::green);

    painter.drawEllipse(MARGIN+foodPoint.x()*BLOCK_SIZE,MARGIN+foodPoint.y()*BLOCK_SIZE,BLOCK_SIZE,BLOCK_SIZE);
    //繪製遊戲分數
    painter.setPen(Qt::black);
    painter.setFont(QFont("Arial",14));
    painter.drawText(MARGIN*3+AREA_COL*BLOCK_SIZE,MARGIN+2*BLOCK_SIZE,"score: "+QString::number(score));
}
鍵盤監聽
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<snake.size();i++)
        if(snake[i]==snake.front())
            return true;
    return false;
}
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