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;
}