【C++】貪喫蛇小遊戲--有圖形界面和背景音樂

 貪喫蛇是一款經典的小遊戲,曾經在一些老式的手機上都是有的,今天我們就來實現一下這個小遊戲。
 首先是寫代碼錢的前期準備工作,我們這次實現的是帶圖形界面的貪喫蛇,必須要先下一個圖形界面庫,方便實現圖形界面,這個圖形界面庫的名字叫做Easyx,可以在網上下載
這裏寫圖片描述
直接進入下載就可以,然後裝在vs的安裝目錄下:
 首先我們要玩貪喫蛇需要兩個部分蛇頭和蛇身,我們先要定義兩個宏一個控制蛇的最大節數,一個控制蛇的的尺寸,方便調整,我們設置蛇的長度爲1000節,蛇的尺寸是20像素點,然後要定義蛇的結構體,我們知道蛇的屬性很多,包括蛇的重量,蛇的長短,蛇的種類,蛇有沒有毒等等很多這裏當然我們用不到這麼多隻需要幾個簡單的屬性,能幫助我們實現相關的功能就可以了,我們要的蛇的屬性包括,蛇的節數、蛇的座標,蛇運動的方向,這裏的座標我們也用一個結構體來表示,蛇的相關信息表示玩,但是貪喫蛇還有一個重要的部分,是什麼呢?對,就是食物,所以我們還要定義一個食物的結構體,食物當然也有食物的屬性,包括食物的座標以及一個標誌位,這個標誌位用來表示是否需要產生食物,相關信息都表示完了,我們就可以正式寫我們的遊戲了。
 先寫我們的初始化函數,我們先寫遊戲的窗口,利用圖形庫中的函數就可以了,我們將窗口的高度設置爲480,寬度設置成640,單位是像素點,完成之後如圖所示:
這裏寫圖片描述

 確實是什麼都沒有就是一個窗口,將來我們設計的蛇就要在這個裏面跑了。
 接下來我們要對蛇的屬性先進行初始化,將蛇的節數設置爲1,蛇的座標設置成0,0,先讓蛇在左上角出現,注意我們這裏的座標和數學上的座標是不一樣的,左上角座標是0,然後向下和向右遞增,數學上是向下是遞減,是不一樣的。然後將蛇的初始方向設置成右,也可以設置成下,但是左和上是不可以的,要不蛇直接就撞牆了。然後對食物屬性進行初始化,這裏食物應該是隨機產生,所以不能初始化,不然食物的位置被固定就沒辦法隨機了,我們先將食物的標誌位設置爲0,表示需要產生食物,具體怎麼用在下面介紹,到這裏初始化函數就完成了,這樣完成後還是沒有什麼效果的。因爲我們還沒有將蛇繪製出來。
 下面我們來繪製蛇,繪製蛇分爲兩個部分一部分是繪製蛇頭,一部分是繪製蛇身。我們先來寫繪製蛇頭,這裏使用圖形庫中的一個函數,fillrectangle(),這個函數可以繪製一個矩形出來,但是我們直到要繪製一個矩形就必須要知道它的對角的兩個座標,所以這個函數的四個參數就是兩個頂點的座標,這樣就可以繪製出來蛇頭了
這裏寫圖片描述
 這就是我們繪製出來的蛇頭,現在還是比較簡陋的只有一個小矩形。下面我們來繪製蛇身,蛇身和蛇頭一樣使用fillrectangle()函數來繪製,這裏我們直到蛇身是不斷增長的,所有需要用一個循環來繪製,蛇頭我們已經繪製了,所以蛇身就需要繪製蛇的節數減一個,這裏我們從後向前繪製,運行之後我們發現還是和上圖一樣的結果,這是因爲,蛇的節數是1,也就是隻有蛇身的情況,彆着急我們繼續寫。
 我們直到蛇要會移動的,而且蛇的移動是自己動的,不是人操作,人只能控制蛇的轉向,下面我們就來寫一下,蛇移動的函數,我們知道蛇的轉向是很靈活,我們觀察發現蛇頭後面的蛇身的位置就是上一次蛇頭的位置,後面跟着的其他蛇身也是前面蛇身前一次的位置,所以蛇的移動就是把前一次的座標賦給這次的結點,考慮清楚這些後就知道蛇的移動是怎麼移動的了,蛇往哪個方向移動呢,這裏用一個switch語句來判斷,判斷的內容就是蛇的方向,蛇的方向向右的化,座標每次要加上蛇的尺寸,然後移動,其他方向也是一樣,因爲蛇是一個矩形不是一個點座標每次移動的大小就必須是蛇的尺寸,才能顯示蛇走了一步。將蛇的移動函數進行測試,可以得到下圖:
這裏寫圖片描述
&cmsp;我們發現並不是我們想要的結果,直接就是一條直線,這是因爲蛇走的太快了,我們加上Sleep()函數,讓每次繪製蛇之後等待300毫秒,
這裏寫圖片描述
 我們發現還是不對,蛇的軌跡連在一起不是我們想要的結果,這是因爲我們沒有刷新屏幕,所以就會在原來的基礎上進行移動,我們調用一下cleardevice()函數刷新一下屏幕,再試一下:
這裏寫圖片描述
 現在就是我們想要的結果了,可是我們發現這是一條失控的蛇,不論按那個按鍵,蛇都按着自己的想法直接衝到右邊,不會拐彎,這是因爲,我們沒有讀入鍵盤信息,現在我們來寫一下控制蛇的函數,還是一個switch語句,判斷的就是鍵盤的輸入,利用getch()函數來判斷(vs中getch()會有警告我們可以選擇忽略或者使用_getch()),利用判斷鍵盤的鍵碼值來判斷,是哪個方向。
這裏寫圖片描述
 我們又發現只有當我們輸入一次方向鍵蛇纔會走一步,這是爲什麼呢?這是因爲程序一直在等待鍵盤輸入,如果鍵盤不輸入,它就一直等待輸入,不會進行下一步,這裏我們使用kbhit()函數來解決,用!kbhit()表示無鍵盤輸入情況,定義一個while循環將要執行的程序除了控制蛇的移動以外都放進這個循環中,這樣只有當有鍵盤輸入的時候它纔會跳出循環,判斷要走的方向是哪一個,進而控制蛇的移動。
這裏寫圖片描述
 這次我們發現就可以正常的走了,當輸入一個方向時纔會改變反向,然後按照新輸入的方向繼續往下走,蛇能正常走了下來我們就要考慮食物的問題了,我們先來生成食物的座標,還記得我們設置食物結構體的時候設置的標誌位嗎,這時候就要用到了,我們知道食物應該是隨機產生的,所以我們用隨機函數來產生了,爲了使產生的座標合法我們還需要考慮,讓產生的座標在這個界面內,我們直到界面的寬度是640,高度是480,我們的食物也是一個矩形所以必須考慮矩形的大小,所以橫座標的範圍就是0-620,y座標的範圍是0-460,又知道蛇每次移動的距離是20,所以橫座標是0-31,縱座標是0-23,所以橫座標%32,縱座標%24兩個分別乘以SIZE(蛇的尺寸);然後將食物的flag設爲1,表示不再需要產生食物。
 下面我們就要繪製食物的圖形,這裏調用roundrect()函數,這個函數可以繪製一個圓角矩形,這個函數的參數除了矩形的對角兩個頂點以外還要直到圓角的弧度,這裏給成10,我們還可以設置線條的顏色,這裏我們把線條顏色設置成藍色,運行之後得到:
這裏寫圖片描述
我們可以看到左上角生成的食物了,但是我們發現,當蛇走到食物的位置的時候蛇沒有變化食物也沒有變化,這是我們還沒有寫喫食物的函數,下面寫一下喫食物的函數,喫食物也就是蛇頭的座標和食物的座標相同,當兩個座標相同的時候,我們將蛇的節數增加,然後將食物的flag置爲0,下次再次進行判斷,如果食物的flag如果是0的話重新生成食物座標,就會自動重新生成食物了,下面我們再測試一下:
這裏寫圖片描述
  這次我們發現當蛇喫掉食物之後,蛇的長度就會總動增長,食物也會更新,可以實現基本的功能了,可是我們的遊戲還沒有設置遊戲結束的條件,我們知道貪喫蛇遊戲的結束條件是蛇碰到牆或者蛇頭碰到蛇身,我們來設計一下游戲結束的條件,蛇頭碰到牆也就是蛇的座標超過了界面,也就是x不在0-620之間,y不在0-460之間,遊戲結束,然後每次移動用循環判斷蛇頭的座標和蛇身的座標是不是相同,如果相同就遊戲結束,遊戲結束的現象就是在屏幕上輸出一個Game Over,我們使用outtextxy()函數來設置在界面上展示的文字以及大小,還可以設置字體格式,字體顏色。我們遊戲結束時可以先刷新一下屏幕,遊戲結束只顯示遊戲結束那幾個字就可以了,爲了我們能看見遊戲結束幾個字,我們可以調用Sleep函數等待3s時間,然後退出程序。
 然後我們最後再來驗證一下:
這裏寫圖片描述
遊戲就可以結束了。
下來就是加入圖像界面,我們首先把自己找到的圖片放在目錄下面,
這裏寫圖片描述
首先定義IMAGE(定義圖像對象)類的對象,在初始化函數中將圖片加載進去,爲了好看一點,我們把界面的顏色設置成白色,這時候不會有任何現象出現,下來我們將蛇頭蛇身都換成圖片,我們直到我們沒有辦法將蛇頭在程序中轉向90度,所以我們準備四張蛇頭的圖片,分別對應上下左右四個方向,然後用一個switch當前的蛇頭的方向是哪一個就把哪一個方向的蛇頭放在蛇頭的位置,圖片的尺寸也要和蛇頭的尺寸相同,然後開始繪製蛇身,蛇身不用那麼多情況,直接將蛇身的圖片放在蛇身的位置就可以了。下面就是繪製食物了,將食物的圖片放在食物的位置即可。
我們再來運行一下:
這裏寫圖片描述
我們介紹一下,加入圖片的幾個函數,
(1)loadimage()加載圖片,一共四個參數,第一個參數是IMAGE類的對象的地址,第二個是圖片的名稱,是一個字符串(L是編碼格式的轉換),剩下兩個參數表示圖片的尺寸
(2)putimage()放置圖片到某個位置,一共三個參數,放入位置的橫座標,放入位置的縱座標,IMAGE類的對象的地址
 下面我們爲這個遊戲再加上背景音樂:
使用PlaySound函數,一共三個參數,第一個是歌曲路徑,第二個參數是應用程序的一個實例句柄,除非指向一個資源標識符,否則設置爲空,第三個參數播放的方式,使用這個函數時要加上:#pragma comment(lib, "winmm.lib")
否則無法使用。而且這個函數只能播放wav格式的音樂,可以使用格式工廠將mp3格式轉化爲wav格式再使用。
這裏寫圖片描述
代碼如下:

#include<graphics.h>//圖形庫頭文件
#include<conio.h>//按鍵相關的頭文件
#include<time.h>
#include<imapi.h>
#include<windows.h>
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
#define MAX 1000//蛇的最大節數
#define SIZE 20//蛇的尺寸
IMAGE sheadup, sheaddown, sheadleft, sheadright,body, sfood;

struct COOR//座標類
{
    int x;
    int y;
};
enum CH
{
    up=72,//鍵碼值
    down=80,
    left=75,
    right=77
};
//蛇類
struct SNAKE
{
    int n;//蛇的節數
    COOR scr[MAX];//蛇的座標
    CH ch;//蛇的方向
}snake;

struct FOOD//食物類    
{
    COOR fcr;//食物座標
    int  flag;//食物是否生成的標誌
}food;

void initGame()
{
    initgraph(640, 480);//窗口寬度,高度

    setbkcolor(WHITE);
    cleardevice();
    //加載圖片
    loadimage(&sheadup, L"蛇頭上.jpg", SIZE, SIZE);
    loadimage(&sheaddown, L"蛇頭下.jpg", SIZE, SIZE);
    loadimage(&sheadleft, L"蛇頭左.jpg", SIZE, SIZE);
    loadimage(&sheadright, L"蛇頭右.jpg", SIZE, SIZE);
    loadimage(&body, L"蛇身.jpg", SIZE, SIZE);
    loadimage(&sfood, L"西瓜.jpg", SIZE, SIZE);
    snake.n = 1;//蛇的節數初始化
    snake.scr[0].x = 0;//蛇的座標
    snake.scr[0].y = 0;
    snake.ch = right;

    food.flag = 0;//生成食物
}

//繪製蛇
void SnakePaint()
{
    //繪製蛇頭
    /*fillrectangle(snake.scr[0].x, snake.scr[0].y, snake.scr[0].x + SIZE, \
        snake.scr[0].y + SIZE);*/
    switch (snake.ch)
    {
    case up:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheadup);
        break;
    case down:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheaddown);
        break;
    case left:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheadleft);
        break;
    case right:
        putimage(snake.scr[0].x, snake.scr[0].y, &sheadright);
        break;
    default:
        break;
    }
    //繪製蛇身
    for (int i = snake.n - 1; i > 0; i--)
    {
        /*fillrectangle(snake.scr[i].x, snake.scr[i].y, snake.scr[i].x + \
            SIZE, snake.scr[i].y + SIZE);*/
        putimage(snake.scr[i].x, snake.scr[i].y, &body);
    }
}
//蛇的移動
void SnakeMove()
{
    for (int i = snake.n - 1; i > 0; i--)
    {
        snake.scr[i].x = snake.scr[i - 1].x;
        snake.scr[i].y = snake.scr[i - 1].y;
    }
    switch (snake.ch)
    {
    case up:
        snake.scr[0].y -= SIZE;
        break;
    case down:
        snake.scr[0].y += SIZE;
        break;
    case left:
        snake.scr[0].x -= SIZE;
        break;
    case right:
        snake.scr[0].x += SIZE;
        break;
    default:
        break;
    }
}
//控制蛇的方向
void ChangeSnakech()
{
    switch (_getch())
    {
    case 72:
        if (snake.ch != down)
            snake.ch = up;
        break;
    case 80:
        if (snake.ch != up)
            snake.ch = down;
        break;
    case 75:
        if (snake.ch != right)
            snake.ch = left;
        break;
    case 77:
        if (snake.ch != left)
            snake.ch = right;
        break;
    default:
        break;
    }
}
//生成食物座標
void FoodCoor()
{
    //隨機數種子
    srand((unsigned)time(NULL));
    food.fcr.x = rand() % 32 * SIZE;
    food.fcr.y = rand() % 24 * SIZE;
    food.flag = 1;//生成座標以後先不生成食物

}
//繪製食物
void FoodPaint()
{
    /*setlinecolor(BLUE);
    roundrect(food.fcr.x,food.fcr.y,\
        food.fcr.x+SIZE,food.fcr.y+SIZE,10,10);*///繪製圓角矩形
    putimage(food.fcr.x, food.fcr.y, &sfood);
}

//喫食物
void EatFood()
{
    if (snake.scr[0].x == food.fcr.x&&snake.scr[0].y == food.fcr.y)
    {
        snake.n++;
        food.flag = 0;
    }
}

void judge()
{
    cleardevice();
    settextcolor(LIGHTGREEN);
    settextstyle(50, 0, L"宋體");
    outtextxy(200, 200, L"Game Over");

    Sleep(3000);
    exit(0);
}
//判斷遊戲結束
void GameOver()
{
    //蛇頭撞到了牆
    if (snake.scr[0].x<0 || snake.scr[0].x>620 ||\
        snake.scr[0].y<0 || snake.scr[0].y>460)
    {
        judge();
    }
    //蛇頭撞到了蛇身
    for (int i = snake.n - 1; i > 0; i--)
    {
        if (snake.scr[0].x == snake.scr[i].x\
            &&snake.scr[0].y == snake.scr[i].y)
        {
            judge();
        }
    }
}
int main()
{

    initGame();

    PlaySound(L"F:\\Documents\\工程\\貪喫蛇\\貪喫蛇\\PGL - Dear mom.wav", NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);

    while (1)
    {
        while (!_kbhit())//當按下按鍵退出循環
        {
            if (food.flag == 0)
                FoodCoor();
            cleardevice();//刷新屏幕
            FoodPaint();
            SnakeMove();//蛇的移動
            SnakePaint();//蛇的繪製
            Sleep(300);
            EatFood();
            GameOver();
        }

        ChangeSnakech();
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章