效果圖如圖
首先發現組成元素是“實心方塊”我們可以百度 也可以在我這裏直接複製 ▇ 進編譯環境 (這個方塊是兩個字節這個很重要)
完成這個小程序基本上我們分以下幾步
1.完成所有靜態的元素(四周的方塊界線)
2.繪製蛇
3.使蛇吃東西
下面分佈進行實現
首先完成第一步
/*****************************************************************頭文件**********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
/*****************************************************************函數聲明*********************************************************/
void muban();
void Pos(int x, int y);
/*****************************************************************自定義函數*******************************************************/
void Pos(int x, int y)//設置光標位置,從哪裏開始輸出
{
COORD pos;//表示一個字符在控制檯屏幕上的座標,左上角(0,0)
HANDLE hOutput;
pos.X = x;
pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回標準的輸入、輸出或錯誤的設備的句柄,也就是獲得輸入、輸出/錯誤的屏幕緩衝區的句柄
SetConsoleCursorPosition(hOutput, pos);
}
void muban()
{
int i;
for(i=0;i<=60;i+=2)//方塊水平方向佔兩個字節
{
Pos(i,0);
printf("▇");//上行
Pos(i,26);
printf("▇");//下行
}
for(i=0;i<=25;i+=1)//方塊豎直方向佔1個字節
{
Pos(0,i);//左列
printf("▇");
Pos(60,i);//右列
printf("▇");
}
}
/*******************************************************************主函數************************************************************/
int main()
{
muban();
return 0;
}
下面我們來描繪蛇,這裏要用到結構體指針和鏈表
主要思想是 一個▇是蛇的一段 然後用鏈表將它們鏈接起來
具體的鏈表和結構體指針可以看下面的文章:(只用掌握最基本的用法即可)
結構體https://blog.csdn.net/zhanghow/article/details/53463825
鏈表https://blog.csdn.net/govshell/article/details/63274614
指針https://blog.csdn.net/cyh183269855/article/details/52278941(這篇文章講的很棒,配合結構體食用)
接下來繼續擼代碼
void initSnake()
{
snake *tail;//尾指針
snake *head;//頭指針
tail = (snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用tail指向這個結構體
tail ->x=30;//因爲實心方塊寬度爲2個單位長度,所以必須爲偶數
tail ->y=10;
tail ->next=NULL;
for(i = 0;i<4;i++)
{
head=(snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用head指向這個結構體
head->next=tail;//將
tail=head;//將尾指針傳向下一個頭指針
}
}
比較難理解的是這段尾插法,如果不會的可以去看這篇https://blog.csdn.net/viafcccy/article/details/84502334
這裏下面的代碼
typedef struct Snake//相當於蛇一個節點
{
int x;//橫座標
int y;//縱座標
struct Snake *next;
}snake;
等價於
struct Snake
{
int x;//橫座標
int y;//縱座標
struct Snake *next;
};
struct Snake snake;
關於typedef看這篇https://blog.csdn.net/viafcccy/article/details/84204456
下面完成蛇身的打印
其中第一遍的代碼是這樣
void initSnake()
{
int i;
snake *tail;//尾指針
snake *head;//頭指針
tail = (snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用tail指向這個結構體
tail ->x=30;//因爲實心方塊寬度爲2個單位長度,所以必須爲偶數
tail ->y=10;
tail ->next=NULL;
for(i = 0;i<4;i++)
{
head=(snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用head指向這個結構體
head->next=tail;//鏈接成鏈
head->x=30+2*i;//下一節點的位置
head->y=10;
tail=head;//將尾指針傳向下一個頭指針
}
結果是
我們錯在原來蛇是從右向左打印了8個單位 但是應爲固定輸出press any keyt continues將三個實心方塊擋住了,於是將代碼修改
/*****************************************************************頭文件**********************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
/*****************************************************************函數聲明*********************************************************/
void muban();//打印四周的邊界
void Pos(int x, int y);//設置光標輸出位置
void initSnake();//初始化蛇身,將結構體中的座標讀取打印實心方塊
/*****************************************************************結構體***********************************************************/
typedef struct Snake//將蛇身的位置存入結構體,相當於蛇身上的一個實心方塊
{
int x;//橫座標
int y;//縱座標
struct Snake *next;
}snake;
/*****************************************************************自定義函數*******************************************************/
void Pos(int x, int y)//設置光標位置,從哪裏開始輸出
{
COORD pos;//表示一個字符在控制檯屏幕上的座標,左上角(0,0)
HANDLE hOutput;
pos.X = x;
pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回標準的輸入、輸出或錯誤的設備的句柄,也就是獲得輸入、輸出/錯誤的屏幕緩衝區的句柄
SetConsoleCursorPosition(hOutput, pos);
}
void muban()
{
int i;
for(i=0;i<=60;i+=2)//方塊水平方向佔兩個單位
{
Pos(i,0);
printf("▇");//上行
Pos(i,26);
printf("▇");//下行
}
for(i=0;i<=25;i+=1)//方塊垂直方向佔1個單位
{
Pos(0,i);//左列
printf("▇");
Pos(60,i);//右列
printf("▇");
}
}
void initSnake()
{
int i;
snake *tail;//尾指針
snake *head;//頭指針
tail = (snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用tail指向這個結構體
tail ->x=30;//因爲實心方塊寬度爲2個單位長度,所以必須爲偶數
tail ->y=10;
tail ->next=NULL;
for(i = 1;i<=4;i++)
{
head=(snake*)malloc(sizeof(snake));//以snake結構體的形式開闢一塊新的內存,內存中的數據是新的,用head指向這個結構體
head->next=tail;//鏈接成鏈
head->x=30-2*i;//下一節點的位置
head->y=10;
tail=head;//將尾指針傳向下一個頭指針
}
//遍歷打印出來
while(tail->next!=NULL)
{
Pos(tail->x,tail->y);
printf("▇");
tail = tail->next;
}
}
/*******************************************************************主函數************************************************************/
int main()
{
muban();
initSnake();
return 0;
}
這樣我用四個永遠緊緊相連的方塊構成了蛇的身體,下面我們來生成食物
思路是隨機生成兩個隨機數使x,y分別滿足在邊界內,但是x有一個要求就是要是偶數這個和實心方塊兩個字節有關,我們要避免下圖的情況產生
void creatFood()//創建食物
{
snake *food;//創造一個食物
food=(snake*)malloc(sizeof(snake));
srand((unsigned int)time(NULL));//隨着時間變化,產生不一樣種子,就會得到沒規律的食物
while(food->x%2!=0)
{
food->x=rand()%56+2;
}
food->y=rand()%23+1;
//上面雖然解決了食物不會出現在城牆裏,沒有考慮食物出現在蛇本身裏面
p=head;//用p來遍歷
while(p!=NULL)//解決食物出現在蛇本身
{
if(food->x==p->x&&food->y==p->y)
{
free(food);
creatFood();
}
p=p->next;
}
Pos(food->x,food->y);
food1=food;//food1用來標記的作用
printf("▇");
}
關於產生隨機數可以看這篇文章https://blog.csdn.net/viafcccy/article/details/84311336
於是我們下一步就是要做到我們按鍵蛇會動我們使用頭文件windows.h,具體看這篇https://blog.csdn.net/viafcccy/article/details/84262393
這裏介紹一下遍歷 就是將所有鏈表中的數據訪問一遍 通常是打印出來
蛇的移動主要通過獲取鍵值,然後如下操作
但是我們要將1的位置打印空格 否則實心方塊不會消失 但是這樣打印時會有光標始終跟着蛇尾 因此需要在外面打印一個東西讓光標絕大部分時間在外面 只有一個瞬間在蛇尾我們就不會看到了
void snakeMove()
{
snake *nexthead;
nexthead=(snake*)malloc(sizeof(snake));
if(status=='R')
{
nexthead->x=head->x+2;
nexthead->y=head->y;
if(nexthead->x==food1->x&&nexthead->y==food->y)
{
nexthead->next=head;
head=nexthead;
p=head;//遍歷
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
}
else
{
nexthead->next=head;
head=nexthead;
p=head;//遍歷
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");//會帶來一個光標閃爍
Pos(70,20);
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
if(status=='L')//向左走
{
nexthead->x=head->x-2;
nexthead->y=head->y;
if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}//吃掉了食物得創造
score=score+add;
creatFood();
}
else//沒有食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");
Pos(70,20);//解決辦法
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
}
if(status=='U')//向上走
{
nexthead->x=head->x;
nexthead->y=head->y-1;
if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}//吃掉了食物得創造
score=score+add;
creatFood();
}
else//沒有食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");
Pos(70,20);//解決辦法
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
}
if(status=='D')//向下走
{
nexthead->x=head->x;
nexthead->y=head->y+1;
if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}//吃掉了食物得創造
score=score+add;
creatFood();
}
else//沒有食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");
Pos(70,20);//解決辦法
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
}
Sleep(sleepTime);//蛇移動的速度,裏面是毫秒,越大速度越慢
status=reDirection();//判別下方向先
if(crossWall()==1||eatSelf()==1)
//exit(0);//直接把程序關閉了
endleap=1;
return endleap;
}
int crossWall()//判斷蛇有沒穿透牆
{
if(head->x==0||head->y==0||head->x==60||head->y==25)
leap=1;
return leap;
}
int eatSelf()//判斷是否咬到了自己
{
snake *q;//遍歷的
q=head->next;
while(q!=NULL)
{
if(q->x==head->x&&head->y==q->y)
leap=1;
q=q->next;
}
return leap;
}
下面我們要解決gameover的判斷
貪吃蛇結束就兩種情況
1)撞到牆壁
2)咬到自己
int crossWall()//判斷蛇有沒撞牆
{
if(head->x==0||head->y==0||head->x==60||head->y==25)
leap=1;
return leap;
}
int eatSelf()//判斷是否咬到了自己
{
snake *q;//遍歷的
q=head->next;
while(q!=NULL)
{
if(q->x==head->x&&head->y==q->y)
leap=1;
q=q->next;
}
return leap;
}
最後經過優化的源代碼在這裏
/*********************************************頭文件************************************/
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<time.h>
/*********************************************函數聲明**********************************/
void Pos(int x, int y);//光標位置設定
void muban();//打印模板
void initSnake();//蛇身的初始化
void creatFood();//創建食物
char reDirection();//識別方向
int snakeMove();//蛇移動
int crossWall();//不能穿牆
int eatSelf();//不能吃自己
/**********************************************結構體***********************************/
typedef struct Snake//相當於蛇一個節點
{
int x;//橫座標
int y;//縱座標
struct Snake *next;
}snake;
/***********************************************全局變量********************************/
snake *head;//頭指針
snake *p;//用來遍歷
snake *food1;//用來標記的
char status='L';//初始方向的狀態,解決開始會動的問題
int score=0;//分數
int add=10;//一個食物的分
int leap=0;//用來標誌是否結束,0沒有,1代表蛇死了代表結束了
int endleap=0;//結束標誌 1就是結束
int sleepTime=500;
/***********************************************自定義函數******************************/
void initSnake()//蛇身初始化,給定一個長度,用結構體表示是蛇的骨架,真正要顯示出來是打印▇
{
int i;
snake *tail;//尾指針
tail=(snake*)malloc(sizeof(snake));//第一個節點/頭結點
tail->x=30;//2的倍數,因爲方塊的長是兩個單位
tail->y=10;//1個單位
tail->next=NULL;
for(i=1;i<=4;i++)//尾插法
{
head=(snake*)malloc(sizeof(snake));//申請一個節點
head->next=tail;//連接成鏈
head->x=30-2*i;//下一個節點的位置
head->y=10;
tail=head;
}
//遍歷打印出來
while(tail!=NULL)
{
Pos(tail->x,tail->y);
printf("▇");
tail=tail->next;
}
}
char reDirection()//識別用戶按下的鍵值 保留方向值
{
if(GetAsyncKeyState(VK_F7))//熱鍵
{
if(sleepTime>300)//最多減到300
{
sleepTime-=50;//每次減50
add++;//每次食物加1分
}
}
if(GetAsyncKeyState(VK_F8))
{
if(sleepTime<800)//最多加到800
{
sleepTime+=50;//每次加50
add--;//每次食物減1分
}
}
if(GetAsyncKeyState(VK_UP)&&status!='D')
status='U';
if(GetAsyncKeyState(VK_DOWN)&&status!='U')
status='D';
if(GetAsyncKeyState(VK_LEFT)&&status!='R')
status='L';
if(GetAsyncKeyState(VK_RIGHT)&&status!='L')
status='R';
return status;
}
void Pos(int x, int y)//設置光標位置,從哪裏開始輸出
{
COORD pos;//表示一個字符在控制檯屏幕上的座標,左上角(0,0)
HANDLE hOutput;
pos.X = x;
pos.Y = y;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//返回標準的輸入、輸出或錯誤的設備的句柄,也就是獲得輸入、輸出/錯誤的屏幕緩衝區的句柄
SetConsoleCursorPosition(hOutput, pos);
}
void creatFood()//創建食物
{
snake *food;//創造一個食物
food=(snake*)malloc(sizeof(snake));
srand((unsigned int)time(NULL));//隨着時間變化,產生不一樣種子,就會得到沒規律的食物
while(food->x%2!=0)
{
food->x=rand()%56+2;
}
food->y=rand()%23+1;
//上面雖然解決了食物不會出現在城牆裏,沒有考慮食物出現在蛇本身裏面
p=head;//用p來遍歷
while(p!=NULL)//解決食物出現在蛇本身
{
if(food->x==p->x&&food->y==p->y)
{
free(food);
creatFood();
}
p=p->next;
}
Pos(food->x,food->y);
food1=food;//food1用來標記的作用
printf("▇");
Pos(70,20);//解決有光標閃爍的辦法
printf("您的分數是:%d",score);
}
void muban()
{
int i;
for(i=0;i<=60;i+=2)//方塊水平方向佔兩個單位
{
Pos(i,0);
printf("▇");//上行
Pos(i,26);
printf("▇");//下行
}
for(i=0;i<=25;i+=1)//方塊垂直方向佔1個單位
{
Pos(0,i);//左列
printf("▇");
Pos(60,i);//右列
printf("▇");
}
}
int snakeMove()
{
snake *nexthead;
nexthead=(snake*)malloc(sizeof(snake));
if(status=='R')//向右走
{
nexthead->x=head->x+2;
nexthead->y=head->y;
if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}//吃掉了食物得創造
score=score+add;
creatFood();
}
else//沒有食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");//會帶來一個光標閃爍
Pos(70,20);//解決辦法
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
}
if(status=='L')//向左走
{
nexthead->x=head->x-2;
nexthead->y=head->y;
if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}//吃掉了食物得創造
score=score+add;
creatFood();
}
else//沒有食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");
Pos(70,20);//解決辦法
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
}
if(status=='U')//向上走
{
nexthead->x=head->x;
nexthead->y=head->y-1;
if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}//吃掉了食物得創造
score=score+add;
creatFood();
}
else//沒有食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");
Pos(70,20);//解決辦法
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
}
if(status=='D')//向下走
{
nexthead->x=head->x;
nexthead->y=head->y+1;
if(nexthead->x==food1->x&&nexthead->y==food1->y)//吃掉了食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}//吃掉了食物得創造
score=score+add;
creatFood();
}
else//沒有食物
{
nexthead->next=head;
head=nexthead;
p=head;//p用來從頭遍歷,打印方塊
while(p->next->next!=NULL)
{
Pos(p->x,p->y);
printf("▇");
p=p->next;
}
Pos(p->next->x,p->next->y);
printf(" ");
Pos(70,20);//解決辦法
printf("您的分數是:%d",score);
free(p->next);
p->next=NULL;
}
}
Sleep(sleepTime);//蛇移動的速度,裏面是毫秒,越大速度越慢
status=reDirection();//判別下方向先
if(crossWall()==1||eatSelf()==1)
//exit(0);//直接把程序關閉了
endleap=1;
return endleap;
}
int crossWall()//判斷蛇有沒穿透牆
{
if(head->x==0||head->y==0||head->x==60||head->y==25)
leap=1;
return leap;
}
int eatSelf()//判斷是否咬到了自己
{
snake *q;//遍歷的
q=head->next;
while(q!=NULL)
{
if(q->x==head->x&&head->y==q->y)
leap=1;
q=q->next;
}
return leap;
}
//打印食物的時候會出現光標,解決辦法就是引開它
/*********************************************主函數***********************************/
int main()
{
muban();//打印模板
initSnake();//初始化蛇
creatFood();//創建食物
while(1)//死循環,讓蛇一直動起來,直到蛇死了
{
if(snakeMove()==1)//判斷是否結束
{
Pos(70,23);
printf("蛇死了");
system("pause");//用來暫停
Pos(70,24);//解決press any key to continue 在該地點打印 大家試下
break;
}
}
printf("是否繼續遊戲,y or n:");//y 繼續
if(getch()=='y')//重新遊戲
{
//蛇一開始就死了,因爲全局變量沒有恢復原值,仍然保留上一局的值
status='L';//初始方向的狀態,解決開始會動的問題
score=0;//分數
add=10;//一個食物的分
leap=0;//用來標誌是否結束,0沒有,1代表蛇死了代表結束了
endleap=0;//結束標誌 1就是結束
sleepTime=500;
system("cls");//清理屏幕
main();//自己調用自己 看不一樣的編譯器,vc6.0允許調用自己
}
if(getch()=='n')
{
Pos(70,25);//定一個位置,再打印press
exit(0);//退出程序
}
return 0;
}
//蛇的速度變化,每個食物的分數增加
//是否繼續遊戲
//按鍵的作用