亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C語言結構數組實現貪吃蛇小游戲

2020-05-23 13:45:32
字體:
來源:轉載
供稿:網友

一、設計思路

蛇身本質上就是個結構數組,數組里存儲了坐標x、y的值,再通過一個循環把它打印出來,蛇的移動則是不斷地刷新重新打印。所以撞墻、咬到自己只是數組x、y值的簡單比較。

二、用上的知識點

結構數組Windows API函數

三、具體實現

先來實現靜態頁面,把地圖、初始蛇身、食物搞定。

這里需要用到Windows API的知識,也就是對控制臺上坐標的修改

//這段代碼來自參考1void Pos(int x, int y) {  COORD pos;  HANDLE hOutput;  pos.X = x;  pos.Y = y;  hOutput = GetStdHandle(STD_OUTPUT_HANDLE);  SetConsoleCursorPosition(hOutput, pos); } 

COORD是Windows API中定義的一種結構,表示在控制臺上的坐標

typedef struct _COORD { SHORT X; // horizontal coordinate SHORT Y; // vertical coordinate } COORD;

而代碼中第七行則是獲得屏幕緩沖區的句柄,第八行是直接修改光標位置的函數。

1.地圖。

有了Pos()函數,打印一個框就不是問題了。假如我們用"-"作為上下邊框,把"|"作為左右邊框,這看起來沒什么不妥,但其實我們已經掉進了坑里,直接上代碼及實際效果圖吧。

//LONG==60//WIDTH==30void CreateMap() {  int i;  for(i=0;i<LONG;i++)//上下兩行  {  Pos(i,1);  printf("-");  Pos(i,WIDTH-1);  printf("-");  }  for(i=2;i<WIDTH-1;i++)//左右兩列  {  Pos(0,i);  printf("|");  Pos(LONG-1,i);  printf("|");  } } 

C語言,貪吃蛇

發現了問題嗎?這是一條正常的蛇。。。那為什么看起來不正常呢?我們把邊框都換成"#"來看看…

C語言,貪吃蛇

這就清楚多了啊,要知道我們上下邊框可是各有60個"#"的,長60寬30的長方形輸出之后竟然成了個正方形。

原因在這

C語言,貪吃蛇

控制臺上每個字符的長寬比例(像素點)是不同的,所以才會出現上圖這種蛋疼的情況。

解決方法其實也很簡單,我們需要引入一些特殊符號,比如"●""■""⊙"等,這些字符的特點是它占據兩個普通字符的位置

所以上下邊框就有60/2=30個符號,要讓它仍然是個正方形的話,左右也可以設為30(28+2)個符號.

代碼及效果圖如下

void CreateMap() {  int i;  for(i=0;i<LONG;i+=2)  {  Pos(i,0);  printf("■");  Pos(i,WIDTH-1);  printf("■");  }  for(i=1;i<WIDTH-1;i++)  {  Pos(0,i);  printf("■");  Pos(LONG-2,i);  printf("■");  } } 

C語言,貪吃蛇

這樣看就舒服多了,不過也讓復雜度提升了一些,上邊框每個符號的坐標分別是(0,0)(2,0)(4,0)…(2*n-2,0)這個在蛇的移動及食物的模塊再提。

2.初始化一條蛇

因為蛇以及食物 本質上都是一個坐標,所以我們可以定義一個新的數據類型Node,每一個Node都是一個存儲了兩個變量(x、y)的結構體,再通過Node來定義蛇和食物。

typedef struct node{  int x;  int y; }Node;   Node snake[60];

好了,我們現在定義了一條叫snake的蛇。為了這條蛇肥胖適中長寬比例一致,我們用"⊙"代表蛇的每一節。剛開始我們令蛇出現在地圖中間位置,蛇頭在右,共3個節點。所以我們需要求得每個節點的坐標。

 void InitializeSnake() {  int i;  for(i=0;i<3;i++)  {  snake[i].x = (LONG/2-i*2);//(30,15)(28,15)(26,15)  snake[i].y = WIDTH/2;  Pos(snake[i].x,snake[i].y);  printf("⊙");  } } 

這樣我們就在(30,15)(28,15)(26,15)三個坐標處確定了一條蛇。X坐標之間減2是因為"⊙"在X軸占兩個基本值。

C語言,貪吃蛇

3.隨機出現食物

先創建一個變量來存儲食物的坐標

Nodefood;

得到它的坐標其實就是用隨機值對長、寬取余,使值在區間(地圖)范圍內。

void CreateFood() {  int i;  srand((unsigned int)time(0));  while(1)  {  do{   food.x = rand()%(LONG-6)+2;  }while(food.x%2!=0);  food.y = rand()%(WIDTH-2)+1;  for(i=0;i<3+length;i++)   if(food.x==snake[i].x && food.y==snake[i].y)   {   i=-1;   break;   }  if(i>=0)  {   Pos(food.x,food.y);   printf("●");   break;  }  }  //AfterEatFood(); } 

X的坐標值求法為rand()%(LONG-6)+2,因為食物"●"也是兩個字符的位置,所以它可能的取值為(2,y)(4,y)…(56,y)上下變寬共30個字符,從0開始,每個+2,所以最后一個為(58,y)

Rand()%(LONG)的取值范圍為0~59而x=1,x=2,x=58,x=59是地圖范圍,所以得對LONG-6(60-6=54)取余,這樣取值范圍就是0~54,再加2,就成了2~56.又因為蛇的各節坐標及移動x坐標都是+2,所以食物的x坐標必須是偶數,這可以用一個do(…)while()搞定,先取值,再判斷,不行就再取值

Y的坐標稍微簡單些,只要保證坐標值在1~28就行。

另外求出了坐標之后要判斷食物是否與蛇身重合,重合的話重新賦值。

搞完上面的,我們就有了一個基本的(靜態)效果了,現在我們要讓它動起來

C語言,貪吃蛇

注:第86行是設置控制臺窗口長、寬的系統函數。

4.讓蛇動起來

蛇每次移動背后發生的事就是數組里的值改變,再在每個坐標位置打印蛇身。

為了讓蛇一直動,我們就需要一個循環   

while(1) {  //獲得輸入,改變坐標  //在每個坐標處輸出 } 

首先,我們需要確定方向,而這需要兩個變量,一個是輸入值(可能是任意值),另一個則是確定方向的變量。

這里介紹一個函數

int kbhit(void); // 檢查當前是否有鍵盤輸入,若有則返回一個非0值,否則返回0

這是一個非阻塞函數,有鍵按下時返回非0,但此時按鍵碼仍然在鍵盤緩沖隊列中。所以在確定鍵盤有響應之后,再用一個char變量將輸入從緩沖區中調出來。

if(kbhit())  ch = getch(); 

再對ch做判斷,如果是符合情況(不能往后走等)的輸入,則開始執行switch改變坐標

if(ch=='w'&&direction!='s')  direction = ch; else if(ch=='s'&&direction!='w')  direction = ch; else if(ch=='a'&&direction!='d')  direction = ch; else if(ch=='d'&&direction!='a')  direction = ch; else if(ch==' ')  continue; 

這里設置空格是暫停,而為了讓蛇一開始就移動,我們把direction設置為d(往右)。 

在方向確定了之后,再用一個switch語句進行坐標判斷

switch(direction) {  case 'w':  if(snake[0].x==food.x && snake[0].y-1==food.y)  {   length++;   score+=10;   snake[2+length].x = snake[2+length-1].x;   snake[2+length].y = snake[2+length-1].y;   for(i=length+3-2;i>0;i--)   {   snake[i].x = snake[i-1].x;   snake[i].y = snake[i-1].y;   }   CreateFood();  }  else  {   Pos(snake[2+length].x,snake[2+length].y);   printf(" ");   for(i=length+3-1;i>0;i--)   {   snake[i].x = snake[i-1].x;   snake[i].y = snake[i-1].y;   }  }  snake[0].y -=1;  break;  case 's':  //。。。  case 'a':  //。。。  case 'd':  //。。。 } 

對蛇頭的下一步做判斷,如果吃到了食物的話,則先對分數等全局變量進行處理,再把snake[2+length-1](吃到食物后的倒數第二個變量)的值賦值給snake[2+length](此時新加的尾節)。

再從倒數第二節開始,把前一節的坐標值賦給后一節,直到第二節得到了之前蛇頭坐標。在食物被吃了之后,再調用隨機出現食物函數。

如果沒有吃到食物的話,先到之前最后一節的坐標處,輸入空格,算是銷毀它再對各節重新賦值。在蛇頭后每節都賦值完成之后,根據輸入值單獨對蛇頭賦值,如輸入是'w',則往上,所以蛇頭縱坐標減一。

對其余輸入也是同樣的道理,在snake數組各值都更新之后,再用一個函數把它打印出來。

這樣移動部分就實現了,現在只需處理一些小模塊就行。

5.移動后的處理。

這一部分相對簡單,即對判斷蛇是否撞墻、是否咬到自身,再對這種情況做處理,我們用兩個函數搞定它

int ThroughWall() {  if(snake[0].x==0 || snake[0].x==58 ||  snake[0].y==0 || snake[0].y==29)  {   Pos(25,15);   printf("撞墻 游戲結束");   return 1;  }  Pos(0,WIDTH);  printf(" "); } 
int BiteItself() {  int i;  for(i=3;i<=2+length;i++)  if((snake[0].x==snake[i].x) && (snake[0].y==snake[i].y))  {   Pos(25,15);   printf("咬到自己 游戲結束");   return 1;  } } 

當返回值為1時,游戲也就GG了。

if(ThroughWall()==1) {  Pos(25,WIDTH);  system("pause");  exit(0); } if(BiteItself()==1) {  Pos(25,WIDTH);  system("pause");  exit(0); }

最后再加一行Sleep()函數,對刷新時間(每次重新打印的時間間隔)做處理。speed是一個變量,在每次吃到食物后遞減。

Sleep(speed);

源代碼在這:結構數組實現_貪吃蛇源碼

四、總結與反思。

首先從蛇的結構上來說,結構數組的實現直接無視了"效率"這個詞,數組占用大量空間且有容量限制,并不是一種好辦法。

其次是BUG的問題,在ThroughWall()函數中,在對蛇頭坐標進行判斷時在蛇頭移動到(x,1)位置時,游戲直接結束,且沒有任何提示。

但詭異的是,在判斷后加入 Pos(0,WIDTH);printf(" "); 這兩行不相干的語句后,這個問題解決了,而我對這兩行語句的原有目的則只是想把閃爍不停光標放到地圖外面去。

還有就是while()循環里代碼行太多,特別是switch-case 里各項,蛇身的移動(結構數組個元素坐標值的變換)應該抽象成一個move()函數。

五、其他。

這是對我第一份代碼(snakeV1.0)的重構,程序結構上有較大變化

重構期間研究了鏈表實現_貪吃蛇源碼,在結構上采用了里面的部分思想。

個人空空如也的github:MagicXyxxx的github

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩欧美黄色动漫| 国产精品欧美亚洲777777| 久久香蕉频线观| 在线视频亚洲欧美| 久久久97精品| 国产午夜精品久久久| 亚洲经典中文字幕| 日韩av不卡在线| 91视频免费在线| 日韩专区在线播放| 亚洲综合社区网| 日本欧美一级片| 欧美精品一区二区三区国产精品| 欧美成人午夜影院| 日本精品视频在线| 国产免费观看久久黄| 热久久免费视频精品| 欧美黄色片在线观看| 26uuu另类亚洲欧美日本老年| 日韩一二三在线视频播| 国产中文字幕亚洲| 57pao成人国产永久免费| 97在线看免费观看视频在线观看| 亚洲精品美女免费| 久久中文字幕在线视频| 欧美最近摘花xxxx摘花| 美乳少妇欧美精品| 成人国产精品免费视频| 国产69久久精品成人| 久久九九亚洲综合| 国产99在线|中文| 国产一区二区视频在线观看| 91精品啪aⅴ在线观看国产| 久久久久久久久久婷婷| 日韩精品免费视频| 国产小视频国产精品| 国产精自产拍久久久久久蜜| 国产精品色午夜在线观看| 欧美日韩在线影院| 91精品视频在线播放| 日韩最新av在线| 日韩精品中文字幕有码专区| 久久久久久久爱| 91精品视频在线看| 精品视频中文字幕| 亚洲美女视频网站| 92版电视剧仙鹤神针在线观看| 欧美巨猛xxxx猛交黑人97人| 日韩免费不卡av| 亚洲欧洲在线看| 深夜福利亚洲导航| 国产精品精品一区二区三区午夜版| 国产69精品久久久| 一本一本久久a久久精品牛牛影视| 国产精品视频免费在线观看| 亚洲最大av网| 久久精品中文字幕免费mv| 成人a在线视频| 中文字幕欧美在线| 日本aⅴ大伊香蕉精品视频| 欧美孕妇毛茸茸xxxx| 国产精品视频yy9099| 国产成人精品免高潮费视频| 久久精品国产一区二区电影| 精品国产91久久久久久老师| 成人看片人aa| 亚洲www永久成人夜色| 富二代精品短视频| 国产成人久久久| 亚洲女人天堂成人av在线| 欧美高清视频在线| 国产一区二区在线播放| 91九色视频导航| 国产精品一区二区三区免费视频| 一区二区三区视频免费在线观看| 亚洲天堂第一页| 亚洲福利在线看| 欧美中文字幕精品| 久久99精品久久久久久琪琪| 国产精品一区av| 国产日韩综合一区二区性色av| 国产精品爽爽爽爽爽爽在线观看| 成人97在线观看视频| 自拍偷拍免费精品| 日韩免费电影在线观看| 国产成人精品久久亚洲高清不卡| 欧美精品videossex性护士| 性欧美在线看片a免费观看| 国产精品吴梦梦| 亚洲成人激情视频| 91精品在线国产| 久久精品国产亚洲| 国产精品海角社区在线观看| 久久人人97超碰精品888| 欧美极品少妇xxxxⅹ免费视频| 欧美精品一区二区三区国产精品| 国产精品国产三级国产aⅴ9色| 欧美成人国产va精品日本一级| 欧美制服第一页| 欧美性猛交xxxx偷拍洗澡| 欧美小视频在线观看| 亚洲第一国产精品| 国产精品夫妻激情| 久久不射电影网| 亚洲成人激情小说| 欧美www在线| 黑人巨大精品欧美一区免费视频| 欧美午夜无遮挡| 欧美激情中文字幕乱码免费| 日韩免费观看视频| 欧美激情国内偷拍| 日韩**中文字幕毛片| 亚洲一区二区中文字幕| 92看片淫黄大片欧美看国产片| 亚洲成人精品在线| 亚洲欧美制服另类日韩| 国产欧美 在线欧美| 国产精品久久久久福利| 最近2019好看的中文字幕免费| 久久久久久久一区二区三区| 亚洲久久久久久久久久| 日韩欧美黄色动漫| 国产日韩欧美中文| 国产精品69久久久久| 国产精国产精品| 国产精品一二三视频| 日韩国产欧美精品一区二区三区| 亚洲欧美日韩中文在线制服| 欧美激情乱人伦| 亚洲视频综合网| 欧美激情伊人电影| 精品国产成人av| 欧美成人激情图片网| 日韩一区二区三区在线播放| 97精品久久久中文字幕免费| 精品人伦一区二区三区蜜桃免费| 亚洲色图第三页| 久久久久久久久久av| 国产成人avxxxxx在线看| 欧美国产精品人人做人人爱| 日韩精品中文字| 久久久av一区| 欧美成人免费视频| 亚洲欧美日韩中文在线| 奇门遁甲1982国语版免费观看高清| 精品国产精品三级精品av网址| 91超碰中文字幕久久精品| 国产精品爽爽爽| 国产精品一香蕉国产线看观看| 日韩欧美亚洲一二三区| 日韩电影中文字幕在线| 欧美精品一本久久男人的天堂| 欧美日韩国产成人高清视频| 亚洲精选一区二区| 两个人的视频www国产精品| 国产欧美韩国高清| www.xxxx欧美| 在线观看中文字幕亚洲| 91在线网站视频| 欧美乱妇40p| 在线观看精品国产视频| 久久精品亚洲国产| 欧美精品videosex极品1| 91欧美日韩一区|