設計的小游戲,初學者可以加點自己想加的其他功能。
游戲規則
貪食蛇游戲要求玩家控制方向鍵(或WSAD鍵)來控制小蛇的前進方向,以使蛇吃掉面板上隨即位置上的食物。每次成功吃掉食物后小蛇體長將增加一點,得分增加。當小蛇撞到邊界或者蛇頭與蛇身相撞時,蛇將掛掉,游戲隨之結束。
游戲結構設計
游戲應當包含初始歡迎界面,游戲界面,游戲結束界面。
建立一個CGame類,來管理游戲的進度。該類放在Game.h文件中,在該類中可以分別定義NewGame(),PlayGame(),SetGame()和ExitGame()四個函數來控制游戲的各個單元,為了使整個程序看起來更像個游戲,可以采取更加漂亮的界面來展示游戲各部分。
NewGame()函數設定游戲歡迎界面。可以簡單地輸出了一些方塊字符組成的游戲名SNAKE和一句提示“Press any key to start…”。點擊任意鍵后,游戲程序將轉入SetGame()中繼續執行。可以加上一些動態效果,讓提示”Press any keyto start…”不斷跳動。
SetGame()中包括游戲的設置內容。可以選擇Easy,Normal,Hard三個選項。這三個選項將對應小蛇不同的的移動速度,具體來說將體現在PlayGame()函數中每次循環執行速度。設置完成后,游戲程序將轉入PlayGame()繼續執行。
PlayGame()函數主體將是一個死循環,因為可將游戲考慮成一個無窮的循環,循環中迭代的每一步都依次進行:判斷用戶是否輸入、然后根據用戶輸入調整游戲內容(如果沒有輸入則按默認方式繼續執行游戲)、判斷是否符合規則(不符合則跳出循環,轉入ExitGame()退出游戲)、判斷是否需要加分扣分。執行完以上這些步驟后,將進行下一次迭代。當然進行游戲之前,還要執行必要的初始化工作,來顯示大體框架和提示信息。
EitGame()中將顯示游戲得分,并詢問玩家是否再玩一次。這里拼出了一個骷髏頭的圖案,表示Game Over。
以上為游戲的主體內容,這四個函數設定了游戲的基本結構,剩余部分將繼續考慮細節問題。然后再展示Game.h的細節內容。
建立游戲對象
先建立一系列類表示游戲對象,其中應包括對游戲對象的處理方式(函數)。分析游戲,可以知道游戲主體是小蛇和食物。
所有的游戲對象,包括蛇和食物,都是由控制臺上的一系列點組成的。因此需要很多處理點對象的方法。可建立Point.h來定義CPoint對象,來簡化其他對象的處理。
代碼如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- #include <conio.h>
- #include <windows.h>
- #define W 32
- #define H 22
- const int SIZE_SK = 30*20;
- HANDLE hOut;
- COORD pos= {0, 0};
- CONSOLE_CURSOR_INFO cur_info = {1, 0};
- struct snake {
- short x, y; //節點坐標
- short md; //節點運動方向
- short num; //節點內容
- int col; //節點顏色
- } snake[30*20];
- const short dx[4] = {-1, 1, 0, 0}; //方向
- const short dy[4] = {0, 0, -1, 1}; //方向
- short gamemap[W][H];
- short head, tail, score=0;
- void initmap(int tm);
- void createFood(void);
- int getkeys(void);
- int move(int idx);
- void pntSnake(int i);
- void drawEdge(void);
- void gameinfo(char *s, int n);
- int main(void)
- {
- int tm = 150; //刷新間隔150毫秒
- hOut = GetStdHandle(STD_OUTPUT_HANDLE);
- SetConsoleCursorInfo(hOut, &cur_info); //隱藏光標
- system("COLOR 2f");
- initmap(tm);
- return 0;
- }
- void initmap(int tm)
- {
- short x, y, i, j;
- int index, tmp, ret_m;
- while (1){
- head=0;
- tail=-1;
- score=0;
- //初始化蛇節點隨機顏色
- srand(time(0));
- for (i=0; i<SIZE_SK; i++)
- snake[i].col = rand()%8+8 | 0x20;
- //初始化地圖數組
- for (i=0; i<W; i++)
- for (j=0; j<H; j++)
- gamemap[i][j] = 0;
- system("cls");
- //繪制邊界
- drawEdge();
- //打印蛇頭
- srand(time(0));
- do {
- x = rand()%(W-2) + 1;
- y = rand()%(H-2) + 1;
- } while (gamemap[x][y] != 0); //只在空白處生成
- snake[head].x = x;
- snake[head].y = y;
- gamemap[x][y] = snake[head].num = 1;
- pos.X = x*2;
- pos.Y = y;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- //打印食物
- createFood();
- while ((index=getkeys()) == 4)
- Sleep(500);
- //循環開始
- while (1){
- ret_m = move(index);
- if (ret_m == 1){
- pos.X = 0; pos.Y = H;
- SetConsoleCursorPosition(hOut, pos);
- printf("哎呀,掛了哦 ^_^");
- break;
- }
- else if (ret_m == 2){
- pos.X = 0; pos.Y = H;
- SetConsoleCursorPosition(hOut, pos);
- printf("哎呀,超神了 ^_^");
- break;
- }
- Sleep(tm);
- if ((tmp = getkeys()) != 4)
- index = tmp;
- }
- //菜單代碼
- puts(" 按w\\s\\a\\d 繼續玩,退出請關閉我");
- getch();
- }
- }
- void createFood(void)
- {
- short x, y;
- srand(time(0));
- do {
- x = rand()%(W-2) + 1;
- y = rand()%(H-2) + 1;
- } while (gamemap[x][y] != 0);
- gamemap[x][y] = 2;
- pos.X = x*2; pos.Y = y;
- SetConsoleCursorPosition(hOut, pos);
- SetConsoleTextAttribute(hOut, 0x2f);
- printf("⊙");
- }
- int getkeys(void) //獲取方向鍵
- {
- char ch;
- while(kbhit()){
- ch = getch();
- if(ch == 'w')
- return 2;
- if(ch == 's')
- return 3;
- if(ch == 'a')
- return 0;
- if(ch == 'd')
- return 1;
- while (kbhit());
- }
- return 4;
- }
- int move(int idx)
- {
- int newx, newy, pt_tail, i;
- newx = snake[head].x + dx[idx];
- newy = snake[head].y + dy[idx];
- switch (gamemap[newx][newy]){
- case 0:
- if ((head = ++head%SIZE_SK) != tail){ //不相等即蛇節點隊列未滿
- snake[head].x = newx;
- snake[head].y = newy;
- snake[head].num = 1;
- gamemap[newx][newy] = 1;
- tail = ++tail%SIZE_SK;
- snake[tail].num = 0; //清除尾部,打印空字符
- gamemap[snake[tail].x][snake[tail].y] = 0;
- snake[head].md = idx; //存儲移動方向
- pt_tail = tail - 1;
- }
- else
- return 2; //回合結束標記2代表蛇填滿空間
- break;
- case 2:
- if ((head = ++head%SIZE_SK) != tail){ //不相等即蛇節點隊列未滿
- snake[head].x = newx; //head+1后存儲新坐標為蛇頭
- snake[head].y = newy;
- snake[head].num = 1;
- gamemap[newx][newy] = 1;
- snake[head].md = idx; //存儲移動方向
- pt_tail = tail = tail%SIZE_SK;
- score++;
- createFood();
- }
- break;
- case 3:
- return 1; //遇到墻壁
- case 1:
- return 1; //遇到自身
- default: ;
- }
- //打印蛇
- if (pt_tail == -1)
- pt_tail++;
- if (pt_tail < head){ //尾巴在頭部后面(這兩者是數組索引)
- for (i=pt_tail; i<=head; i++){
- pntSnake(i); //打印
- }
- }
- else{
- for (i=pt_tail; i<SIZE_SK; i++)
- pntSnake(i);
- for (i=0; i<=head; i++)
- pntSnake(i);
- }
- gameinfo(" 分數", score);
- return 0;
- }
- void pntSnake(int i)
- {
- //轉換x坐標:內部是連續整數,打印時一個字符占位2,所以要2x
- pos.X = snake[i].x * 2;
- pos.Y = snake[i].y;
- SetConsoleCursorPosition(hOut, pos);
- if (snake[i].num == 0){
- putchar('\0'); //清除蛇尾
- putchar('\0');}
- else if (snake[i].num == 1){
- //下面函數第二個參數低4位控制前景色,高4位控制背景色,共8位
- SetConsoleTextAttribute(hOut, snake[i].col);
- printf("█");
- }
- }
- void drawEdge(void)
- {
- int i;
- for (i=0; i<W; i++){
- pos.X = i*2; pos.Y = 0;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[i][pos.Y] = 3;
- pos.Y = H-1;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[i][H-1] = 3;
- }
- for (i=1; i<H-1; i++){
- pos.X = 0; pos.Y = i;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[pos.X][i] = 3;
- pos.X = (W-1)*2;
- SetConsoleCursorPosition(hOut, pos);
- printf("█");
- gamemap[W-1][i] = 3;
- }
- }
- void gameinfo(char *s, int n)
- {
- pos.X = 0; pos.Y = H;
- SetConsoleCursorPosition(hOut, pos);
- SetConsoleTextAttribute(hOut, 0x2f);
- printf(" 貪吃蛇");
- printf(" %s: %d", s, n);
- }
復制代碼
|