c 實現貪吃蛇(附詳細講解)

2021-09-29 04:30:19 字數 4091 閱讀 7499

更新了不會刷屏的貪吃蛇版本, 鏈結為:snake2。

修復星星出現在牆上的bug。

條件判斷

迴圈函式

陣列多cpp檔案呼叫(不然你也可以直接寫在乙個檔案裡)

指標結構體

鍊錶(採用頭插法)

1. 畫圖

這一步是最簡單的,沒錯,我的習慣就是從最簡單的開始。

我們的目標是畫乙個框框,然後這個框框裡有條蛇, 還有個星星,根據這個目標,最後的**就會書寫成這樣子:

void draw(int **map, int *star, int height, int weight)else if(map[i][j] == 2)else if(i == star[0] && j == star[1])else

} cout << endl;

} return;

}

這裡,多介紹一些各個引數是什麼, map是地圖陣列,star是星星的所在位置,height是地圖的高,weight是地圖的寬。

有乙個細節我想你也注意到了,沒錯就是清空螢幕這個操作,這是為了什麼要這麼做呢,其實一開始是沒有這個操作的,每次移動都會多畫乙個圖在原來的圖下面,這就會導致我們的地圖位置不固定,影響我們的發揮,所以,我們要把這個地圖固定住,這裡明確一點,我們只會使用cmd這個視窗作為我們的遊戲介面。

清空螢幕之後,就會開始畫圖,這樣就會保證每次畫的圖都是新的。不過由於用了清屏的操作,所以會一閃一閃的,即便如此,我也玩得很嗨,沒辦法,原始人的快樂就是這麼樸實。

不過上面的**是完整版的**,實際上, 如果你是從零開始搭建,開始的畫圖**應該是只有地圖,也就是一堵牆和地磚, 然後加上了蛇之後,畫上蛇,然後有了小星星後, 畫上小星星。

有個比較需要注意的細節, 由於蛇是用鍊錶實現的,出於減少計算量的強迫症,我會先將蛇融合到地圖中去,如下:

void drawsnake(int **map, struct snake snake)

return;

}

然後繪圖時,就還是一次畫完就可以了, 不用不斷遍歷蛇是否在某個點上,沒錯,強迫症真的很可怕。

以上**你可以簡單理解為 map = map + snake(當然,這個加可不是真的數值運算哈,我再偷偷提醒你一下吧。)。

也許你會問我, 那為什麼不把星星也直接加到map裡呢?問得好,這是因為蛇是變長的, 蛇最長能達到map的大小,假如我們不把蛇先用以上方式加到map中的話,在繪圖時進行檢查,最糟糕情況會是o(n*n),n=height*weight的計算複雜度,這我能忍嗎?這我不能忍,直接融入地圖吧, 你這個蛇兒。那星星呢?星星只是乙個點,他永遠都是乙個點而已,按照時間複雜度的計算法來看, 都沒有改變o(n),所以我就把它保留了,免得再寫個函式。

沒想到第一步就要知道這麼多東西吧。(看到這裡的都是熱愛學習的好寶寶呀)。

2. 地圖重新整理

這個地圖重新整理其實上面已經講過了, 並且也說了為什麼要那麼做,我們就不再細講,我們可以在繪製好地圖和蛇之後做這個地圖重新整理,至於星星,如果你現在就想畫可以直接跳到後面,不過因為星星的特殊功能,我們現在可以先不管他。

3. 運動

我想,動起來,是整個遊戲最最最重要的事情,不會動, 有什麼用呢?沒錯,沒有任何用處。

我們先來明確運動部分的**我們需要做的事情。

當按下方向鍵後, 蛇會按照按下的方向鍵運動(注意最終的目標不是馬上運動,不過我們是開發者,可以先寫個立刻執行的)。

當沒有輸入任何命令時,蛇按照上一次的運動方向繼續運動。(這個是第二階段的目標,我們先來看看怎麼實現第一階段的目標吧。)

確定方向鍵:w:上,s:下,a:左,d:右。

確定ascii值:w:119, s:115,a:97, d:100。

ok,確定之後就可以直接擼**了。

int move(struct snake *s, int *star)

if(sign == 2)

/* 死亡檢查 */

return check_dead(s);

}

看到這個如果你不去深究_move這個函式到底怎麼做的話,我相信你還是相當清晰到底是怎麼回事的,那麼我就接著將_move這個函式吧。

int _move(struct snake *s, int* star, int x, int y)

/* 修改目前運動方向 */

direct_x = x;

direct_y = y;

/* 增加新節點 */

struct snake *top = s->next;

struct snake *p = s->next;

struct snake *ntop;

ntop = (struct snake*)malloc(sizeof(struct snake));

ntop->i = top->i + x;

ntop->j = top->j + y;

s->next = ntop;

ntop->next = top;

/* 檢查是否吃到星星 */

if(check_star(s, star) == 1)

/* 刪除舊節點 */

while(p->next->next != null)

p->next = null;

return 0;

}

簡單講一下我沒有提出來的**(我也不準備後面貼出來),運動檢查是看看這個輸入的運動方向是否合理,比如不能倒退。還有更上面的死亡檢查,就是看看是否這麼走之後,蛇會不會撞死,我們知道它不能撞到自己,也不能撞到牆。

ok,這部分講完我們開始講上面貼出來的**,如果方向錯誤的話,我們會選擇使用原前進方向,也就說,如果你要倒退走的話,我們就直接照著之前的方向往前走,那麼運動到底是怎麼做到的,其實這裡就需要你有個更加大局的看法了,我們之前把蛇融入到地圖中去了,那麼蛇的每個節點中的元素i,j其實代表的就是map中i,j座標有蛇,且值是1或者2(當時蛇頭時),所以,我們只需要把蛇前進方向的格仔的座標變成蛇頭,變成蛇的新節點,然後刪除尾節點就可以了。(這是沒有吃到星星的情況下),如果吃到了星星,尾節點是不刪除的,這樣就可以實現長度+1。

好了,第一部分的運動我們說完了, 那麼怎麼做乙個自動運動的蛇呢?其實這也很簡單,只需要定時運動一下就好了, 前面我們知道我們有乙個獲取運動方向的函式,如下:

void getaction()

}else if(_kbhit() && sign == 1)

finish = clock();

duration = (double)(finish - start);

if(duration > speed)

break;

} return;

}

我們監視一定時長的鍵盤輸入,並且將第乙個有效輸入保留到全域性變數中的方向鍵ch中去,然後一直等到時間到了, 如果沒有有效輸入,就不做任何修改,我們會使用上次的方向鍵ch。這樣子我們就得到了乙個會執行一定時長的獲取方向的模組了, 接下來就是呼叫這個模組的事情了,(沒錯,迴圈呼叫就好了, 這樣就會沒過一定時間獲取一次,然後獲取後運動,也就可以實現一段時間運動一次的目的了)。

一開始是想用多執行緒的, 後來發現定時的方式更加穩定且簡單(畢竟多執行緒是乙個很麻煩的東西,弱雞傷不起)。

####4. 生成星星

生成星星的方式可以採用隨機生成座標的方式,然後檢查是否會不會撞牆,也可以採用我**中的方式,時間複雜度是相同的。

/* 生成星星 */

void generatestar(int **map, int *star, int height, int weight)

} return;

}

5. 增加蛇長

增加蛇長上面已經有講到了,就是吃到星星後, 頭部增加,尾部不刪,就可以實現增加蛇長。

6. 特殊情況檢查

其實這個上面也已經有提到了。

死亡檢查:是否撞到牆和撞到自己

運動檢查:是否有導著走這種錯誤指令

是否超時:這個是寫第二部分運動–自動運動的時候的定時功能。

輸入合理性檢查:如果不做這個的話,會導致輸入wsad之外的鍵導致整個遊戲暫停,我看一些部落格將這個當做暫停方式,我想這個更應該算是乙個bug,所以我選擇避免這種情況。

github:

c 實現貪吃蛇

include include include include include include include include include word square color 7 義方向 define key up 72 define key down 80 define key left 75...

貪吃蛇 c 實現

週末無聊,嘗試寫了下貪吃蛇。先上 include include include include include includeusing namespace std define up 72 define down 80 define left 75 define right 77 struct ...

C 實現貪吃蛇

vs 2015 easyx 蛇能上下左右移動 蛇能吃食物 能判斷蛇的死亡 蛇的長度,每節蛇的座標,蛇移動的方向 蛇初始化,移動,改變方向,吃食物,畫蛇,蛇是否死亡 食物的座標,食物是否被吃掉 初始化食物,新的食物,畫食物 因為蛇吃食物時需要知道食物的座標,所以需要獲得食物座標的方法 因為蛇吃食物後需...