1. 程式人生 > >C語言小遊戲————貪吃蛇.c

C語言小遊戲————貪吃蛇.c

1.主函式框架的搭建

int main (void)
{
	starup();//資料初始化
	while(1)
	{
		show();//列印畫面
		updateWithoutInput();//與使用者輸入無關的更新
		updateWithInput();//與使用者輸入有關的更新 
	} 
	return 0;
} 
說明:

    a.startup()對全域性變數的初始化;

    b.while(1)為死迴圈:

每次迴圈進行的內容:

    c show().先清屏在列印一次畫面(元素:圍牆,蛇頭,蛇身,果子,空白);

    d.updateWithoutInput()進行與輸入無關的更新(判斷是否吃到的果子,食物的重新整理);

    e.updateWithInput()進行與輸入有關的更新(蛇的移動,方向的改變);

2.全域性變數的設定

#define High 20//地圖的尺寸 
#define Width 30
#define SerpentNum 200//蛇長的最大值 

//全域性變數
int serpent_x[SerpentNum],serpent_y[SerpentNum];//蛇的座標 
int serpent_long; //蛇的長度 
int canvas[High][Width]={0};//地圖對應元素 0空格 1蛇 2食物 3圍牆  4蛇頭 
int food_x,food_y;//食物的座標 
int score; //分數 
char input;//方向的控制 
說明:

   a.對於畫面上所有的元素均用兩個值即可確定位置,所以每個點都(x,y)表示,但是不同於直角座標系,(x,y)表示x行y列;

   b.起始行由於以陣列形式儲存所以,從0行0列開始;

   c.地圖的列印使用的是二維陣列,第一維表示列,第二維表示行,例canvas[i][j]表示i行j列;

3.第一個函式startup():對資料的初始化

void startup()
{
	serpent_x[0]=High/2;serpent_y[0]=Width/3;//蛇的初始位置 
	serpent_x[1]=High/2;serpent_y[1]=Width/3+1;
	serpent_long=2;
	food_x=High/3;food_y=Width/3;//食物
	score=0;
	
	int i,j;
	canvas[serpent_x[0]][serpent_y[0]]=4;
	canvas[serpent_x[1]][serpent_y[1]]=1;
	canvas[food_x][food_y]=2;
	for(i=0;i<High;i++)
		for(j=0;j<Width;j++)
			if(i==0||j==0||i==High-1||j==Width-1)
				canvas[i][j]=3; 	 
}

說明:

     a.蛇,食物座標可以隨意設定;

     b.對地圖中元素的初始化:0空格 1蛇 2食物 3圍牆  4蛇頭;在定義時已經對二維陣列進行了初始化賦0;

     c.對圍牆元素的初始化:使用兩重迴圈,將行數為0和Higj-1列數為0和Width-1的點全部賦值為3;

4.第二個函式show():畫面列印

void gotoxy(int x,int y)
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X=x;
	pos.Y=y;
	SetConsoleCursorPosition(handle,pos);
} 
void show()//畫面列印 
{
	gotoxy(0,0);
	int i,j;
	for(i=0;i<High;i++)
	{
		for(j=0;j<Width;j++)
		{
			if(canvas[i][j]==0)
				printf(" ");
			else if(canvas[i][j]==1)
				printf("+");
			else if(canvas[i][j]==2)
				printf("@");
			else if(canvas[i][j]==3)
				printf("*");
			else if(canvas[i][j]==4)
				printf("O");
		}
		printf("\n");
	}	
	printf("得分;%d",score);
}

說明:

    a.對於gotoxy()功能為返回某點重新列印,即相當於清屏函式system("cls"),但是不存在閃屏問題。具體原理牽扯到windows系統的操作,嗯,本人不太懂(๑• . •๑)

使用時需載入#include <windows.h>標頭檔案

    b.列印效果如下:


5.第三個函式updateWithoutInput()與輸入無關的更新

void updateWithoutInput()//與輸入無關的更新 
{
    int i;
    if(serpent_x[0]==food_x&&serpent_y[0]==food_y)//如果蛇吃到果子 
    {
        score++;
        
        srand((unsigned)time(NULL));//食物重新整理
        food_x=rand()%(High-3)+1;
        food_y=rand()%(Width-3)+1;
        canvas[food_x][food_y]=2;
        
        serpent_long++;//蛇的增長 
    }
    if((serpent_x[0]==0||serpent_y[0]==0||serpent_x[0]==High-1||serpent_y[0]==Width-1))
    {
        printf("死亡遊戲結束!\n");//撞牆死亡 
        getch();
        exit(1);
    }
    for(i=1;i<serpent_long;i++)
        if(serpent_x[0]==serpent_x[i]&&serpent_y[0]==serpent_y[i])
        {
            printf("死亡遊戲結束!\n");//碰到自己身子死亡 
            getch();
            exit(1);
        }
}
說明:

    a.判斷蛇是否吃到果子:即蛇頭的座標等於食物的座標serpent_x[0]==food_x&&serpent_y[0]==food_y;   

    b.如果蛇吃到果子:食物座標變0,蛇身加長(只需要加長,在下一個函式中自動實現賦值1)

    b.食物的重新整理:使用rand()函式獲得隨機數,然後對(High-3)取餘,使食物的座標落在圍牆內;

     c. srand((unsigned)time(NULL))使用時間函式作隨機數的種子;

   e.死亡判定,如果蛇頭座標等於牆的座標死亡;如果等於蛇身的座標死亡

   f.死亡後,先提示,然後輸入任意鍵退出,exit(1)退出程式

6.第四個函式updateWithInput() 與輸入有關的更新

void updateWithInput()//與輸入有關的更新 
{
	int i=1;
	if(kbhit())//輸入方向
		input=getch();
	if(input=='w'||input=='s'||input=='a'||input=='d')//蛇的移動
	{	
		canvas[serpent_x[serpent_long-1]][serpent_y[serpent_long-1]]=0;
		for(i=serpent_long-1;i>0;i--)
		{
			serpent_x[i]=serpent_x[i-1];
			serpent_y[i]=serpent_y[i-1];
			canvas[serpent_x[i]][serpent_y[i]]=1;
		}
	}
	
	if(input=='w')//頭的移動
		serpent_x[0]--;
	else if(input=='s')
		serpent_x[0]++;
	else if(input=='a')
		serpent_y[0]--;
	else if(input=='d')
		serpent_y[0]++;
		
	canvas[serpent_x[0]][serpent_y[0]]=4;
	
}

說明:

    a.四個函式中,這個函式最麻煩也是最難理解的。

    b.輸入方向:if(kbhit())   input=getch(); kbhit()函式作用為檢測當前是否有輸入的值,有的話返回1,getch()作用:接受鍵盤輸入的字元,並返回;類似於scanf("%c",& input);但是不需要回車鍵表示輸入完成;

    c.蛇身的移動:讓蛇從第二個節點開始等於前一個節點,讓最後一個節點變為零(這個方法想了很久);

    d.方向的控制:根據鍵盤輸入的方向鍵(wasd)改變蛇頭的座標,注意,input一定要設為全域性變數,在其他控制檯小遊戲中如果想實現輸入動,不輸入不動,則設為自動區域性變數;

7.完整程式碼

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h> 

#define High 20//地圖的尺寸 
#define Width 30
#define SerpentNum 200//蛇長的最大值 

//全域性變數
int serpent_x[SerpentNum],serpent_y[SerpentNum];//蛇的座標 
int serpent_long; //蛇的長度 
int canvas[High][Width]={0};//地圖對應元素 0空格 1蛇 2食物 3圍牆  4蛇頭 
int food_x,food_y;//食物的座標 
int score; //分數 
char input;//方向的控制 

void gotoxy(int x,int y)
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X=x;
	pos.Y=y;
	SetConsoleCursorPosition(handle,pos);
} 

void startup()
{
	serpent_x[0]=High/2;serpent_y[0]=Width/3;//蛇的初始位置 
	serpent_x[1]=High/2;serpent_y[1]=Width/3+1;
	serpent_long=2;
	food_x=High/3;food_y=Width/3;//食物
	score=0;
	
	int i,j;
	canvas[serpent_x[0]][serpent_y[0]]=4;
	canvas[serpent_x[1]][serpent_y[1]]=1;
	canvas[food_x][food_y]=2;
	for(i=0;i<High;i++)
		for(j=0;j<Width;j++)
			if(i==0||j==0||i==High-1||j==Width-1)
				canvas[i][j]=3; 	 
}

void show()//畫面列印 
{
	gotoxy(0,0);
	int i,j;
	for(i=0;i<High;i++)
	{
		for(j=0;j<Width;j++)
		{
			if(canvas[i][j]==0)
				printf(" ");
			else if(canvas[i][j]==1)
				printf("+");
			else if(canvas[i][j]==2)
				printf("@");
			else if(canvas[i][j]==3)
				printf("*");
			else if(canvas[i][j]==4)
				printf("O");
		}
		printf("\n");
	}	
	printf("得分;%d",score);
}

void updateWithoutInput()//與輸入無關的更新 
{
    int i;
    if(serpent_x[0]==food_x&&serpent_y[0]==food_y)//如果蛇吃到果子 
    {
        score++;
        
        srand((unsigned)time(NULL));//食物重新整理
        food_x=rand()%(High-3)+1;
        food_y=rand()%(Width-3)+1;
        canvas[food_x][food_y]=2;
        
        serpent_long++;//蛇的增長 
    }
    if((serpent_x[0]==0||serpent_y[0]==0||serpent_x[0]==High-1||serpent_y[0]==Width-1))
    {
        printf("死亡遊戲結束!\n");//撞牆死亡 
        getch();
        exit(1);
    }
    for(i=1;i<serpent_long;i++)
        if(serpent_x[0]==serpent_x[i]&&serpent_y[0]==serpent_y[i])
        {
            printf("死亡遊戲結束!\n");//碰到自己身子死亡 
            getch();
            exit(1);
        }
}

void updateWithInput()//與輸入有關的更新 
{
	int i=1;
	if(kbhit())
		input=getch();
	if(input=='w'||input=='s'||input=='a'||input=='d')
	{	
		canvas[serpent_x[serpent_long-1]][serpent_y[serpent_long-1]]=0;
		for(i=serpent_long-1;i>0;i--)
		{
			serpent_x[i]=serpent_x[i-1];
			serpent_y[i]=serpent_y[i-1];
			canvas[serpent_x[i]][serpent_y[i]]=1;
		}
	}
	
	if(input=='w')
		serpent_x[0]--;
	else if(input=='s')
		serpent_x[0]++;
	else if(input=='a')
		serpent_y[0]--;
	else if(input=='d')
		serpent_y[0]++;
		
	canvas[serpent_x[0]][serpent_y[0]]=4;
	
}

int main(void)
{
	startup();
	while(1)
	{
		show();
		updateWithoutInput();
		updateWithInput(); 
	}
	return 0;
}

8.總結:

    優點:

        a.函式功能明確,找錯好找

        b.思路較為簡單

   缺點:

       a.功能不太完善

       b.蛇的橫向和縱向移動速度不同

       c.食物重新整理存在bug

   後續優化:

       a.暫停功能

       b.難度的遞增

說明:在這裡非常感謝知乎上童晶老師的做遊戲,學程式設計的專欄。程式大方向(框架)是由他提供的,我結合他的教程自己製作的貪吃蛇。


第二次更新:

a.可以暫停

b.難度的遞增

c.修復食物重新整理bug(原因,食物被重新整理到蛇的體內)

#include 
#include 
#include 
#include 
#include  

#define High 20//地圖的尺寸 
#define Width 30
#define SerpentNum 200//蛇長的最大值 

//全域性變數
int serpent_x[SerpentNum],serpent_y[SerpentNum];//蛇的座標 
int serpent_long; //蛇的長度 
int canvas[High][Width]={0};//地圖對應元素 0空格 1蛇 2食物 3圍牆  4蛇頭 
int food_x,food_y;//食物的座標 
int score; //分數 
char input;//方向的控制 
int speed; 

 
void gotoxy(int x,int y)
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X=x;
	pos.Y=y;
	SetConsoleCursorPosition(handle,pos);
} 
void HideCursor()
{
 	CONSOLE_CURSOR_INFO cursor_info = {1, 0}; 
 	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}
void startup()
{
	serpent_x[0]=High/2;serpent_y[0]=Width/3;//蛇的初始位置 
	serpent_x[1]=High/2;serpent_y[1]=Width/3+1;
	serpent_long=2;
	food_x=High/3;food_y=Width/3;//食物
	score=0;
	speed=5;
	
	int i,j;
	canvas[serpent_x[0]][serpent_y[0]]=4;
	canvas[serpent_x[1]][serpent_y[1]]=1;
	canvas[food_x][food_y]=2;
	for(i=0;i0)
		speed--;
		
	}
	if((serpent_x[0]==0||serpent_y[0]==0||serpent_x[0]==High-1||serpent_y[0]==Width-1))
	{
		printf("死亡遊戲結束!\n");//撞牆死亡 
		getch();
		exit(1);
	}
	for(i=1;i0;i--)
			{
				serpent_x[i]=serpent_x[i-1];
				serpent_y[i]=serpent_y[i-1];
				canvas[serpent_x[i]][serpent_y[i]]=1;
			}
		}
	
		if(input=='w')
			serpent_x[0]--;
		else if(input=='s')
			serpent_x[0]++;
		else if(input=='a')
			serpent_y[0]--;
		else if(input=='d')
			serpent_y[0]++;
		
		canvas[serpent_x[0]][serpent_y[0]]=4;
		v=0;
	
	}
		canvas[food_x][food_y]=2;
	
}

int main(void)
{
	startup();
	while(1)
	{
		show();
		updateWithoutInput();
		updateWithInput(); 
	}
	return 0;
}