程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 貪吃蛇—C—基於easyx圖形庫(下):從畫圖程序到貪吃蛇【自帶穿牆術】,easyx貪吃蛇

貪吃蛇—C—基於easyx圖形庫(下):從畫圖程序到貪吃蛇【自帶穿牆術】,easyx貪吃蛇

編輯:關於C語言

貪吃蛇—C—基於easyx圖形庫(下):從畫圖程序到貪吃蛇【自帶穿牆術】,easyx貪吃蛇


上節我們用方向控制函數寫了個小畫圖程序,它雖然簡單好玩,但我們不應該止步於此。革命尚未成功,同志還需努力。

 

先復習一下貪吃蛇的結構:

 

開始實現之前,我們先理清一下思路。和前面畫圖程序不同,貪吃蛇可以有很多節,可以用一個足夠大的結構體數組來儲存它。 還需要一個食物坐標。定義如下:

typedef struct Position  //坐標結構
{
    int x;
    int y;
}Pos;

Pos array;                         //移動方向向量
Pos snake[300000] = {};  //蛇的結構體數組,誰能夠無聊到吃299999個食物~_~
long len=1; //蛇的長度
Pos egg; //食物坐標

 

之前的畫圖程序是四個方向都可以走,可蛇是不能倒著走的,所以方向控制函數要改成這樣:

void command()                              //獲取鍵盤命令
{
    if (_kbhit())       //如果有鍵盤消息
        switch (_getch())      /*這裡不能用getchar()*/
        {
        case 'a':
            if (array.x != 1 || array.y != 0) {//如果命令不是倒著走,就修正方向向量,否則不做改變,下同。
                array.x = -1;
                array.y = 0;
            }
            break;
        case 'd':
            if (array.x != -1 || array.y != 0) {
                array.x = 1;
                array.y = 0;
            }
            break;
        case 'w':
            if (array.x != 0 || array.y != 1) {
                array.x = 0;
                array.y = -1;
            }
            break;
        case 's':
            if (array.x != 0 || array.y != -1) {
                array.x = 0;
                array.y = 1;
            }
            break;
        }
} 

 

蛇可能不止一節,所以移動函數需要做出改變。仔細一想就知道,除了頭結點外,每個節點的下一個坐標為它前一個結點當前的坐標,而頭節點的坐標等於它本身坐標加上移動向量(這裡是 方向向量*10)

還有個問題是蛇走過的痕跡需要擦除,每走一步,它留下的痕跡應該是走這一步之前蛇的最末一個結點的坐標,我們需要擦除掉它。

結果如下:

void move()    //修改各節點坐標以達到移動的目的
{
    setcolor(BLACK);        //覆蓋尾部走過的痕跡
    rectangle(snake[len-1].x - 5, snake[len-1].y - 5, snake[len-1].x + 5, snake[len-1].y + 5);

    for (int i = len-1; i >0; i--)    //除了頭結點外,每個節點的下一個坐標為它前一個結點當前的坐標
    {
        snake[i].x = snake[i - 1].x;
        snake[i].y = snake[i - 1].y;
    }
    snake[0].x += array.x*10;             //頭節點的坐標等於它本身坐標加上移動向量(這裡是 方向向量*10)
    snake[0].y += array.y*10;
}

 

另外,我們的蛇是有穿牆術的~~~它的實現方法非常簡單:

void break_wall()
{
    if (snake[0].x >= 640)             //如果越界,從另一邊出來
        snake[0].x = 0;
    else if (snake[0].x <= 0)
        snake[0].x = 640;
    else if (snake[0].y >= 480)
        snake[0].y = 0;
    else if (snake[0].y <= 0)
        snake[0].y = 480;
}

 

 

接下來是食物相關函數,這個算是重點。

1. 食物生成

我們希望食物每次出現的位置都是隨機的, 可以這樣實現。

1         srand((unsigned)time(NULL));
2         egg.x = rand() % 80 * 5 + 100;    //頭節點位置隨機化
3         egg.y = rand() % 50 * 5 + 100;

 而且食物不能與蛇重合,最好也不要離蛇太近。綜合起來就是這樣:(srand在初始化中會被調用,所以這裡略去了)

void creat_egg()
{
    while (true)
    {
        int ok = 0;   //這是個標記,用於判斷函數是否進入了某一分支
        egg.x = rand() % 80 * 5 + 100;    //頭節點位置隨機化
        egg.y = rand() % 50 * 5 + 100;
        for (int i = 0; i < len; i++)     //判斷是否離蛇太近
        {
            if (snake[i].x == 0 && snake[i].y == 0)
                continue;
            if (fabs(snake[i].x - egg.x) <= 10 && fabs(snake[i].y - egg.y) <= 10)
                ok = -1;   //如果,進入此分支,改變標記
                break;
        }
        if (ok == 0)    //如果不重合了,跳出函數
            return;
    }
}

2. 吃到食物

如果吃到食物,那麼需要消除被吃掉的食物,生成新食物,蛇也要增長一節。

我覺得這裡最麻煩的就是蛇變長的實現:是在蛇頭添加一節,還是在蛇尾?添加在蛇頭(尾)的上下左右哪一邊?

想來想去,只有在蛇頭位置,我們可以根據當前方向向量,在移動方向上新添一節。這對應的代碼如下:

        //add snake node
        len += 1;
        for (int i = len - 1; i >0; i--)    //所有數據後移一個單位,騰出snake[0]給新添的一節
        {
            snake[i].x = snake[i - 1].x;
            snake[i].y = snake[i - 1].y;
        }
        snake[0].x += array.x * 10;             //這就是新添的這一節的位置
        snake[0].y += array.y * 10;

吃到食物的完整代碼如下:

void eat_egg()
{
    if (fabs(snake[0].x - egg.x)<=5 && fabs(snake[0].y - egg.y)<=5)    //判斷是否吃到食物,因為食物位置有點小偏差,只好使用范圍判定~~
    {
        setcolor(BLACK);          //hide old egg
        circle(egg.x, egg.y, 5);
creat_egg(); //create new egg //add snake node len += 1; for (int i = len - 1; i >0; i--) { snake[i].x = snake[i - 1].x; snake[i].y = snake[i - 1].y; } snake[0].x += array.x * 10; //每次移動10pix snake[0].y += array.y * 10; } }

 

 

游戲結束判定

最後,我們還差一個死亡判定,因為自帶穿牆術,所以實際的死亡判定只有一個,就是咬到自己,代碼如下:

void eat_self()
{
    if (len == 1)             //只有一節當然吃不到自己~~
        return;
    for (int i = 1; i < len; i++)
        if (fabs(snake[i].x - snake[0].x) <= 5 && fabs(snake[i].y - snake[0].y) <= 5)            //如果咬到自己(為了不出bug,使用了范圍判定)
        {
            outtextxy(250, 200, "GAME OVER!");  //你的蛇死了~
            Sleep(3000);      //3s時間讓你看看你的死相~~
            closegraph();
            exit(0);     //退出
        }
}

當然,你也可以直接丟掉這個函數,然後開心地狂咬自己—_—||

最後:畫圖函數

畫出食物和蛇,其實蛇沒必要全部畫出來,只要畫蛇頭就可以了,但這之中有些小問題,誰有興趣可以自己玩玩,我是懶得動了~

void draw()     //畫出蛇和食物
{
    setcolor(BLUE);
    for (int i = 0; i < len; i++)
    {
        rectangle(snake[i].x - 5, snake[i].y - 5, snake[i].x + 5, snake[i].y + 5);
    }
    setcolor(RED);        //畫蛋(怎麼感覺怪怪的~)
    circle(egg.x, egg.y, 5);
    Sleep(100);
}

到這裡,游戲大功告成~~  什麼?你說運行不起來?那是因為少了初始化函數,和游戲循環啦~~這幾個都比較簡單,就直接放下面了:

void init()              //初始化
{
    initgraph(640, 480);                    //初始化圖形界面
    srand((unsigned)time(NULL));            //初始化隨機函數
    snake[0].x = rand() % 80 * 5 + 100;    //頭節點位置隨機化
    snake[0].y = rand() % 50 * 5 + 100;
    array.x = pow(-1,rand());        //初始化方向向量,左或者右
    array.y = 0;
    creat_egg();
}

int main()
{
    init();
    while (true)
    {
        command();      //獲取鍵盤消息
        move();         //修改頭節點坐標-蛇的移動
        eat_egg();
        draw();         //作圖
        eat_self();
    }

    return 0;
}

好了,這是真的大功告成了。給你們看看死亡方式之自盡:

完整代碼如下:

1 #include<graphics.h> 2 #include<conio.h> 3 #include<time.h> 4 #include<math.h> 5 6 typedef struct Position //坐標結構 7 { 8 int x; 9 int y; 10 }Pos; 11 12 Pos snake[300000] = {}; 13 Pos array; 14 Pos egg; 15 long len=1; 16 17 void creat_egg() 18 { 19 while (true) 20 { 21 int ok = 0; 22 srand((unsigned)time(NULL)); //初始化隨機函數 23 egg.x = rand() % 80 * 5 + 100; //頭節點位置隨機化 24 egg.y = rand() % 50 * 5 + 100; 25 for (int i = 0; i < len; i++) 26 { 27 if (snake[i].x == 0 && snake[i].y == 0) 28 continue; 29 if (fabs(snake[i].x - egg.x) <= 10 && fabs(snake[i].y - egg.y) <= 10) 30 ok = -1; 31 break; 32 } 33 if (ok == 0) 34 return; 35 } 36 } 37 38 void init() //初始化 39 { 40 initgraph(640, 480); //初始化圖形界面 41 srand((unsigned)time(NULL)); //初始化隨機函數 42 snake[0].x = rand() % 80 * 5 + 100; //頭節點位置隨機化 43 snake[0].y = rand() % 50 * 5 + 100; 44 array.x = pow(-1,rand()); //初始化方向向量 45 array.y = 0; 46 creat_egg(); 47 } 48 49 void command() //獲取鍵盤命令 50 { 51 if (_kbhit()) //如果有鍵盤消息 52 switch (_getch()/*這裡不能用getchar()*/) 53 { 54 case 'a': 55 if (array.x != 1 || array.y != 0) {//如果不是反方向 56 array.x = -1; 57 array.y = 0; 58 } 59 break; 60 case 'd': 61 if (array.x != -1 || array.y != 0) { 62 array.x = 1; 63 array.y = 0; 64 } 65 break; 66 case 'w': 67 if (array.x != 0 || array.y != 1) { 68 array.x = 0; 69 array.y = -1; 70 } 71 break; 72 case 's': 73 if (array.x != 0 || array.y != -1) { 74 array.x = 0; 75 array.y = 1; 76 } 77 break; 78 } 79 } 80 81 void move() //修改各節點坐標以達到移動的目的 82 { 83 setcolor(BLACK); //覆蓋尾部走過的痕跡 84 rectangle(snake[len-1].x - 5, snake[len-1].y - 5, snake[len-1].x + 5, snake[len-1].y + 5); 85 86 for (int i = len-1; i >0; i--) 87 { 88 snake[i].x = snake[i - 1].x; 89 snake[i].y = snake[i - 1].y; 90 } 91 snake[0].x += array.x*10; //每次移動10pix 92 snake[0].y += array.y*10; 93 94 if (snake[0].x >= 640) //如果越界,從另一邊出來 95 snake[0].x = 0; 96 else if (snake[0].x <= 0) 97 snake[0].x = 640; 98 else if (snake[0].y >= 480) 99 snake[0].y = 0; 100 else if (snake[0].y <= 0) 101 snake[0].y = 480; 102 } 103 104 void eat_egg() 105 { 106 if (fabs(snake[0].x - egg.x)<=5 && fabs(snake[0].y - egg.y)<=5) 107 { 108 setcolor(BLACK); //shade old egg 109 circle(egg.x, egg.y, 5); 110 creat_egg(); 111 //add snake node 112 len += 1; 113 for (int i = len - 1; i >0; i--) 114 { 115 snake[i].x = snake[i - 1].x; 116 snake[i].y = snake[i - 1].y; 117 } 118 snake[0].x += array.x * 10; //每次移動10pix 119 snake[0].y += array.y * 10; 120 } 121 } 122 123 void draw() //畫出蛇和食物 124 { 125 setcolor(BLUE); 126 for (int i = 0; i < len; i++) 127 { 128 rectangle(snake[i].x - 5, snake[i].y - 5, snake[i].x + 5, snake[i].y + 5); 129 } 130 setcolor(RED); 131 circle(egg.x, egg.y, 5); 132 Sleep(100); 133 } 134 135 void eat_self() 136 { 137 if (len == 1) 138 return; 139 for (int i = 1; i < len; i++) 140 if (fabs(snake[i].x - snake[0].x) <= 5 && fabs(snake[i].y - snake[0].y) <= 5) 141 { 142 Sleep(1000); 143 outtextxy(250, 200, "GAME OVER!"); 144 Sleep(3000); 145 closegraph(); 146 exit(0); 147 } 148 } 149 150 int main() 151 { 152 init(); 153 while (true) 154 { 155 command(); //獲取鍵盤消息 156 move(); //修改頭節點坐標-蛇的移動 157 eat_egg(); 158 draw(); //作圖 159 eat_self(); 160 } 161 162 return 0; 163 } snakey

可能還有若干bug留存,歡迎大家指正~~

甲鐵城鎮~

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved