貪吃蛇(修改版)
阿新 • • 發佈:2018-12-28
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<time.h> #include<windows.h> #include<mmsystem.h> #include<conio.h> #include<stdbool.h> #include"背景和畫蛇.h"//把佔空間大的放在標頭檔案中 #include"resource.h" #pragma comment(lib,"winmm.lib")//手動新增庫 void firstpage(void);//首頁 void firstpage_music(void);//首頁音樂 //void empty_cmd(void);//清空首頁 void stopmusic(void);//停止音樂 void startgame(void);//開始遊戲(按鈕) void ground(void);//遊戲邊框 void snake_pos(void);//蛇的隨機產生 void draw_snake(void);//畫蛇 void empty_snake(void);//去除舊蛇殘留 void change_dir(void);//改變方向 bool snake_die(void);//死亡判斷 void snake_food(void);//畫食物 void snake_up(void);//蛇變長 void gotoxy(); //首頁 void firstpage(void) { printf("\n"); printf("\n"); printf("\n"); printf(" ************************************************\n"); printf("\n"); printf("\n"); printf("\n"); printf(" * <來和程式張一起van♂遊戲> *\n"); printf(" * van♂心提示: *\n"); printf(" * 1. W A S D 控制老蛇的方向 *\n"); printf(" * 2. 按 開始遊戲 *\n"); printf("\n"); printf("\n"); printf("\n"); printf(" ************************************************\n"); printf("\n"); printf("\n"); printf("\n"); } //播放音樂(必須是wav格式的) void firstpage_music(void) { //絕對路徑:檔案的完整路徑 //相對路徑:和要執行的檔案所在的位置一樣 PlaySound(MAKEINTRESOURCE(IDR_WAVE2), NULL, SND_RESOURCE | SND_ASYNC);//載入資源在內部需要加標頭檔案 } void game_music(void) { PlaySound(MAKEINTRESOURCE(IDR_WAVE1), NULL, SND_RESOURCE | SND_ASYNC); } void startgame(void) { //按空格開始遊戲 char start; while (1)//無限迴圈 { start = _getch(); if (' ' == start)//輸入空格時跳出迴圈開始遊戲 { break; } } } //音樂停止(或者更換播放音樂) void stopmusic(void) { PlaySound(NULL, 0, 0); } //void empty_cmd(void) //{ // //清空控制檯介面,準備進入遊戲介面 // system("cls"); //} //專心畫背景emmmmm void background(void) { int i = 0; for (i = 0; i < 29; i++) { printf(black_ground[i]); } } void snake_pos(void) { //產生隨機數 srand((unsigned int)time(NULL));//種種子 //隨機數的範圍 int x = (rand() % 19) + 1;//1~19 int y = (rand() % 27) + 1;//1~27 //初始化蛇的三個節點(身體) //頭 arr_snake[0][0] = y;//行 arr_snake[0][1] = 2 * x;//列 arr_snake[0][2] = to_west;//方向 //身 arr_snake[1][0] = y;//行 arr_snake[1][1] = 2 * x + 2;//列 arr_snake[0][2] = to_west;//方向 //尾 arr_snake[2][0] = y;//行 arr_snake[2][1] = 2 * x + 4;//列 arr_snake[2][2] = to_west;//方向 draw_snake(); } void draw_snake(void) { int i = 0; for (i = 0; arr_snake[i][0] != 0; i++)//列印3個節點 { //字串自帶\0,所以"●"帶了三個字元,當我們直接賦值給陣列時會吧\0複製過去,會停止,所以要規定只複製兩個字元過去 //把放在蛇數組裡的三個節點拿到介面中 strncpy(&black_ground[arr_snake[i][0]][arr_snake[i][1]], "█", 2); } } //蛇動 void snake_move(void) { int i = snake_max_long - 1;//100個元素,但是下標只有99 empty_snake();//清除蛇殘留 for (i; i > 0; i--)//迴圈遍歷蛇陣列 { //因為我們初始化的蛇陣列都為0,所以遍歷時若為0說明不是蛇身體上的任一部分,可以過濾掉0的部分 if (0 == arr_snake[i][1]) { continue; } //把前一個節點的x座標給後一個節點,新的蛇的位置裝在了陣列中 arr_snake[i][0] = arr_snake[i - 1][0];//就是x的座標 arr_snake[i][1] = arr_snake[i - 1][1]; arr_snake[i][2] = arr_snake[i - 1][2]; } //蛇頭 arr_snake[0][2] = snake_dir;//將上下左右的移動賦值給它以進行下一步的判斷(南北時1和-1,東西時2和-2) if (to_east == arr_snake[0][2] || to_west == arr_snake[0][2])//當輸入的鍵是左右時執行if,上下執行else { arr_snake[0][1] += arr_snake[0][2];//左右移動列動,當輸入的是左右時就朝當前位置加輸入方向大小 (-2和2)位移 } else { arr_snake[0][0] += arr_snake[0][2];//上下移動行動,當輸入的是上下時就朝當前位置加輸入方向大小(-1和1)位移 } draw_snake(); } //去除舊蛇殘留 void empty_snake(void) { int i = 0; for (i = 0; arr_snake[i][0] != 0; i++) { strncpy(&black_ground[arr_snake[i][0]][arr_snake[i][1]], " ", 2); } } //改變方向 void change_dir(void) { if (GetAsyncKeyState('W')) { if (arr_snake[0][2] != to_south) { snake_dir = to_north; } } else if (GetAsyncKeyState('S')) { if (arr_snake[0][2] != to_north) { snake_dir = to_south; } } else if (GetAsyncKeyState('A')) { if (arr_snake[0][2] != to_east) { snake_dir = to_west; } } else if (GetAsyncKeyState('D')) { if (arr_snake[0][2] != to_west) { snake_dir = to_east; } } } //蛇死亡判斷 bool snake_die(void) { if (to_west == arr_snake[0][2] || to_east == arr_snake[0][2]) { //老蛇左右移動的時候行不動列動,arr_snake[0][0](行),arr_snake[0][1] + arr_snake[0][2](列) //這裡代表的是列座標+輸入的鍵位大小,即是下一個座標,並對下一個座標進行判斷 //strncmp若返回0則代表兩個比較的字串相等(需要的引數是字串的首地址) if (0 == (strncmp(&black_ground[arr_snake[0][0]][arr_snake[0][1] + arr_snake[0][2]], "█", 2) /*|| 0 == (strncmp(&black_ground[arr_snake[0][0]][arr_snake[0][1] + arr_snake[0][2]], "〇", 2))*/))//背景上蛇頭的下一個點 { return false; } } else { //列不動行動 if (0 == (strncmp(&black_ground[arr_snake[0][0] + arr_snake[0][2]][arr_snake[0][1]], "█", 2) /*|| 0 == (strncmp(&black_ground[arr_snake[0][0] + arr_snake[0][2]][arr_snake[0][1]], "〇", 2))*/)) { return false; } } return true; } //產生食物 void snake_food(void) { //產生隨機座標 //判斷是否需要產生新的食物 if (crea_food == 0)//不需要產生食物 { return; } srand((unsigned int)time(NULL)); while (1) { int mark = 1; //因為死亡判斷原因,食物放在邊框旁的話蛇會無法獲得並轉身(或轉身後無法獲取),所以我們+2使其陣列下標x=2,y=2開始 food_x = (rand() % 18) + 2;//實際範圍應該是1~21,隨機範圍低於21都行,(0~20)+1==1~21 food_y = (rand() % 25) + 2; int i;//遍歷蛇身 for (i = 0; arr_snake[i][0] != 0; i++) { if (food_x * 2 == arr_snake[i][1] && food_y == arr_snake[i][0])//食物的橫縱座標和蛇的橫縱座標做對比,若重合,則重新產生一個隨機數 { mark = 0;//標記當前的不滿足不重合的條件,不能跳出while迴圈 break;//一有重合部分就跳出for迴圈,不用每個節點都判斷是否重合,提高效率 } } if (mark == 1) { break;//滿足條件後跳出while迴圈 } } //畫食物 strncpy(&black_ground[food_y][food_x * 2], "※", 2);//橫座標乘2,兩位元組,背景陣列為[y][x] crea_food = 0;//產生食物後賦值一個0,防止又有食物產生 } //蛇變長 void snake_up(void) { //蛇頭座標跟食物座標重合時尾巴增長1 if (arr_snake[0][0] == food_y && arr_snake[0][1] == food_x * 2) { //增長 if (arr_snake[0][2] == to_east) { arr_snake[snake_long + 1][0] = arr_snake[snake_long][0];//向東西移動時縱座標不變 arr_snake[snake_long + 1][1] = arr_snake[snake_long][1] + to_west;//向東移動時蛇尾座標往西加一格 arr_snake[snake_long + 1][2] = arr_snake[snake_long][2];//方向不變 } else if (arr_snake[0][2] == to_west) { arr_snake[snake_long + 1][0] = arr_snake[snake_long][0];//向東西移動時縱座標不變 arr_snake[snake_long + 1][1] = arr_snake[snake_long][1] + to_east; arr_snake[snake_long + 1][2] = arr_snake[snake_long][2];//方向不變 } else if (arr_snake[0][2] == to_north) { arr_snake[snake_long + 1][0] = arr_snake[snake_long][0] + to_south; arr_snake[snake_long + 1][1] = arr_snake[snake_long][1];//向上下移動時橫座標不變 arr_snake[snake_long + 1][2] = arr_snake[snake_long][2];//方向不變 } else if (arr_snake[0][2] == to_south) { arr_snake[snake_long + 1][0] = arr_snake[snake_long][0] + to_north; arr_snake[snake_long + 1][1] = arr_snake[snake_long][1];//向東西移動時縱座標不變 arr_snake[snake_long + 1][2] = arr_snake[snake_long][2];//方向不變 } snake_long++; crea_food = 1;//重置食物標記為true g_score += 10;//吃一次食物加10分 } } //記分 void score(void) { COORD rd; rd.X = 55; rd.Y = 15; //設定游標位置在遊戲介面右側 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), rd); printf("分數:%d", g_score); COORD sd; sd.X = 55; sd.Y = 16; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), sd); if (to_east == arr_snake[0][2]) { printf("對對對,向左"); } if (to_west == arr_snake[0][2]) { printf("對對對,向右"); } if (to_north == arr_snake[0][2]) { printf("對對對,向下"); } if (to_south == arr_snake[0][2]) { printf("對對對,向上"); } } int main(void) { //首頁音樂 firstpage_music(); //顯示首頁 firstpage(); //空格進入遊戲介面 startgame(); //停止音樂 stopmusic(); game_music(); //清空控制檯介面以顯示背景 system("cls"); //隨機位置顯示蛇,因為只需要顯示一開始的一次,因此需要放在遊戲外 snake_pos(); background(); //背景,重複呼叫函式內容以畫背景 while (1) { gotoxy(0, 0);//清空了控制檯並解決了閃爍 snake_food();//產生食物 snake_up();//蛇變長 change_dir();//改變方向 //別把清除蛇殘留寫在這,因為在畫完蛇以後直接清除的畫相當於每次都重新畫了一條蛇,是無法判斷下一個位點是否是自己的 if (false == snake_die()) { system("cls"); printf("恭喜你,你死了\n"); printf("辣雞,分數才: %d\n", g_score); break; } snake_move();//蛇動 background();//背景 score();//分數 Sleep(100); } system("pause"); return 0; } //解決閃爍問題 void gotoxy(unsigned char x, unsigned char y) { COORD cor; HANDLE hout; cor.X = x; cor.Y = y; hout = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(hout, cor); }
解決了貪吃蛇撞自己不會死的問題(呼叫邏輯使我瘋狂),以及頻閃問題(不使用system(“cls”))