如果要用鍵盤來控制移動方向(w:上 s:下 a:左 d:右),就要讓電腦知道當下按的鍵,並馬上做出反應,再根據按鍵來決定移動方向。
這裡可以用 _kbhit() 以及 _getch() 這兩個函式來實現此目標。
_kbhit() 是用來檢查當前是否有鍵盤輸入,若否,則回傳 false(無論有無按鍵都會立刻回傳)。
_getch() 可以從鍵盤讀入一個字元,回傳字元的 ASCII 值,若沒輸入就會一直卡在那邊。
使用 _kbhit(), _getch() 需要 #include <conio.h>
鍵盤讀取
首先,我們先嘗試讓電腦可以不斷印出當下的按鍵為何,用 _kbhit() 來判斷是否有按鍵,若有,則用 _getch() 接收此按鍵,並將其輸出,外面用一個無窮迴圈包著使其可以重複判斷讀取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <bits/stdc++.h> #include <conio.h> using namespace std;
int main(){ char Key; while(true){ if(_kbhit()){ Key = _getch(); cout << Key << "\n"; } } return 0; }
|
結果
移動
接著,就可以開始想辦法讓蛇在螢幕上顯示出來,並根據按鍵來移動。
下面是我最初的做法,用 x, y 來儲存蛇當前的位置,若有輸入,判斷按鍵為何,更新 x, y 值,最後用迴圈跑,在對應的 x, y 位置輸出蛇的代號,否則輸出空白,並且適時的換行,在下一次輸入時使用 system(“CLS”) 來達成清屏效果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <bits/stdc++.h> #include <conio.h> using namespace std;
int main(){ char Key; int x = 10, y = 10; while(true){ if(_kbhit()){ system("CLS"); Key = _getch(); if(Key == 'd') ++x; else if(Key == 'a') --x; else if(Key == 'w') --y; else if(Key == 's') ++y; for(int i = 0; i < 20; ++i){ for(int j = 0; j < 20; ++j){ if(y == i && x == j){ cout << "S"; } else cout << " "; } cout << "\n"; } } } return 0; }
|
這當然不是一個好方法,實在太沒效率了,於是我後來查了一下發現,有 gotoxy() 這個強大的函式,可以用來控制游標位置,舉例來說,如果蛇的位置在 (10, 16),就可以這樣寫
1 2
| gotoxy(10, 16); cout << "S";
|
就這?
沒錯,多麼輕鬆愉快,完全不用使用迴圈,也不用清屏,只要將移動前的位置輸出空白就好了
1 2 3 4
| gotoxy(10, 16); cout << " "; gotoxy(10, 17); cout << "S";
|
gotoxy() 不是內建的,使用前要自己定義
1 2 3 4 5 6 7
| void gotoxy(int xpos, int ypos) { COORD scrn; HANDLE hOuput = GetStdHandle(STD_OUTPUT_HANDLE); scrn.X = xpos; scrn.Y = ypos; SetConsoleCursorPosition(hOuput,scrn); }
|
使用 gotoxy() 需要 #include <windows.h>
改良之後的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <bits/stdc++.h> #include <conio.h> #include <windows.h> using namespace std;
void gotoxy(int xpos, int ypos) { COORD scrn; HANDLE hOuput = GetStdHandle(STD_OUTPUT_HANDLE); scrn.X = xpos; scrn.Y = ypos; SetConsoleCursorPosition(hOuput,scrn); }
int main(){ char Key; int x = 10, y = 10; while(true){ if(_kbhit()){ gotoxy(x, y); cout << " ";
Key = _getch(); if(Key == 'd') ++x; else if(Key == 'a') --x; else if(Key == 'w') --y; else if(Key == 's') ++y; gotoxy(x, y); cout << "S"; } } return 0; }
|
結果
連續移動
目前只有按按鍵才會移動,這樣你滿足了嗎?
NO, I am curious and interested!!
我們要讓蛇可以自動依照最後一個按鍵方向移動,只要將 _getch() 以外的東西移到 _kbhit()判斷條件外,如此一來就算沒有鍵盤輸入也會按照前一次更新的 Key 值做移動。
但是由於程式運行速度太快,蛇會以10毫秒16步來移動,這顯然不是我們要的,因此,我們需要使用 Sleep() 函式,傳入的值代表程式運行到這裡會在此卡多久(毫秒),製造移動的時間差。
使用 Sleep() 需要 #include <windows.h>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <bits/stdc++.h> #include <conio.h> #include <windows.h> using namespace std;
void gotoxy(int xpos, int ypos) { COORD scrn; HANDLE hOuput = GetStdHandle(STD_OUTPUT_HANDLE); scrn.X = xpos; scrn.Y = ypos; SetConsoleCursorPosition(hOuput,scrn); }
int main(){ char Key = 'd'; int x = 10, y = 10; while(true){ if(_kbhit()){ Key = _getch(); } gotoxy(x, y); cout << " ";
if(Key == 'd') ++x; else if(Key == 'a') --x; else if(Key == 'w') --y; else if(Key == 's') ++y;
gotoxy(x, y); cout << "S"; Sleep(200); } return 0; }
|
結果
基本上長差不多,只是鍵盤不用一直按
隱藏光標
一直閃看了很不舒服,現在我有 HideCursor() 可以隱藏光標,使用前要自己定義
1 2 3 4
| void HideCursor(){ CONSOLE_CURSOR_INFO cursor_info = {1, 0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); }
|
在 main() 一開始呼叫即可
1 2 3 4 5 6 7 8 9 10 11 12 13
| 以上省略
void HideCursor(){ CONSOLE_CURSOR_INFO cursor_info = {1, 0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info); }
int main(){ HideCursor(); ... ... return 0; }
|
結果
看起來多麼舒適~
移動的部分目前就先告一段落ㄌ