1、游戲展示
2、游戲功能
實(shí)現(xiàn)基本的功能:
? 貪吃蛇地圖繪制
? 蛇吃?物的功能(上、下、左、右?向鍵控制蛇的動(dòng)作)
? 蛇撞墻死亡
? 蛇撞??死亡
? 計(jì)算得分
? 蛇?加速、減速
? 暫停游戲
3、Win32 API
Win32 API是一套由Microsoft提供的應(yīng)用程序編程接口,用于開發(fā)Windows平臺(tái)上的應(yīng)用程序。它包括了豐富的函數(shù)、數(shù)據(jù)結(jié)構(gòu)和消息機(jī)制,允許開發(fā)者與操作系統(tǒng)進(jìn)行交互。這些接口覆蓋了各個(gè)方面,如圖形用戶界面(GUI)、文件和輸入輸出、多媒體、網(wǎng)絡(luò)通信等。通過調(diào)用這些API,開發(fā)者可以實(shí)現(xiàn)窗口創(chuàng)建、消息處理、事件響應(yīng)、內(nèi)存管理等功能,從而構(gòu)建功能完善的Windows應(yīng)用程序。Win32 API是基于C語言的,但也可以通過其他編程語言進(jìn)行調(diào)用。
實(shí)現(xiàn)貪吃蛇會(huì)使?到的?些Win32API知識(shí) ,下面我們來看看
3.1 控制臺(tái)程序
平常我們運(yùn)行起來的cmd命令框程序其實(shí)就是控制臺(tái)程序
比如我們可以使用cmd命令來設(shè)置控制臺(tái)窗口的大小
mode con cols=100 lines=30
也可以通過命令設(shè)置控制臺(tái)窗口的名字
title 愛學(xué)習(xí)的魚佬
這些能在控制臺(tái)窗口執(zhí)行的命令,也可以調(diào)用C語言函數(shù)system來執(zhí)行。例如:
#include<stdio.h>
int main(){
system("mode con cols=100 lines=30");
system("title 愛學(xué)習(xí)的魚佬");
return 0;
}
3.2 控制臺(tái)屏幕上的坐標(biāo)COORD
COORD是Windows API中定義的?個(gè)結(jié)構(gòu)體,表示?個(gè)字符在控制臺(tái)屏幕上的坐標(biāo)
typedef struct _COORD {
SHORT X;
SHORT Y;
} COORD, *PCOORD;
給坐標(biāo)賦值:
COORD pos={10,15};
3.3 GetStdHandle函數(shù)
GetStdHandle
函數(shù)是用于獲取標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出的句柄(handle)的 Windows API 函數(shù)。它通常用于控制臺(tái)應(yīng)用程序,允許你訪問這些標(biāo)準(zhǔn)流以進(jìn)行輸入和輸出操作。
以下是 GetStdHandle
函數(shù)的基本用法:
#include <windows.h>
int main() {
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hStderr = GetStdHandle(STD_ERROR_HANDLE);
// 使用 hStdout, hStdin, 和 hStderr 進(jìn)行輸入、輸出和報(bào)錯(cuò)操作
return 0;
}
這個(gè)示例演示如何獲取標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)錯(cuò)誤輸出的句柄,并將它們存儲(chǔ)在 HANDLE
變量中。你可以使用這些句柄來執(zhí)行與控制臺(tái)輸入和輸出相關(guān)的操作,例如寫入到控制臺(tái)或從控制臺(tái)讀取數(shù)據(jù)。
請注意,GetStdHandle
函數(shù)需要包含 <windows.h>
頭文件,并且通常與其他 Windows 控制臺(tái)函數(shù)一起使用,如 WriteFile
和 ReadFile
用于實(shí)際的輸入和輸出操作。
3.4 GetConsoleCursorInfo函數(shù)
GetConsoleCursorInfo
函數(shù)是一個(gè)用于獲取控制臺(tái)光標(biāo)信息的 Windows API 函數(shù)。它允許你檢索控制臺(tái)光標(biāo)的可見性和閃爍屬性以及光標(biāo)的大小。
以下是 GetConsoleCursorInfo
函數(shù)的基本用法:
#include <windows.h>
int main() {
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorInfo;
if (GetConsoleCursorInfo(hConsoleOutput, &cursorInfo)) {
// cursorInfo.dwSize 表示光標(biāo)的大小
// cursorInfo.bVisible 表示光標(biāo)是否可見
// cursorInfo.dwSize 和 cursorInfo.bVisible 可以用于讀取光標(biāo)的屬性
} else {
// 處理獲取光標(biāo)信息失敗的情況
}
return 0;
}
在上述示例中,我們首先獲取標(biāo)準(zhǔn)輸出句柄,并然后使用 GetConsoleCursorInfo
函數(shù)來檢索控制臺(tái)光標(biāo)的信息,包括光標(biāo)的大小 (dwSize
) 和可見性 (bVisible
)。獲取成功時(shí),你可以讀取這些屬性,以了解當(dāng)前光標(biāo)的狀態(tài)。
這個(gè)函數(shù)通常在控制臺(tái)應(yīng)用程序中用于獲取和修改光標(biāo)的屬性,例如改變光標(biāo)的可見性或大小。你還可以使用其他控制臺(tái)函數(shù)來設(shè)置新的光標(biāo)屬性,如 SetConsoleCursorInfo
來修改光標(biāo)的屬性。
3.4.1 CONSOLE_CURSOR_INFO結(jié)構(gòu)體
CONSOLE_CURSOR_INFO
是一個(gè)結(jié)構(gòu)體,用于控制和設(shè)置控制臺(tái)光標(biāo)的屬性。這個(gè)結(jié)構(gòu)體通常用于控制光標(biāo)的大小和可見性。
在 Windows API 中,CONSOLE_CURSOR_INFO
結(jié)構(gòu)體的定義如下:
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize; // 光標(biāo)的百分比高度 (1 到 100)
BOOL bVisible; // 光標(biāo)是否可見
} CONSOLE_CURSOR_INFO;
-
dwSize
表示光標(biāo)的大小,以百分比高度的形式表示,1 到 100 之間的值。光標(biāo)的高度由控制臺(tái)的字符單元高度和dwSize
決定。 -
bVisible
表示光標(biāo)的可見性,TRUE
表示光標(biāo)可見,FALSE
表示光標(biāo)不可見。
這個(gè)結(jié)構(gòu)體通常與 GetConsoleCursorInfo
和 SetConsoleCursorInfo
函數(shù)一起使用,用于獲取和設(shè)置控制臺(tái)光標(biāo)的屬性。通過設(shè)置 CONSOLE_CURSOR_INFO
結(jié)構(gòu)體的屬性,你可以控制控制臺(tái)中光標(biāo)的外觀和行為。
例如,可以使用這個(gè)結(jié)構(gòu)體來設(shè)置光標(biāo)的大小和可見性,然后將其傳遞給 SetConsoleCursorInfo
函數(shù),從而更改控制臺(tái)光標(biāo)的屬性
3.5 SetConsoleCursorInfo函數(shù)
SetConsoleCursorInfo
函數(shù)是一個(gè)用于設(shè)置控制臺(tái)光標(biāo)信息的 Windows API 函數(shù)。它允許你更改控制臺(tái)光標(biāo)的可見性、閃爍屬性以及光標(biāo)的大小。
以下是 SetConsoleCursorInfo
函數(shù)的基本用法:
#include <windows.h>
int main() {
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorInfo;
cursorInfo.dwSize = 25; // 設(shè)置光標(biāo)的大小,單位是百分之一
cursorInfo.bVisible = TRUE; // 設(shè)置光標(biāo)可見
if (SetConsoleCursorInfo(hConsoleOutput, &cursorInfo)) {
// 光標(biāo)屬性設(shè)置成功
} else {
// 處理設(shè)置光標(biāo)屬性失敗的情況
}
return 0;
}
在上述示例中,我們首先獲取標(biāo)準(zhǔn)輸出句柄,并然后使用 SetConsoleCursorInfo
函數(shù)來設(shè)置控制臺(tái)光標(biāo)的信息,包括光標(biāo)的大小 (dwSize
) 和可見性 (bVisible
)。你可以根據(jù)需要將這些屬性設(shè)置為所需的值。
這個(gè)函數(shù)通常在控制臺(tái)應(yīng)用程序中用于自定義控制臺(tái)光標(biāo)的屬性,例如更改光標(biāo)的大小或可見性。通過調(diào)用 SetConsoleCursorInfo
函數(shù),你可以實(shí)時(shí)更改控制臺(tái)光標(biāo)的外觀。如果需要獲取光標(biāo)信息,可以使用 GetConsoleCursorInfo
函數(shù)。
3.6 SetConsoleCursorPosition函數(shù)
SetConsoleCursorPosition
函數(shù)是一個(gè) Windows API 函數(shù),用于設(shè)置控制臺(tái)窗口的光標(biāo)位置。通過調(diào)用這個(gè)函數(shù),你可以將光標(biāo)移動(dòng)到指定的控制臺(tái)窗口坐標(biāo)位置。
以下是 SetConsoleCursorPosition
函數(shù)的基本用法:
#include <windows.h>
int main() {
HANDLE hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);
COORD cursorPosition;
cursorPosition.X = 10; // 設(shè)置光標(biāo)的水平位置
cursorPosition.Y = 5; // 設(shè)置光標(biāo)的垂直位置
if (SetConsoleCursorPosition(hConsoleOutput, cursorPosition)) {
// 光標(biāo)位置設(shè)置成功
} else {
// 處理設(shè)置光標(biāo)位置失敗的情況
}
return 0;
}
在上述示例中,我們首先獲取標(biāo)準(zhǔn)輸出句柄,然后創(chuàng)建一個(gè) COORD
結(jié)構(gòu)體,其中包括水平位置(X
)和垂直位置(Y
)。接著,我們使用 SetConsoleCursorPosition
函數(shù)來將光標(biāo)移動(dòng)到指定的控制臺(tái)窗口坐標(biāo)位置。
這個(gè)函數(shù)通常在控制臺(tái)應(yīng)用程序中用于控制光標(biāo)的移動(dòng),以便在控制臺(tái)上繪制文本或執(zhí)行其他操作。通過設(shè)置光標(biāo)位置,你可以控制光標(biāo)在控制臺(tái)窗口中的位置。
3.7 GetAsyncKeyState函數(shù)
GetAsyncKeyState
是一個(gè) Windows API 函數(shù),用于檢查指定虛擬鍵碼對應(yīng)的鍵是否處于按下狀態(tài)。它可以用來檢測鍵盤上的按鍵是否被按下,而且不會(huì)阻塞程序執(zhí)行,因此適用于實(shí)現(xiàn)基本的鍵盤輸入檢測。
以下是 GetAsyncKeyState
函數(shù)的基本用法:
#include <windows.h>
int main() {
// 檢查某個(gè)鍵是否被按下,比如檢查A鍵是否被按下
SHORT keyState = GetAsyncKeyState('A');
// 檢查鍵的狀態(tài)
if (keyState & 0x8000) {
// A鍵被按下
} else {
// A鍵沒有被按下
}
return 0;
}
在上述示例中,我們使用 GetAsyncKeyState
函數(shù)檢查鍵盤上的’A’鍵是否被按下。函數(shù)返回一個(gè) SHORT
類型的值,其中高位表示鍵的狀態(tài),如果鍵被按下,高位的最低位(最低有效位)將被設(shè)置為1,即0x8000。
這個(gè)函數(shù)通常用于游戲開發(fā)、輸入檢測以及其他需要實(shí)時(shí)鍵盤輸入的應(yīng)用程序,因?yàn)樗粫?huì)阻塞程序,可以在循環(huán)中持續(xù)檢查鍵的狀態(tài)。需要注意的是,GetAsyncKeyState
可以檢測虛擬鍵碼而不僅限于字符鍵,因此你可以使用虛擬鍵碼來檢查其他鍵盤上的按鍵。
每個(gè)按鍵都有對應(yīng)的虛擬鍵碼(Virtual-Key Codes),以下是一些常用鍵的虛擬鍵碼:
-
左鍵鼠標(biāo)按鈕:
VK_LBUTTON
(0x01) -
右鍵鼠標(biāo)按鈕:
VK_RBUTTON
(0x02) -
中鍵鼠標(biāo)按鈕:
VK_MBUTTON
(0x04) -
Backspace鍵:
VK_BACK
(0x08) -
Tab鍵:
VK_TAB
(0x09) -
Enter鍵:
VK_RETURN
(0x0D) -
Shift鍵:
VK_SHIFT
(0x10) -
Ctrl鍵:
VK_CONTROL
(0x11) -
Alt鍵:
VK_MENU
(0x12) -
Pause鍵:
VK_PAUSE
(0x13) -
Caps Lock鍵:
VK_CAPITAL
(0x14) -
Esc鍵:
VK_ESCAPE
(0x1B) -
空格鍵:
VK_SPACE
(0x20) -
Page Up鍵:
VK_PRIOR
(0x21) -
Page Down鍵:
VK_NEXT
(0x22) -
End鍵:
VK_END
(0x23) -
Home鍵:
VK_HOME
(0x24) -
左方向鍵:
VK_LEFT
(0x25) -
上方向鍵:
VK_UP
(0x26) -
右方向鍵:
VK_RIGHT
(0x27) -
下方向鍵:
VK_DOWN
(0x28) -
0鍵 (主鍵盤):
0x30
-
A鍵:
0x41
-
F1鍵:
VK_F1
(0x70) -
F2鍵:
VK_F2
(0x71) -
F3鍵:
VK_F3
(0x72) - …以此類推,F(xiàn)4至F12鍵的虛擬鍵碼為0x73至0x7C
GetAsyncKeyState
的返回值是short
類型,在上?次調(diào)用 GetAsyncKeyState
函數(shù)后,如果返回的16位的short
數(shù)據(jù)中,最高位是1,說明按鍵的狀態(tài)是按下,如果最高是0,說明按鍵的狀態(tài)是抬起;如果最低位被置為1則說明,該按鍵被按過,否則為0
所以我們要判斷?個(gè)鍵是否被按過,可以檢測 GetAsyncKeyState 返回值的最低值是否為1
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0)
4、設(shè)計(jì)貪吃蛇地圖
我們先看設(shè)計(jì)好的界面,控制臺(tái)窗口的坐標(biāo)如下所示,橫向的是X軸,從左向右依次增長,縱向是Y軸,從上到下依次增長
在游戲地圖上,我們打印墻體使用寬字符:□,打印蛇使用寬字符●,打印食物使用寬字符★
普通的字符是占?個(gè)字節(jié)的,這類寬字符是占?2個(gè)字節(jié)。
C語言的國際化特性相關(guān)的知識(shí),過去C語言并不適合非英語國家(地區(qū))使用。
C語言字符默認(rèn)是采用ASCII編碼的,ASCI字符集采用的是單字節(jié)編碼,且只使用了單字節(jié)中的低7位,最高位是沒有使用的,可表示為
0xxxxxxxx
;可以看到,ASCII字符集共包含128個(gè)字符,在英語國家中,128個(gè)字符是基本夠用的,但是,在其他國家語言中,比如,在法語中,字母上方有注音符號,它就無法用 ASCII 碼表示。于是,一些歐洲國家就決定,利用字節(jié)中閑置的最高位編入新的符號。比如,法語中的e的編碼為130(二進(jìn)制10000010
)。這樣一來,這些歐洲國家使用的編碼體系,可以表示最多256個(gè)符號。但是,這里又出現(xiàn)了新的問題。不同的國家有不同的字母,因此,哪怕它們都使用256個(gè)符號的編碼方式,代表的字母卻不一樣。比如,130在法語編碼中代表了,在希伯來語編碼中卻代表了字母Gimel(區(qū)),在俄語編碼中又會(huì)代表另一個(gè)符號。但是不管怎樣,所有這些編碼方式中,0–127表示的符號是一樣的,不一樣的只是128--255
的這一段至于亞洲國家的文字,使用的符號就更多了,漢字就多達(dá)10萬左右。一個(gè)字節(jié)只能表示256種符號,肯定是不夠的,就必須使用多個(gè)字節(jié)表達(dá)一個(gè)符號。比如,簡體中文常見的編碼方式是
GB2312
,使用兩個(gè)字節(jié)表示一個(gè)漢字,所以理論上最多可以表示256 x 256=65536
個(gè)字符
后來為了使C語言適應(yīng)國際化,C語言的標(biāo)準(zhǔn)中不斷加入了國際化的支持。比如:加入和寬字符的類型wchar_t
和寬字符的輸入和輸出函數(shù),加入<locale.h>
頭文件,其中提供了允許程序員針對特定地區(qū) (通常是國家或者說某種特定語言的地理區(qū)域)調(diào)整程序行為的函數(shù)。
4.1 <locale.h>
<locale.h>
是C語言標(biāo)準(zhǔn)庫中的頭文件,提供了有關(guān)本地化(localization)和本地化相關(guān)函數(shù)的支持。本地化涉及根據(jù)特定地區(qū)或文化習(xí)慣的需求來格式化文本、日期、時(shí)間和數(shù)字等。這有助于使程序適應(yīng)不同的地域文化。
在C語言中,<locale.h>
提供了設(shè)置地區(qū)和語言環(huán)境的函數(shù),例如 setlocale
,允許程序員根據(jù)所需的語言環(huán)境進(jìn)行設(shè)置。它還提供了函數(shù)來處理在不同地區(qū)設(shè)置中使用的格式化、排序、貨幣和日期時(shí)間信息。
一些常見的函數(shù)和功能包括:
-
setlocale
: 設(shè)置程序的當(dāng)前地區(qū)和語言環(huán)境,允許程序根據(jù)特定的語言環(huán)境來處理數(shù)據(jù)。 -
localeconv
: 返回一個(gè)描述當(dāng)前地區(qū)設(shè)置中的數(shù)值格式的結(jié)構(gòu)體。 -
strftime
: 根據(jù)指定的格式化字符串將日期和時(shí)間格式化為本地化格式。 -
printf
和scanf
系列函數(shù)中的格式化標(biāo)志: 例如%n
和%Ld
等,可以受到地區(qū)設(shè)置的影響而改變輸出或輸入的格式。
這些函數(shù)可以根據(jù)地區(qū)設(shè)置調(diào)整貨幣符號、日期格式、時(shí)間格式、數(shù)字分隔符等,從而使程序在不同的文化環(huán)境下更適用和易懂。
在 <locale.h>
頭文件中,定義了一系列常量用于設(shè)置不同的本地化范疇。這些常量用于確定在程序中所需的不同本地化設(shè)置。以下是一些常用的常量:
-
LC_ALL
: 代表所有的本地化設(shè)置。 -
LC_COLLATE
: 字符串排序規(guī)則的設(shè)置。 -
LC_CTYPE
: 字符分類和轉(zhuǎn)換規(guī)則的設(shè)置。 -
LC_MONETARY
: 通貨格式的設(shè)置。 -
LC_NUMERIC
: 數(shù)值格式的設(shè)置。 -
LC_TIME
: 時(shí)間和日期格式的設(shè)置。
這些常量可以作為 setlocale()
函數(shù)的參數(shù),用于設(shè)置程序中的不同本地化范疇。例如,setlocale(LC_TIME, "en_US")
用于設(shè)置日期和時(shí)間格式為美國英語,setlocale(LC_MONETARY, "fr_FR")
用于設(shè)置貨幣格式為法國法語等。
4.2 setlocale函數(shù)
char *setlocale(int category, const char *locale);
-
category
是要設(shè)置的本地化范疇,可以是諸如LC_ALL
、LC_COLLATE
、LC_CTYPE
、LC_MONETARY
、LC_NUMERIC
、LC_TIME
等預(yù)定義常量之一。 -
locale
是一個(gè)字符串,表示要設(shè)置的本地化環(huán)境,比如 “zh_CN” 表示中文的本地化設(shè)置。
示例用法如下:
#include <stdio.h>
#include <locale.h>
int main() {
setlocale(LC_ALL, "en_US.UTF-8");
// 其他程序邏輯...
return 0;
}
"zh_CN.UTF-8"
中的 "zh_CN"
是表示中文(中國)的語言代碼,"UTF-8"
表示使用 UTF-8 編碼
需要注意的是,setlocale
函數(shù)在不同的操作系統(tǒng)或環(huán)境下可能有不同的支持程度,有些環(huán)境可能并不支持特定的本地化設(shè)置。
C標(biāo)準(zhǔn)給第二個(gè)參數(shù)僅定義了2種可能取值:"C"和” "
在任意程序執(zhí)行開始,都會(huì)隱藏式執(zhí)行調(diào)用:
setlocale(LC_ALL, "C");
當(dāng)?shù)貐^(qū)設(shè)置為"C"
時(shí),庫函數(shù)按正常方式執(zhí)行,小數(shù)點(diǎn)是一個(gè)點(diǎn)。當(dāng)程序運(yùn)行起來后想改變地區(qū),就只能顯式調(diào)用setlocale
函數(shù)。用" "
作為第2個(gè)參數(shù),調(diào)用setlocale
函數(shù)就可以切換到本地模式,這種模式下程序會(huì)適應(yīng)本地環(huán)境。
setlocale(LC_ALL, " ");//切換到本地環(huán)境
4.3 寬字符的打印
在 C 語言中,你可以使用 wchar_t
類型和一些相關(guān)的寬字符函數(shù)來處理和打印寬字符。
首先,確保你的編譯環(huán)境支持寬字符,并且使用寬字符格式的輸出函數(shù)。
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main() {
setlocale(LC_ALL, ""); // 設(shè)置本地化環(huán)境,以支持寬字符
char ch1='a';
char ch2='b'; //普通字符
wchar_t wideChar1 = L'你';
wchar_t wideChar2 = L'●'; // 使用寬字符
printf("%c%c\n",ch1,ch2); //輸出普通字符
wprintf(L"%lc\n%lc\n", wideChar1,wideChar2); // 使用wprintf輸出寬字符
return 0;
}
-
wchar_t
是用于表示寬字符的 C 語言數(shù)據(jù)類型。 -
wprintf
是用于打印寬字符的函數(shù),L"..."
表示寬字符常量。
setlocale(LC_ALL, "");
這行代碼設(shè)置程序的本地化環(huán)境以支持寬字符的處理和顯示。使用 wprintf
和 wchar_t
類型可以正確地處理和打印寬字符。
輸出結(jié)果
從輸出的結(jié)果來看,我們發(fā)現(xiàn)一個(gè)普通字符占一個(gè)字符的位置但是打印一個(gè)漢字字符或特殊符號,占用2個(gè)字符的位置,那么我們?nèi)绻谪澇陨咧惺褂脤捵址偷锰幚砗玫貓D上坐標(biāo)的計(jì)算。
4.4 地圖坐標(biāo)及蛇身和食物
按照我們上面實(shí)現(xiàn)的效果,這里的游戲地圖區(qū)域是58列,27行達(dá)到的效果,當(dāng)然你也可以根據(jù)自己的情況進(jìn)行修改
初始化狀態(tài),假設(shè)蛇的長度是5,蛇身的每個(gè)節(jié)點(diǎn)是●,在固定的一個(gè)坐標(biāo)處,比如上面的地圖中(20,6)處開始出現(xiàn)蛇,連續(xù)5個(gè)節(jié)點(diǎn)。
注意:蛇的每個(gè)節(jié)點(diǎn)的x坐標(biāo)必須是2個(gè)倍數(shù),否則可能會(huì)出現(xiàn)蛇的一個(gè)節(jié)點(diǎn)有一半兒出現(xiàn)在墻體中,另外一般在墻外的現(xiàn)象,坐標(biāo)不好對齊。關(guān)于食物,就是在墻體內(nèi)隨機(jī)生成一個(gè)坐標(biāo)**(x坐標(biāo)必須是2的倍數(shù))**,坐標(biāo)不能和蛇的身體重合,然后打印大。
5. 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)
5.1 蛇節(jié)點(diǎn)
在游戲運(yùn)行的過程中,蛇每次吃一個(gè)食物,蛇的身體就會(huì)變長一節(jié),如果我們使用鏈表存儲(chǔ)蛇的信息,那么蛇的每一節(jié)其實(shí)就是鏈表的每個(gè)節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)只要記錄好蛇身節(jié)點(diǎn)在地圖上的坐標(biāo)就行.所以蛇節(jié)點(diǎn)結(jié)構(gòu)如下:
typedef struct SnakeNode
{
int x;
int y;
struct SnakeNode* next;
}SnakeNode, * pSnakeNode;
pSnakeNode
: 這個(gè)別名是一個(gè)指向 SnakeNode
結(jié)構(gòu)體的指針類型,使得你可以更簡潔地聲明指向 SnakeNode
的指針變量
5.2 蛇狀態(tài)結(jié)構(gòu)
typedef struct Snake
{
pSnakeNode _pSnake;//維護(hù)整條蛇的指針
pSnakeNode _pFood;//維護(hù)食物的指針
enum DIRECTION _Dir;//蛇頭的方向默認(rèn)是向右
enum GAME_STATUS _Status;//游戲狀態(tài)
int _Socre;//當(dāng)前獲得分?jǐn)?shù)
int _Add;//默認(rèn)每個(gè)食物10分
int _SleepTime;//每走一步休眠時(shí)間
}Snake, * pSnake;
5.3 蛇的方向
enum DIRECTION
{
UP = 1,
DOWN,
LEFT,
RIGHT
};
5.4 游戲狀態(tài)
enum GAME_STATUS
{
OK,//正常運(yùn)行
KILL_BY_WALL,//撞墻
KILL_BY_SELF,//咬到自己
END_NOMAL//正常結(jié)束
};
7. 游戲整體流程
8. 核心邏輯實(shí)現(xiàn)
8.1 游戲主邏輯
#include "Snake.h"
void test()
{
int ch = 0;
srand((unsigned int)time(NULL));
do
{
Snake snake = { 0 };
GameStart(&snake);
GameRun(&snake);
GameEnd(&snake);
SetPos(20, 15);
printf("再來一局嗎?(Y/N):");
ch = getchar();
getchar();//清理\n
} while (ch == 'Y');
SetPos(0, 27);
}
int main()
{
//修改當(dāng)前地區(qū)為本地模式,為了支持中文寬字符的打印
setlocale(LC_ALL, "");
//測試邏輯
test();
return 0;
}
8.2 游戲開始
void GameStart(pSnake ps)
{
system("mode con cols=100 lines=30");
system("title 貪吃蛇");
//獲取標(biāo)準(zhǔn)輸出的句柄(用來標(biāo)識(shí)不同設(shè)備的數(shù)值)
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//隱藏光標(biāo)操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//獲取控制臺(tái)光標(biāo)信息
CursorInfo.bVisible = false; //隱藏控制臺(tái)光標(biāo)
SetConsoleCursorInfo(hOutput, &CursorInfo);//設(shè)置控制臺(tái)光標(biāo)狀態(tài)
//打印歡迎界面
WelcomeToGame();
//打印地圖
CreateMap();
//初始化蛇
InitSnake(ps);
//創(chuàng)造第一個(gè)食物
CreateFood(ps);
}
-
設(shè)置控制臺(tái)窗口大小和標(biāo)題:
-
system("mode con cols=100 lines=30");
用于設(shè)置控制臺(tái)窗口的大小,將其設(shè)定為 100 列和 30 行。 -
system("title 貪吃蛇");
設(shè)置了控制臺(tái)窗口的標(biāo)題為 “貪吃蛇”。
-
-
隱藏控制臺(tái)光標(biāo):
- 使用
GetStdHandle
獲取標(biāo)準(zhǔn)輸出的句柄hOutput
,然后通過GetConsoleCursorInfo
獲取控制臺(tái)光標(biāo)的信息。 - 將光標(biāo)狀態(tài)設(shè)置為不可見:
CursorInfo.bVisible = false;
- 最后通過
SetConsoleCursorInfo
設(shè)置控制臺(tái)光標(biāo)的狀態(tài)。
- 使用
-
顯示歡迎界面:
- 調(diào)用
WelcomeToGame()
函數(shù),這個(gè)函數(shù)可能打印一些歡迎信息和游戲的介紹。
- 調(diào)用
-
打印地圖、初始化蛇和創(chuàng)建第一個(gè)食物:
-
CreateMap()
函數(shù)負(fù)責(zé)繪制游戲地圖。 -
InitSnake()
初始化蛇的數(shù)據(jù)結(jié)構(gòu)和顯示蛇的初始狀態(tài)。 -
CreateFood()
創(chuàng)建游戲中的第一個(gè)食物。
-
8.2.0 SetPos函數(shù)的封裝
void SetPos(short x, short y)
{
COORD pos = { x, y };
HANDLE hOutput = NULL;
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hOutput, pos);
}
-
定義函數(shù) SetPos:
-
void SetPos(short x, short y)
:指定了該函數(shù)接受兩個(gè)參數(shù),分別是 x 和 y 坐標(biāo)。
-
-
設(shè)置光標(biāo)位置:
-
COORD pos = { x, y };
:創(chuàng)建了一個(gè)COORD
類型的結(jié)構(gòu)體變量pos
,用給定的 x 和 y 坐標(biāo)值初始化。 -
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
:獲取標(biāo)準(zhǔn)輸出的句柄,用來標(biāo)識(shí)控制臺(tái)窗口。 -
SetConsoleCursorPosition(hOutput, pos);
:使用SetConsoleCursorPosition
函數(shù)將控制臺(tái)光標(biāo)的位置設(shè)置為pos
所指定的坐標(biāo)。
-
8.2.1 歡迎界面和提示
void WelcomeToGame()
{
SetPos(40, 15);
printf("歡迎來到貪吃蛇小游戲");
SetPos(40, 25);//讓按任意鍵繼續(xù)的出現(xiàn)的位置好看點(diǎn)
system("pause");
system("cls");
SetPos(25, 12);
printf("用 ↑ . ↓ . ← . → 分別控制蛇的移動(dòng), F3為加速,F(xiàn)4為減速\n");
SetPos(25, 13);
printf("加速將能得到更高的分?jǐn)?shù)。\n");
SetPos(40, 25);//讓按任意鍵繼續(xù)的出現(xiàn)的位置好看點(diǎn)
system("pause");
system("cls");
}
-
設(shè)置光標(biāo)位置并打印歡迎信息:
-
SetPos(40, 15); printf("歡迎來到貪吃蛇小游戲");
將光標(biāo)移動(dòng)到 (40, 15) 的位置,打印歡迎信息 “歡迎來到貪吃蛇小游戲”。
-
-
等待用戶按下任意鍵繼續(xù):
-
SetPos(40, 25);
設(shè)置光標(biāo)位置,這是為了讓下一次的system("pause");
顯示在一個(gè)美觀的位置。 -
system("pause");
暫停程序執(zhí)行,等待用戶按下任意鍵繼續(xù)。
-
-
清屏:
-
system("cls");
清空控制臺(tái)屏幕。
-
-
打印游戲操作說明:
-
SetPos(25, 12); printf("用 ↑ . ↓ . ← . → 分別控制蛇的移動(dòng), F3為加速,F(xiàn)4為減速\n");
在指定位置打印游戲操作說明,告訴用戶如何控制蛇的移動(dòng)以及使用 F3 和 F4 來加速或減速。 -
SetPos(25, 13); printf("加速將能得到更高的分?jǐn)?shù)。\n");
給出一些游戲策略建議。
-
-
再次等待用戶按下任意鍵繼續(xù):
-
SetPos(40, 25);
再次設(shè)置光標(biāo)位置,保證下一次的system("pause");
顯示在一個(gè)合適的位置。 -
system("pause");
再次暫停程序執(zhí)行,等待用戶按下任意鍵繼續(xù)。
-
這個(gè)函數(shù)的目的是為了向玩家展示游戲的歡迎信息和操作說明,在游戲開始前給玩家提供一些基本的操作提示和規(guī)則。
歡迎界面
提示界面
8.2.2 創(chuàng)建地圖
創(chuàng)建地圖就是將墻打印出來,因?yàn)槭菍捵址蛴?,所有使?code>wprintf函數(shù),打印格式串前使用L
打印地圖的關(guān)鍵是要算好坐標(biāo),才能在想要的位置打印墻體。
墻體打印的寬字符:
#define WALL L'□'
創(chuàng)建地圖
void CreateMap()
{
int i = 0;
SetPos(0, 0);
for (i = 0; i < 58; i += 2)
{
wprintf(L"%c", WALL);
}
SetPos(0, 26);
for (i = 0; i < 58; i += 2)
{
wprintf(L"%c", WALL);
}
for (i = 1; i < 26; i++)
{
SetPos(0, i);
wprintf(L"%c", WALL);
}
for (i = 1; i < 26; i++)
{
SetPos(56, i);
wprintf(L"%c", WALL);
}
}
繪制游戲地圖:
-
SetPos(0, 0);
將光標(biāo)移動(dòng)到 (0, 0) 的位置開始繪制游戲地圖。 - 通過
for
循環(huán)和wprintf
函數(shù),從 (0,0) 到 (56,0) 繪制頂部墻體,由WALL
符號構(gòu)成。 - 從 (0,26) 到 (56,26) 繪制底部墻體。
- 從 (0,1) 到 (0,25) 繪制左側(cè)墻體。
- 從 (56,1) 到 (56,25) 繪制右側(cè)墻體。
8.2.3 初始化蛇身
蛇最開始長度為5節(jié),每節(jié)對應(yīng)鏈表的一個(gè)節(jié)點(diǎn),蛇身的每一個(gè)節(jié)點(diǎn)都有自己的坐標(biāo)
創(chuàng)建5個(gè)節(jié)點(diǎn),然后將每個(gè)節(jié)點(diǎn)存放在鏈表中進(jìn)行管理。創(chuàng)建完蛇身后,將蛇的每一節(jié)打印在屏幕上
蛇身打印的寬字符:
#define BODY L'●'
初始化蛇身函數(shù)
void InitSnake(pSnake ps)
{
pSnakeNode cur = NULL;
int i = 0;
//創(chuàng)建蛇身節(jié)點(diǎn),并初始化坐標(biāo)
for (i = 0; i < 5; i++)
{
//創(chuàng)建蛇身的節(jié)點(diǎn)
cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("InitSnake()::malloc()");
return;
}
//設(shè)置坐標(biāo)
cur->next = NULL;
cur->x = POS_X + i * 2;
cur->y = POS_Y;
//頭插法
if (ps->_pSnake == NULL)
{
ps->_pSnake = cur;
}
else
{
cur->next = ps->_pSnake;
ps->_pSnake = cur;
}
}
//打印蛇的身體
cur = ps->_pSnake;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%c", BODY);
cur = cur->next;
}
//初始化貪吃蛇數(shù)據(jù)
ps->_SleepTime = 200;
ps->_Socre = 0;
ps->_Status = OK;
ps->_Dir = RIGHT;
ps->_Add = 10;
}
創(chuàng)建貪吃蛇身體節(jié)點(diǎn):
- 使用
for
循環(huán)創(chuàng)建貪吃蛇的五個(gè)身體段。 - 每個(gè)段都是使用
malloc
動(dòng)態(tài)分配的。 - 為每個(gè)段設(shè)置坐標(biāo),創(chuàng)建了貪吃蛇身體的水平線。
打印貪吃蛇身體:
- 創(chuàng)建段之后,代碼通過遍歷貪吃蛇的鏈表,將光標(biāo)移動(dòng)到每個(gè)段的坐標(biāo)并使用
wprintf
打印貪吃蛇的身體,使用宏BODY
符號。
初始化游戲變量:
- 最后,函數(shù)初始化各種游戲相關(guān)變量,如休眠時(shí)間、得分、方向和額外得分規(guī)則。
8.2.4 創(chuàng)建食物
注意:
x坐標(biāo)必須是2的倍數(shù)
食物的坐標(biāo)不能和蛇身每個(gè)節(jié)點(diǎn)的坐標(biāo)重復(fù)
食物打印的寬字符:
#define FOOD L'★'
創(chuàng)建食物函數(shù)
void CreateFood(pSnake ps)
{
int x = 0;
int y = 0;
again:
//產(chǎn)生的x坐標(biāo)應(yīng)該是2的倍數(shù),這樣才可能和蛇頭坐標(biāo)對齊。
do
{
x = rand() % 53 + 2;
y = rand() % 25 + 1;
} while (x % 2 != 0);
pSnakeNode cur = ps->_pSnake;//獲取指向蛇頭的指針
//食物不能和蛇身沖突
while (cur)
{
if (cur->x == x && cur->y == y)
{
goto again;
}
cur = cur->next;
}
pSnakeNode pFood = (pSnakeNode)malloc(sizeof(SnakeNode)); //創(chuàng)建食物
if (pFood == NULL)
{
perror("CreateFood::malloc()");
return;
}
else
{
pFood->x = x;
pFood->y = y;
SetPos(pFood->x, pFood->y);
wprintf(L"%c", FOOD);
ps->_pFood = pFood;
}
}
生成食物位置:
- 使用
do-while
循環(huán)生成食物的 x 和 y 坐標(biāo)。 - x 坐標(biāo)被限制為偶數(shù),以便與貪吃蛇的頭部坐標(biāo)對齊。
避免食物與蛇身沖突:
- 代碼通過檢查新生成的食物位置,確保它不會(huì)與貪吃蛇的身體重疊。
- 如果新的食物坐標(biāo)與蛇身重疊,代碼會(huì)跳回
again
標(biāo)簽處重新生成食物位置。
創(chuàng)建食物節(jié)點(diǎn):
- 如果食物的位置是有效的,就會(huì)動(dòng)態(tài)分配一個(gè)新的節(jié)點(diǎn)來表示食物。
- 食物節(jié)點(diǎn)的坐標(biāo)被設(shè)置為新的 x 和 y 坐標(biāo),然后在控制臺(tái)上的該位置打印食物的符號,并將食物節(jié)點(diǎn)指針保存在游戲狀態(tài)中。
8.3 游戲運(yùn)行
游戲運(yùn)行期間,右側(cè)打印幫助信息,提示玩家
根據(jù)游戲狀態(tài)檢查游戲是否繼續(xù),如果是狀態(tài)是OK,游戲繼續(xù),否則游戲結(jié)束
如果游戲繼續(xù),就是檢測按鍵情況,確定蛇下一步的方向,或者是否加速減速,是否暫?;蛘咄顺鲇螒?。
void GameRun(pSnake ps)
{
//打印右側(cè)幫助信息
PrintHelpInfo();
do
{
SetPos(64, 10);
printf("得分:%d ", ps->_Socre);
printf("每個(gè)食物得分:%d分", ps->_Add);
if (KEY_PRESS(VK_UP) && ps->_Dir != DOWN)
{
ps->_Dir = UP;
}
else if (KEY_PRESS(VK_DOWN) && ps->_Dir != UP)
{
ps->_Dir = DOWN;
}
else if (KEY_PRESS(VK_LEFT) && ps->_Dir != RIGHT)
{
ps->_Dir = LEFT;
}
else if (KEY_PRESS(VK_RIGHT) && ps->_Dir != LEFT)
{
ps->_Dir = RIGHT;
}
else if (KEY_PRESS(VK_SPACE))
{
pause();
}
else if (KEY_PRESS(VK_ESCAPE))
{
ps->_Status = END_NOMAL;
break;
}
else if (KEY_PRESS(VK_F3))
{
if (ps->_SleepTime >= 50)
{
ps->_SleepTime -= 30;
ps->_Add += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
if (ps->_SleepTime < 350)
{
ps->_SleepTime += 30;
ps->_Add -= 2;
if (ps->_SleepTime == 350)
{
ps->_Add = 1;
}
}
}
//蛇每次一定之間要休眠的時(shí)間,時(shí)間短,蛇移動(dòng)速度就快
Sleep(ps->_SleepTime);
SnakeMove(ps);
} while (ps->_Status == OK);
}
這段代碼實(shí)現(xiàn)了游戲的運(yùn)行邏輯,在一個(gè)循環(huán)中持續(xù)地更新游戲狀態(tài)并響應(yīng)玩家的輸入。
-
打印幫助信息:
-
PrintHelpInfo()
被調(diào)用來顯示游戲幫助信息。
-
-
游戲循環(huán):
- 游戲的主要邏輯被包含在一個(gè) do-while 循環(huán)中。
- 檢查玩家的按鍵輸入,根據(jù)按鍵不同做出相應(yīng)的動(dòng)作。
- 根據(jù)按鍵狀態(tài)來改變蛇頭的移動(dòng)方向。
- 如果按下空格鍵,游戲會(huì)暫停。
- 如果按下 ESC 鍵,游戲狀態(tài)被設(shè)為正常結(jié)束(END_NOMAL),并跳出循環(huán)。
-
加速和減速:
- 如果按下 F3,游戲中蛇的移動(dòng)速度會(huì)加快,得分增加。
- 如果按下 F4,游戲中蛇的移動(dòng)速度會(huì)減慢,得分減少。
-
蛇的移動(dòng):
- 游戲根據(jù)當(dāng)前設(shè)置的移動(dòng)速度來控制蛇的移動(dòng)。
- 使用
Sleep()
函數(shù)來控制每次蛇移動(dòng)之間的時(shí)間間隔。 - 調(diào)用
SnakeMove()
函數(shù)來處理蛇的移動(dòng)邏輯。
-
循環(huán)終止:
- 游戲循環(huán)會(huì)持續(xù)進(jìn)行,直到游戲狀態(tài)變?yōu)榉钦顟B(tài)(不是 OK)為止。
8.3.1 幫助信息
void PrintHelpInfo()
{
//打印提示信息
SetPos(64, 15);
printf("不能穿墻,不能咬到自己\n");
SetPos(64, 16);
printf("用↑.↓.←.→分別控制蛇的移動(dòng).");
SetPos(64, 17);
printf("F1 為加速,F(xiàn)2 為減速\n");
SetPos(64, 18);
printf("ESC :退出游戲.space:暫停游戲.");
SetPos(64, 20);
printf("愛學(xué)習(xí)的魚佬");
}
8.3.2 蛇身移動(dòng)
void SnakeMove(pSnake ps)
{
//創(chuàng)建下一個(gè)節(jié)點(diǎn)
pSnakeNode pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pNextNode == NULL)
{
perror("SnakeMove()::malloc()");
return;
}
//確定下一個(gè)節(jié)點(diǎn)的坐標(biāo),下一個(gè)節(jié)點(diǎn)的坐標(biāo)根據(jù),蛇頭的坐標(biāo)和方向確定
switch (ps->_Dir)
{
case UP:
{
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y - 1;
}
break;
case DOWN:
{
pNextNode->x = ps->_pSnake->x;
pNextNode->y = ps->_pSnake->y + 1;
}
break;
case LEFT:
{
pNextNode->x = ps->_pSnake->x - 2;
pNextNode->y = ps->_pSnake->y;
}
break;
case RIGHT:
{
pNextNode->x = ps->_pSnake->x + 2;
pNextNode->y = ps->_pSnake->y;
}
break;
}
//如果下一個(gè)位置就是食物
if (NextIsFood(pNextNode, ps))
{
EatFood(pNextNode, ps);
}
else//如果沒有食物
{
NoFood(pNextNode, ps);
}
KillByWall(ps);
KillBySelf(ps);
}
-
創(chuàng)建下一個(gè)節(jié)點(diǎn):利用
malloc()
分配內(nèi)存來創(chuàng)建一個(gè)新的蛇身節(jié)點(diǎn)pNextNode
。 - 根據(jù)方向確定下一個(gè)節(jié)點(diǎn)的坐標(biāo):根據(jù)蛇頭當(dāng)前的方向,計(jì)算出蛇頭下一個(gè)移動(dòng)的位置。
-
檢查下一個(gè)位置是否是食物:
- 如果下一個(gè)位置是食物,調(diào)用
EatFood(pNextNode, ps)
函數(shù)來處理吃食物的邏輯。 - 如果不是食物,調(diào)用
NoFood(pNextNode, ps)
函數(shù)來處理不吃食物的邏輯。
- 如果下一個(gè)位置是食物,調(diào)用
-
檢查游戲結(jié)束的條件:
- 調(diào)用
KillByWall(ps)
和KillBySelf(ps)
函數(shù)來檢查是否撞墻或者咬到自己,如果游戲結(jié)束,則會(huì)設(shè)定相應(yīng)的游戲狀態(tài)。
- 調(diào)用
8.3.3 吃食物的三種情況
int NextIsFood(pSnakeNode psn, pSnake ps)
{
return (psn->x == ps->_pFood->x) && (psn->y == ps->_pFood->y);
}
void EatFood(pSnakeNode psn, pSnake ps)
{
//頭插法
psn->next = ps->_pSnake;
ps->_pSnake = psn;
pSnakeNode cur = ps->_pSnake;
//打印蛇
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%c", BODY);
cur = cur->next;
}
ps->_Socre += ps->_Add;
free(ps->_pFood);
CreateFood(ps);
}
void NoFood(pSnakeNode psn, pSnake ps)
{
//頭插法
psn->next = ps->_pSnake;
ps->_pSnake = psn;
pSnakeNode cur = ps->_pSnake;
//打印蛇
while (cur->next->next)
{
SetPos(cur->x, cur->y);
wprintf(L"%c", BODY);
cur = cur->next;
}
//最后一個(gè)位置打印空格,然后釋放節(jié)點(diǎn)
SetPos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
-
NextIsFood
:該函數(shù)檢查下一個(gè)位置是否有食物。如果坐標(biāo)與食物位置匹配,返回真;否則返回假。 -
EatFood
:當(dāng)下一個(gè)位置有食物時(shí)調(diào)用此函數(shù)。它采用鏈表方法,在蛇的開頭插入表示下一個(gè)位置的新節(jié)點(diǎn),從而延長蛇的身體。它更新蛇的分?jǐn)?shù),移除被吃掉的食物(通過釋放內(nèi)存),然后創(chuàng)建新的食物以繼續(xù)游戲。 -
NoFood
:當(dāng)下一個(gè)位置沒有食物時(shí)調(diào)用此函數(shù)。它也采用鏈表方法,在蛇的開頭插入表示下一個(gè)位置的新節(jié)點(diǎn)。然而,它通過移除最后一個(gè)節(jié)點(diǎn)來管理蛇尾,模擬蛇的移動(dòng)。然后釋放最后一個(gè)節(jié)點(diǎn)的內(nèi)存,并設(shè)置適當(dāng)?shù)闹羔槨?/li>
8.3.4 G掉的2種情況
int KillByWall(pSnake ps)
{
if ((ps->_pSnake->x == 0)
|| (ps->_pSnake->x == 56)
|| (ps->_pSnake->y == 0)
|| (ps->_pSnake->y == 26))
{
ps->_Status = KILL_BY_WALL;
return 1;
}
return 0;
}
int KillBySelf(pSnake ps)
{
pSnakeNode cur = ps->_pSnake->next;
while (cur)
{
if ((ps->_pSnake->x == cur->x)
&& (ps->_pSnake->y == cur->y))
{
ps->_Status = KILL_BY_SELF;
return 1;
}
cur = cur->next;
}
return 0;
}
-
KillByWall
檢查蛇頭是否碰到了游戲地圖的邊界。如果蛇頭在地圖的邊緣,函數(shù)將設(shè)置蛇的狀態(tài)為KILL_BY_WALL
并返回 1(表示蛇碰到了墻壁),否則返回 0。 -
KillBySelf
檢查蛇頭是否碰到了自己的身體。它遍歷了蛇身鏈表(除了頭節(jié)點(diǎn)),檢查蛇頭的坐標(biāo)是否與任何其他節(jié)點(diǎn)的坐標(biāo)相匹配。如果發(fā)生了碰撞,函數(shù)將設(shè)置蛇的狀態(tài)為KILL_BY_SELF
并返回 1(表示蛇碰到了自己的身體),否則返回 0。
8.3.5 暫停函數(shù)
void pause()
{
while (1)
{
Sleep(300);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
它會(huì)在游戲執(zhí)行時(shí)進(jìn)行循環(huán),每 300 毫秒檢查一次是否按下空格鍵。當(dāng)檢測到空格鍵按下時(shí),循環(huán)結(jié)束,游戲繼續(xù)執(zhí)行文章來源:http://www.zghlxwxcb.cn/news/detail-815743.html
8.4 游戲結(jié)束
void GameEnd(pSnake ps)
{
pSnakeNode cur = ps->_pSnake;
SetPos(24, 12);
switch (ps->_Status)
{
case END_NOMAL:
printf("您主動(dòng)退出游戲\n");
break;
case KILL_BY_SELF:
printf("您撞上自己了 ,游戲結(jié)束!\n");
break;
case KILL_BY_WALL:
printf("您撞墻了,游戲結(jié)束!\n");
break;
}
while (cur)
{
pSnakeNode del = cur;
cur = cur->next;
free(del);
}
}
根據(jù)游戲狀態(tài)在屏幕上顯示相應(yīng)的消息,例如玩家主動(dòng)退出游戲、蛇撞到自己或撞到墻壁導(dǎo)致游戲結(jié)束。此后,它會(huì)釋放蛇身的節(jié)點(diǎn),并清理分配的內(nèi)存文章來源地址http://www.zghlxwxcb.cn/news/detail-815743.html
到了這里,關(guān)于一篇文章教會(huì)你寫一個(gè)貪吃蛇小游戲(純C語言)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!