最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言結(jié)構(gòu)數(shù)組實(shí)現(xiàn)貪吃蛇小游戲

 更新時(shí)間:2017年10月09日 09:16:55   作者:Magic激流  
這篇文章主要為大家詳細(xì)介紹了C語言結(jié)構(gòu)數(shù)組實(shí)現(xiàn)貪吃蛇小游戲,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

一、設(shè)計(jì)思路

蛇身本質(zhì)上就是個(gè)結(jié)構(gòu)數(shù)組,數(shù)組里存儲(chǔ)了坐標(biāo)x、y的值,再通過一個(gè)循環(huán)把它打印出來,蛇的移動(dòng)則是不斷地刷新重新打印。所以撞墻、咬到自己只是數(shù)組x、y值的簡(jiǎn)單比較。

二、用上的知識(shí)點(diǎn)

結(jié)構(gòu)數(shù)組Windows API函數(shù)

三、具體實(shí)現(xiàn)

先來實(shí)現(xiàn)靜態(tài)頁面,把地圖、初始蛇身、食物搞定。

這里需要用到Windows API的知識(shí),也就是對(duì)控制臺(tái)上坐標(biāo)的修改

//這段代碼來自參考1
void 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中定義的一種結(jié)構(gòu),表示在控制臺(tái)上的坐標(biāo)

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

而代碼中第七行則是獲得屏幕緩沖區(qū)的句柄,第八行是直接修改光標(biāo)位置的函數(shù)。

1.地圖。

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

//LONG==60
//WIDTH==30
void 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("|"); 
 } 
} 


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

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

原因在這

控制臺(tái)上每個(gè)字符的長(zhǎng)寬比例(像素點(diǎn))是不同的,所以才會(huì)出現(xiàn)上圖這種蛋疼的情況。

解決方法其實(shí)也很簡(jiǎn)單,我們需要引入一些特殊符號(hào),比如"●""■""⊙"等,這些字符的特點(diǎn)是它占據(jù)兩個(gè)普通字符的位置

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

代碼及效果圖如下

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("■"); 
 } 
} 

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

2.初始化一條蛇

因?yàn)樯咭约笆澄?本質(zhì)上都是一個(gè)坐標(biāo),所以我們可以定義一個(gè)新的數(shù)據(jù)類型Node,每一個(gè)Node都是一個(gè)存儲(chǔ)了兩個(gè)變量(x、y)的結(jié)構(gòu)體,再通過Node來定義蛇和食物。

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

好了,我們現(xiàn)在定義了一條叫snake的蛇。為了這條蛇肥胖適中長(zhǎng)寬比例一致,我們用"⊙"代表蛇的每一節(jié)。剛開始我們令蛇出現(xiàn)在地圖中間位置,蛇頭在右,共3個(gè)節(jié)點(diǎn)。所以我們需要求得每個(gè)節(jié)點(diǎn)的坐標(biāo)。

 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)三個(gè)坐標(biāo)處確定了一條蛇。X坐標(biāo)之間減2是因?yàn)?⊙"在X軸占兩個(gè)基本值。

3.隨機(jī)出現(xiàn)食物

先創(chuàng)建一個(gè)變量來存儲(chǔ)食物的坐標(biāo)

Nodefood;

得到它的坐標(biāo)其實(shí)就是用隨機(jī)值對(duì)長(zhǎng)、寬取余,使值在區(qū)間(地圖)范圍內(nèi)。

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的坐標(biāo)值求法為rand()%(LONG-6)+2,因?yàn)槭澄?●"也是兩個(gè)字符的位置,所以它可能的取值為(2,y)(4,y)…(56,y)上下變寬共30個(gè)字符,從0開始,每個(gè)+2,所以最后一個(gè)為(58,y)

Rand()%(LONG)的取值范圍為0~59而x=1,x=2,x=58,x=59是地圖范圍,所以得對(duì)LONG-6(60-6=54)取余,這樣取值范圍就是0~54,再加2,就成了2~56.又因?yàn)樯叩母鞴?jié)坐標(biāo)及移動(dòng)x坐標(biāo)都是+2,所以食物的x坐標(biāo)必須是偶數(shù),這可以用一個(gè)do(…)while()搞定,先取值,再判斷,不行就再取值

Y的坐標(biāo)稍微簡(jiǎn)單些,只要保證坐標(biāo)值在1~28就行。

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

搞完上面的,我們就有了一個(gè)基本的(靜態(tài))效果了,現(xiàn)在我們要讓它動(dòng)起來

注:第86行是設(shè)置控制臺(tái)窗口長(zhǎng)、寬的系統(tǒng)函數(shù)。

4.讓蛇動(dòng)起來

蛇每次移動(dòng)背后發(fā)生的事就是數(shù)組里的值改變,再在每個(gè)坐標(biāo)位置打印蛇身。

為了讓蛇一直動(dòng),我們就需要一個(gè)循環(huán)   

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

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

這里介紹一個(gè)函數(shù)

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

這是一個(gè)非阻塞函數(shù),有鍵按下時(shí)返回非0,但此時(shí)按鍵碼仍然在鍵盤緩沖隊(duì)列中。所以在確定鍵盤有響應(yīng)之后,再用一個(gè)char變量將輸入從緩沖區(qū)中調(diào)出來。

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

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

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; 


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

在方向確定了之后,再用一個(gè)switch語句進(jìn)行坐標(biāo)判斷

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': 
 //。。。 
} 

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

再從倒數(shù)第二節(jié)開始,把前一節(jié)的坐標(biāo)值賦給后一節(jié),直到第二節(jié)得到了之前蛇頭坐標(biāo)。在食物被吃了之后,再調(diào)用隨機(jī)出現(xiàn)食物函數(shù)。

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

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

這樣移動(dòng)部分就實(shí)現(xiàn)了,現(xiàn)在只需處理一些小模塊就行。

5.移動(dòng)后的處理。

這一部分相對(duì)簡(jiǎn)單,即對(duì)判斷蛇是否撞墻、是否咬到自身,再對(duì)這種情況做處理,我們用兩個(gè)函數(shù)搞定它

int ThroughWall() 
{ 
 if(snake[0].x==0 || snake[0].x==58 || 
 snake[0].y==0 || snake[0].y==29) 
 { 
  Pos(25,15); 
  printf("撞墻 游戲結(jié)束"); 
  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("咬到自己 游戲結(jié)束"); 
  return 1; 
 } 
} 

當(dāng)返回值為1時(shí),游戲也就GG了。

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

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

Sleep(speed);

源代碼在這:結(jié)構(gòu)數(shù)組實(shí)現(xiàn)_貪吃蛇源碼

四、總結(jié)與反思。

首先從蛇的結(jié)構(gòu)上來說,結(jié)構(gòu)數(shù)組的實(shí)現(xiàn)直接無視了"效率"這個(gè)詞,數(shù)組占用大量空間且有容量限制,并不是一種好辦法。

其次是BUG的問題,在ThroughWall()函數(shù)中,在對(duì)蛇頭坐標(biāo)進(jìn)行判斷時(shí)在蛇頭移動(dòng)到(x,1)位置時(shí),游戲直接結(jié)束,且沒有任何提示。

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

還有就是while()循環(huán)里代碼行太多,特別是switch-case 里各項(xiàng),蛇身的移動(dòng)(結(jié)構(gòu)數(shù)組個(gè)元素坐標(biāo)值的變換)應(yīng)該抽象成一個(gè)move()函數(shù)。

五、其他。

這是對(duì)我第一份代碼(snakeV1.0)的重構(gòu),程序結(jié)構(gòu)上有較大變化

重構(gòu)期間研究了鏈表實(shí)現(xiàn)_貪吃蛇源碼,在結(jié)構(gòu)上采用了里面的部分思想。

個(gè)人空空如也的github:MagicXyxxx的github

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解如何用C++寫一個(gè)日期計(jì)算器

    詳解如何用C++寫一個(gè)日期計(jì)算器

    寫一個(gè)日期計(jì)算器是一個(gè)很有現(xiàn)實(shí)意義的,比如,我想弄一個(gè)倒計(jì)時(shí)或者你女朋友問你和她認(rèn)識(shí)多少天了,這就需要讓兩個(gè)日期相減,又或者和別人約定幾年以后怎么怎么樣,這就得知道那一天的日期,接下來小編借給大家介紹一下如何用C++寫一個(gè)日期計(jì)算器,感興趣的朋友可以動(dòng)手嘗試
    2024-04-04
  • OpenCV實(shí)現(xiàn)可分離濾波

    OpenCV實(shí)現(xiàn)可分離濾波

    這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)可分離濾波,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C語言控制臺(tái)應(yīng)用程序GDI繪制正弦曲線

    C語言控制臺(tái)應(yīng)用程序GDI繪制正弦曲線

    這篇文章主要為大家詳細(xì)介紹了C語言控制臺(tái)應(yīng)用程序GDI繪制正弦曲線,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-06-06
  • C語言數(shù)據(jù)存儲(chǔ)歸類介紹

    C語言數(shù)據(jù)存儲(chǔ)歸類介紹

    使用編程語言進(jìn)行編程時(shí),需要用到各種變量來存儲(chǔ)各種信息。變量保留的是它所存儲(chǔ)的值的內(nèi)存位置。這意味著,當(dāng)您創(chuàng)建一個(gè)變量時(shí),就會(huì)在內(nèi)存中保留一些空間。您可能需要存儲(chǔ)各種數(shù)據(jù)類型的信息,操作系統(tǒng)會(huì)根據(jù)變量的數(shù)據(jù)類型,來分配內(nèi)存和決定在保留內(nèi)存中存儲(chǔ)什么
    2022-08-08
  • QT使用QChart繪制面積圖

    QT使用QChart繪制面積圖

    在Qt中使用QChart類可以快速繪制一個(gè)圖表出來,比如折線圖、餅圖、柱狀圖等,本文就來為大家介紹一下如何利用QChart繪制簡(jiǎn)單的面積圖吧
    2024-11-11
  • C語言實(shí)現(xiàn)高精度減法

    C語言實(shí)現(xiàn)高精度減法

    高精度的本質(zhì)是將數(shù)字以字符串的形式讀入,然后將每一位分別存放入int數(shù)組中,通過模擬每一位的運(yùn)算過程,來實(shí)現(xiàn)最終的運(yùn)算效果,下面我們就來看看C語言如何實(shí)現(xiàn)高精度減法吧
    2023-11-11
  • C語言數(shù)據(jù)結(jié)構(gòu)與算法時(shí)間空間復(fù)雜度基礎(chǔ)實(shí)踐

    C語言數(shù)據(jù)結(jié)構(gòu)與算法時(shí)間空間復(fù)雜度基礎(chǔ)實(shí)踐

    這篇文章主要為大家介紹了C語言數(shù)據(jù)結(jié)構(gòu)與算法中時(shí)間空間復(fù)雜度的基礎(chǔ)實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-02-02
  • VScode中添加頭文件和源文件(C/C++)的方法

    VScode中添加頭文件和源文件(C/C++)的方法

    使用VSCode編譯C/C++時(shí),會(huì)存在找不到頭文件的情況,下面這篇文章主要給大家介紹了關(guān)于VScode中添加頭文件和源文件(C/C++)的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • C語言實(shí)現(xiàn)簡(jiǎn)單彈跳小球

    C語言實(shí)現(xiàn)簡(jiǎn)單彈跳小球

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)簡(jiǎn)單彈跳小球,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • C++ 智能指針的魅力你都了解嗎

    C++ 智能指針的魅力你都了解嗎

    智能指針使用和普通指針類似。解引用一個(gè)智能指針返回它指向的對(duì)象。如果在一個(gè)條件判斷中使用智能指針,效果就是檢測(cè)它是否為空,本文給大家介紹C++ 智能指針的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2021-06-06

最新評(píng)論

太仓市| 孟村| 汽车| 年辖:市辖区| 佛学| 南江县| 平武县| 南汇区| 开江县| 深圳市| 荃湾区| 陈巴尔虎旗| 沿河| 通化市| 酒泉市| 安远县| 微博| 吕梁市| 新干县| 嘉鱼县| 辽中县| 宁国市| 舒城县| 仙游县| 福建省| 桐城市| 绩溪县| 井陉县| 凤庆县| 宝清县| 新巴尔虎左旗| 天长市| 辽阳县| 大化| 光泽县| 涿州市| 双流县| 黑山县| 万州区| 东辽县| 马山县|