摘? 要:Linux系統(tǒng)不僅免費、開源,而且還安全穩(wěn)定,通過學(xué)習(xí)簡單的指令讓程序員更快地入門,讓大家早一點接觸Linux系統(tǒng),并用C語言進(jìn)行簡單的貪吃蛇游戲設(shè)計。貪吃蛇是許多人小時候的經(jīng)典回憶,在對貪吃蛇游戲編寫時會涉及地圖,食物,蛇的更新,文章是基于Linux系統(tǒng)通過使用C語言進(jìn)行蛇游戲的設(shè)計與編寫,經(jīng)過每一個功能的設(shè)計與測試一步一步地最終實現(xiàn)游戲呈現(xiàn)的效果。
關(guān)鍵詞:Linux系統(tǒng);C語言;ncurses庫;結(jié)構(gòu)體;線程
中圖分類號:TP311? ? ? ? ? ?文獻(xiàn)標(biāo)識碼:A文章編號:2096-4706(2022)01-0093-04
Abstract: The Linux system is not only free and open source, but also safe and stable. By learning simple instructions, programmers can get started faster, so that everyone can get in touch with the Linux system earlier, and use C language to design a simple snake game. Snake game is a classic memory of our childhood. When writing the snake game, it will involve the update of maps, food, and snakes. This paper designs and writes the snake game based on the Linux system by using C language. After the design and test of each function step by step, it finally achieves the effect of the game presentation.
Keywords: Linux system; C language; ncurses library; structure; thread
0? 引? 言
現(xiàn)在我們經(jīng)常會安裝這些游戲:王者榮耀、和平精英、英雄聯(lián)盟等等,這些游戲畫面高清,代入感強,受廣大人們的喜愛,但貪吃蛇仍是我們大多數(shù)人心中的經(jīng)典游戲。貪吃蛇游戲出現(xiàn)之后發(fā)展迅速,從最初的游戲機到普通手機再到智能機再到電腦;從最初的單機個人游戲,到現(xiàn)在的聯(lián)網(wǎng)多人同時游戲。貪吃蛇可以通過設(shè)定速度攝取食物讓自己變得更長,通過簡單地操作讓人們獲得滿足感和勝利感。該游戲的優(yōu)勢是簡單易操作,實現(xiàn)容易。本文基于Linux ncurses庫通過c語言設(shè)計和編寫貪吃蛇游戲,以便程序員以后更好地接觸Linux系統(tǒng)并在其中進(jìn)行開發(fā)。
1? 編程語言簡介
1.1? C語言簡介
C語言是一門面向大眾的、過程的、抽象化的通用程序進(jìn)行設(shè)計教學(xué)語言,廣泛應(yīng)用于底層技術(shù)開發(fā)。C語言可以用一種簡單的方式編譯,處理低級內(nèi)存。C語言是一種高效的編程語言,它只生成少量的機器語言,并且可以在沒有任何運行環(huán)境支持的情況下運行。盡管C語言發(fā)展提供了許多低級處理的功能,但仍然保持著跨平臺的特性,以一個國家標(biāo)準(zhǔn)規(guī)格寫出的C語言應(yīng)用程序可在包括企業(yè)類似嵌入式處理器技術(shù)以及中國超級計算機等作業(yè)管理平臺的許多學(xué)生計算機網(wǎng)絡(luò)平臺上進(jìn)行編譯,C語言則與面向?qū)ο蟮恼Z言不同,在進(jìn)行C語言程序設(shè)計時,如果開發(fā)者沒有一開始就確定好合適的數(shù)據(jù)類型、結(jié)構(gòu)體、變量等,那么在調(diào)試時,查到程序錯誤或者有漏洞,修改起來就會相當(dāng)困難。
1.2? 設(shè)計環(huán)境
本程序在vmware虛擬機配置ubuntu鏡像操作系統(tǒng)實現(xiàn)Linux下編程Vim編寫C文件、GCC編譯文件,生成文件后運行。用戶可以自由編輯、編譯、運行和調(diào)試程序,該開發(fā)工具優(yōu)勢可以讓程序員熟悉LINUX操作指令,并在其中練習(xí)C語言,為以后的開發(fā)做下鋪墊。
2? 程序設(shè)計
2.1? 程序簡介
貪吃蛇這款游戲玩家用戶從成人到青年,深受大家的喜愛。蛇初始化在一個自己設(shè)計的封閉圖形里 ,食物會隨機出現(xiàn)在墻壁內(nèi)。玩家可以通過按鍵盤上的上下左右(蛇在移動過程中不可以直接更改到相反的方向)來控制蛇頭移動的方向,如果蛇的頭部(蛇的頭部為鏈表的尾部)碰到食物,食物消失,蛇的身體就會在尾部長出一節(jié),然后食物會隨機在地圖內(nèi)出現(xiàn),重復(fù)以上的過程。如果蛇在運動中碰到邊界或者碰到自己的身體,則游戲結(jié)束。
2.2? 設(shè)計思路
在設(shè)計時,需要用一個圖案來代表蛇的一節(jié)身體,作者用“【】”來代表,因為這個圖形移動感強、相似度較高,能給玩家?guī)砀玫捏w驗。每當(dāng)蛇吃掉一個食物時,身體就會自動在尾部增長一節(jié)。當(dāng)蛇移動時必須從蛇頭(在這里作者用鏈表的尾部充當(dāng)蛇頭,鏈表的頭部充當(dāng)蛇尾)開始。如果玩家不按下任何方向鍵,蛇就會按照游戲初始方向自動前移。當(dāng)玩家按下相應(yīng)的方向鍵時,蛇頭就會朝著方向按鍵所指的方向移動,通過不斷刷新地圖來顯示蛇的移動。而食物我們用“#”來代替,在這里食物的位置我們用rand函數(shù)通過計算隨機地出現(xiàn)在地圖當(dāng)中,確保食物出現(xiàn)的隨機性。
貪吃蛇的游戲過程我們可以分為5個部分。
第一部分:游戲界面初始化地圖,食物,貪吃蛇出現(xiàn)。
第二部分:蛇向游戲初始化設(shè)定的方向自動前進(jìn)。
第三部分:根據(jù)玩家的鍵盤指令,蛇向相應(yīng)的方向前進(jìn)。
第四部分:判斷蛇是否吃到食物,若吃到食物,蛇在尾部增長一節(jié),食物重新隨機地出現(xiàn)在地圖的內(nèi)部,若蛇沒有吃到食物則按原長度繼續(xù)前進(jìn)。
第五部分:當(dāng)蛇撞到地圖邊界或者撞到自身的身體,蛇死亡游戲結(jié)束進(jìn)行第一部分重新開始游戲繼續(xù)執(zhí)行二三四部分,若蛇沒有死亡則繼續(xù)執(zhí)行三四部分直到游戲結(jié)束。
2.3? 設(shè)計流程圖
游戲的設(shè)計流程圖如圖1所示。
程序按照設(shè)定初始游戲界面,地圖食物蛇出現(xiàn),程序不斷捕獲玩家輸入,若為無效輸入,蛇按照原方向正常運行;若為有效輸入,蛇就會朝著按鍵方向正常移動。程序會不斷檢測蛇是否吃到食物,若吃到食物,食物消失,蛇增長;若沒吃到食物,程序繼續(xù)判斷。若蛇碰到自身或者邊界則游戲結(jié)束重新初始化;若沒有繼續(xù)循環(huán)以上程序,直到蛇碰到墻壁或自己游戲結(jié)束。
3? 算法分析
在這里我們需要用到ncurses庫幫助我們完成從鍵盤獲取輸入的任務(wù),因為C語言函數(shù)庫里的函數(shù)從鍵盤獲取的響應(yīng)速度慢,不具有實時性。而我們用ncurses庫則能彌補這一點,當(dāng)我們按下方向鍵時,程序迅速反應(yīng)來達(dá)到我們控制蛇移動方向的要求。主函數(shù)main()首先調(diào)用函數(shù)initcurses()來實現(xiàn)ncurses庫和獲取鍵盤輸入函數(shù)的初始化,然后調(diào)用函數(shù)InitSnake()來初始化貪吃蛇(程序設(shè)定最初的蛇為3個【】),在此函數(shù)中調(diào)用InitFood()隨機出現(xiàn)食物,調(diào)用Addnode()來增加節(jié)點。調(diào)用函數(shù)Initmap()出現(xiàn)地圖,此處我們需要創(chuàng)建兩個線程并在這兩個線程中一個執(zhí)行Refresh_()函數(shù)實現(xiàn)不斷刷新地圖和蛇的移動功能的函數(shù)另一個線程執(zhí)行ChangeDir()函數(shù)不斷獲取用戶輸入的按鍵,(在這里我簡單地闡述一下,線程是一種可以多任務(wù)共同執(zhí)行的操作方式,若在這里我們創(chuàng)建一個新的進(jìn)程去執(zhí)行這個功能,需要很多的數(shù)據(jù)去維護(hù)它,實在浪費空間,若改為線程則所需的時間和空間遠(yuǎn)小于進(jìn)程并且他們二者在切換時,線程的優(yōu)勢也體現(xiàn)的較明顯,增強了程序的健壯性和代碼的可移植性而且操作也較為簡單更利于程序員理解和應(yīng)用)最后執(zhí)行while()函數(shù)保持整個程序地進(jìn)行。
接下來對各函數(shù)進(jìn)行詳細(xì)分析:
(1)主函數(shù)main()是程序的主要流程,用來調(diào)用各個函數(shù)。各函數(shù)最終都在主函數(shù)中被直接或間接地調(diào)用執(zhí)行來實現(xiàn)游戲的效果。
(2)初始化函數(shù)initSnake(),在此函數(shù)中我們首先設(shè)定了一個游戲的初始方向RIGHT,并在其中調(diào)用Initfood()函數(shù)來出現(xiàn)食物,并在函數(shù)外定義結(jié)構(gòu)體通過結(jié)構(gòu)體指針給蛇最初出現(xiàn)的身體賦值,在調(diào)用倆次Addnode()函數(shù)增加蛇的節(jié)點使蛇初始化長度為3,因為每次蛇死亡都會調(diào)用這個函數(shù),為了減少對內(nèi)存空間的浪費,我們在此函數(shù)的最開始把原來制造的內(nèi)存釋放掉,增強程序的健壯性。此段釋放空間的代碼為:
Structtan*p;
While(head!=NULL){
P=head;
head=head->next;
free(p);
}
(3)繪制地圖函數(shù)InitMap(),地圖實際上是一個封閉的正方形用一個嵌套的雙重for循環(huán)打印出來的上下邊界我們用-代表,左右邊界用|來代表中間用空格。這樣便形成一個長20寬20的正方形。并在此函數(shù)中調(diào)用HaveSnake()函數(shù)和Havefood()函數(shù),當(dāng)這兩個函數(shù)判斷返回值為1時便打印【】和#來確定蛇和食物在地圖中出現(xiàn)的位置,這兩個函數(shù)用我們最初給的起始值與地圖的相應(yīng)位置進(jìn)行比較,相同返回1,不同返回0,便能達(dá)到我們想要的效果,最后在此函數(shù)開頭調(diào)用move(0,0)函數(shù)調(diào)整光標(biāo),如果不調(diào)用新的地圖將會在光標(biāo)默認(rèn)的位置打印,達(dá)到不了游戲的效果。
(4)Initcurses()獲取鍵盤輸入和初始化Ncurses庫,此處比較簡單具體實現(xiàn)代碼如下:
Void Initcurses(){
Initscr();
keypad(stdscr,1);//從小鍵盤上獲取方向
noecho();
}
(5)增加節(jié)點函數(shù)AddNode(),此函數(shù)我們在InitSnake()中調(diào)用,在這里詳細(xì)講解一下,我們用malloc函數(shù)給新增的節(jié)點開辟空間,因為我們的蛇移動的方向涉及上下左右,這就需要我們在增加節(jié)點的時候根據(jù)方向的不同來確定,此處我們用switch()函數(shù)進(jìn)行方向的選擇。舉例 當(dāng)我們按下up鍵時便讓這個新產(chǎn)生的節(jié)點行坐標(biāo)比原來的行坐標(biāo)減一,列坐標(biāo)不變。再讓這個頭指針指向這個新的節(jié)點,把這個新節(jié)點變成尾部,這樣我們便完成了一次移動,其他方向也是如此。
(6)刷新Refresh_()函數(shù),在此函數(shù)中我們調(diào)用while()函數(shù)并在里面不斷執(zhí)行Movesnake()函數(shù)和Initmap()函數(shù)和refresh()函數(shù)和usleep()函數(shù)。通過以上函數(shù)的調(diào)用我們便能不斷地刷新顯示出蛇的整體運動,并調(diào)整地圖更新速度。
(7)刪除蛇節(jié)點函數(shù)DeleteNode()當(dāng)蛇沒有吃到食物需要保持原長度進(jìn)行移動時。我們就需要使頭節(jié)點指向下一個節(jié)點,釋放當(dāng)前節(jié)點來達(dá)到移動的效果。代碼如下:
void DeleteNode()
{
Struct tan *p=head;
Head=head->next;
Free(p);
}
(8)改變方向函數(shù)ChangeDir()在這里我們調(diào)用while()函數(shù)不斷獲取用戶輸入,當(dāng)我們檢測到用戶輸入時,該值便會在AddNode()函數(shù)中被調(diào)用來確定用戶輸入的方向從而進(jìn)行精準(zhǔn)走位,因為貪吃蛇的移動方向不能從左邊直接轉(zhuǎn)到右邊,所以我們需要判斷一下輸入的方向與當(dāng)前的方向的關(guān)系,我們在函數(shù)外用define重新定義 DOWN……,把相反的方向設(shè)為相反數(shù),當(dāng)我們從鍵盤輸入時,該值就會傳到turn()函數(shù)中進(jìn)行判斷,若與他自身的相反數(shù)不相等,則此值按照輸入方向執(zhí)行,若相等則按原方向繼續(xù)移動,turn()函數(shù)中我們把輸入的方向當(dāng)作參數(shù)傳進(jìn)來,讓參數(shù)的相反值與原方向相反值進(jìn)行比較得出結(jié)果。
(9)移動函數(shù)MoveSnake()每次調(diào)用此函數(shù)時節(jié)點就會按照方向的輸入增加一個,判斷蛇是否吃到食物,若吃到食物,調(diào)用Initfood()函數(shù)更新食物,若沒吃到食物則調(diào)用DeleteNode()函數(shù)刪掉頭節(jié)點達(dá)到蛇移動的效果。并在此函數(shù)中調(diào)用SnakeDie()函數(shù)判斷蛇是否撞墻或者撞到自身,若撞到則執(zhí)行InitSnake()函數(shù),否則按照原程序繼續(xù)前進(jìn)。
(10)初始化食物函數(shù)Initfood()為了讓食物隨機出現(xiàn)在地圖內(nèi)我們調(diào)用rand()函數(shù)給食物的橫,縱坐標(biāo)賦值并讓其與20取余
這樣就能保證它出現(xiàn)在地圖以內(nèi)。代碼如下:
void initfood(){
int x=rand()%20;
int y=rand()%20;
food.hang=x;
food.lie=y;
}
在主程序當(dāng)中我們需要調(diào)用線程來實現(xiàn)ChangeDir()和Refresh_()函數(shù),這樣才能保證程序不斷地刷新和獲取地圖的更新,應(yīng)用線程能降低程序的內(nèi)存,提高運行效率,給玩家?guī)砀玫挠脩趔w驗。
4? 程序測試
程序測試的目的是為了檢測程序有無編譯上的漏洞和邏輯上的漏洞。在編寫程序期間,作者每完成一個部分就去編寫運行一下。作者在測試運行時發(fā)現(xiàn),蛇的初始化都已經(jīng)完成,但當(dāng)玩家按下方向鍵時,地圖會在原來的地圖下面打印一個新地圖如圖2所示。
經(jīng)過查找信息發(fā)現(xiàn)是光標(biāo)的問題。找到這個bug后就需要程序員思考如何清除漏洞。這里我們在InitSnake()函數(shù)中調(diào)用move(0,0)函數(shù)并賦值便可幫助我們解決問題。
程序編寫完成,進(jìn)入最終測試階段。在小鍵盤上按下方向鍵蛇按照鍵入方向改變了原本的線路,并且當(dāng)蛇向右邊運行時按下左鍵蛇無反應(yīng),與我們設(shè)定的程序執(zhí)行效果一樣,說明程序輸入和蛇的移動沒有問題。對于地圖,我們可自行參照根據(jù)自身要求來設(shè)定。如果發(fā)現(xiàn)蛇移動的速度過快或過慢,或者想要改變速度,可以在程序中Refresh()函數(shù)中修改usleep()函數(shù)的參數(shù)。經(jīng)過最終測試,本程序通過編譯后沒有明顯的漏洞在邏輯上,最終效果如圖3所示,且運行結(jié)果與最初預(yù)想無較大不同。
5? 結(jié)? 論
本文基于Linux系統(tǒng)調(diào)用Ncurses庫用C語言和一些簡單的Linux的基礎(chǔ)知識完成了對貪吃蛇游戲的程序設(shè)計,算法分析等等。適合于剛學(xué)完C語言卻不知如何使用的同學(xué)們,并且還能簡單地認(rèn)識一下Linux系統(tǒng),每個環(huán)節(jié)的設(shè)計都源于C語言的基礎(chǔ)知識,例如鏈表、封裝函數(shù)、循環(huán)等等知識點,有利于啟發(fā)大家對以后編程的興趣。
參考文獻(xiàn):
[1] 李雨哲,孫煉.基于C語言的貪吃蛇游戲設(shè)計 [J].河南科技,2020(7):31-33.
[2] 張文華,廖俊杰,付乙眉.基于C語言的貪吃蛇游戲的簡單設(shè)計與實現(xiàn) [J].智富時代,2019(3):183.
[3] 王思樂,盧素魁,楊文柱,等.鏈表結(jié)構(gòu)在基于C語言項目中復(fù)用方法 [J].電腦編程技巧與維護(hù),2017(20):9-10+13.
[4] 匡泰,時允田.C語言程序設(shè)計項目式教程 編程語言 [M].北京:人民郵電出版社,2017.
[5] 周小云.C語言的快樂教學(xué)之旅 [J].課程教育研究,2013(2):156.
作者簡介:孫繼承(2000—),男,漢族,內(nèi)蒙古呼倫貝爾人,本科在讀,研究方向:計算機。