1. 程式人生 > >貪吃蛇(修改版)

貪吃蛇(修改版)

#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”))