貪喫蛇中的小技術
程序設計思路
- 構造遊戲地圖,用矩陣實現,每一個元素就是一個點
- 構造蛇對象,有長度,身體,移動等屬性
- 遊戲時,蛇一直在移動,獲取到鍵盤輸入的消息後改變方向,繼續移動
- 構建食物對象,位置,以及被蛇喫掉後的位置更新
- 程序終止條件,也就是蛇不能撞到自己或者邊界,也就是輸了;當然爲了遊戲效果,設置一個最大值,當蛇的長度超過這個值,就判定爲win
關鍵點
由於是控制檯程序,因此實現貪喫蛇的關鍵點在於將蛇移動後的界面重繪並展示出來,並隨時獲取到鍵盤輸入的消息,以及食物被喫後的重新設定。
將每次移動後的畫面重現,初步構想是把每次界面中各元素的位置記錄下來,然後清楚屏幕內容,重新輸出。(MFC中易於實現,主要是除了redraw window,不知道還有什麼方法可以去除畫過的痕跡)但實際上並沒有必要,改變的只是蛇頭和蛇尾的位置,只需要改兩個點的位置的內容就好。
隨時獲取鍵盤消息,在MFC中很方便可以實現(雖然親自試驗並沒有起作用),但在控制檯程序裏,還需要引用一些庫中的函數。
食物的重新設定,主要是位置不要出界,不要落在蛇身在的位置。
引用頭文件
標準C++程序開頭,除了常見的,還需要
#include <iostream>
#include <ctime> //需要隨機設置食物的位置,時間作爲隨機種子
#include <conio.h> //響應鍵盤函數
#include <windows.h> //控制檯界面的控制
關鍵點的實現
void Snake::Move()
{
gotoxy(body[len - 1].x, body[len - 1].y);
cout <<' ';
for (int i = len - 1; i > 0; --i) {
this->body[i].x = this->body[i - 1].x;
this->body[i].y = this->body[i - 1].y;
}
if (this->dir == 1) {
this->body[0].x--;
}
else if (this->dir == 2) {
this->body[0].y++;
}
else if (this->dir == 3) {
this->body[0].x++;
}
else if (this->dir == 4) {
this->body[0].y--;
}
gotoxy(this->body[0].x, this->body[0].y);
cout <<'O';
}
這個是蛇的移動方法,同時表現了在界面裏的展示方法
if(kbhit()) //kbhit 非阻塞函數
{
ch=getch(); //使用 getch 函數獲取鍵盤輸入
switch (ch){
case 'A':
case 'a':
snake.Left();
break;
case 's':
case 'S':
snake.Down();
break;
case 'd':
case 'D':
snake.Right();
break;
case 'w':
case 'W':
snake.Up();
break;
}
}
這裏的 kbhit() 函數就是可以實現隨時響應鍵盤輸入的函數,效果比較好
void setFood(Snake &s){
food.x = rand() % 28 + 2;
food.y = rand() % 78 + 2;
int i;
int flag = 1;
while(flag){
for (i = 0; i < s.len; ++i){
if(s.body[i].x==food.x && s.body[i].y == food.y){
break;
}
}
if(i==s.len){
flag = 0;
gotoxy(food.x, food.y);
cout << '$';
}else{
food.x = rand() % 28 + 2;
food.y = rand() % 78 + 2;
}
}
return;
}
這個就是食物設置的方法,這個的28和78是邊界大小
問題探討
目前程序實現的基本還有一個蛇身重合,也就是自己會撞自己沒有加上去(懶得改了)。其他的沒有問題。
但對於鍵盤消息響應,一直耿耿於懷,查到了還有一種方法是
#define KEY_DOWN(VK_NONAME) ((GetAsyncKeyState(VK_NONAME)&0x8000)?1:0)
if(KEY_DOWN('A')){
······
}else if(){
······
}······
但是和kbhit()比起來,效果比較卡。在實現雙擊加速時,使用了條件嵌套,結果kbhit()效果也遜色不少,所以想着應該有更好的辦法,還是說在MFC等封裝好的圖形界面設計裏,優化效果會好點?這個目前還沒有找到答案。