国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

一篇文章教會(huì)你寫一個(gè)貪吃蛇小游戲(純C語言)

這篇具有很好參考價(jià)值的文章主要介紹了一篇文章教會(huì)你寫一個(gè)貪吃蛇小游戲(純C語言)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

1、游戲展示

記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

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í)的魚佬
記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

這些能在控制臺(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ù)一起使用,如 WriteFileReadFile 用于實(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)體通常與 GetConsoleCursorInfoSetConsoleCursorInfo 函數(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è)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

在游戲地圖上,我們打印墻體使用寬字符:□,打印蛇使用寬字符●,打印食物使用寬字符★
普通的字符是占?個(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í)間格式化為本地化格式。
  • printfscanf 系列函數(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_MONETARYLC_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)境以支持寬字符的處理和顯示。使用 wprintfwchar_t 類型可以正確地處理和打印寬字符。

輸出結(jié)果
記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

從輸出的結(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)行修改

記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

初始化狀態(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. 游戲整體流程

記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

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);
}
  1. 設(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)題為 “貪吃蛇”。
  2. 隱藏控制臺(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)。
  3. 顯示歡迎界面
    • 調(diào)用 WelcomeToGame() 函數(shù),這個(gè)函數(shù)可能打印一些歡迎信息和游戲的介紹。
  4. 打印地圖、初始化蛇和創(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);
}
  1. 定義函數(shù) SetPos:
    • void SetPos(short x, short y):指定了該函數(shù)接受兩個(gè)參數(shù),分別是 x 和 y 坐標(biāo)。
  2. 設(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");
}
  1. 設(shè)置光標(biāo)位置并打印歡迎信息
    • SetPos(40, 15); printf("歡迎來到貪吃蛇小游戲"); 將光標(biāo)移動(dòng)到 (40, 15) 的位置,打印歡迎信息 “歡迎來到貪吃蛇小游戲”。
  2. 等待用戶按下任意鍵繼續(xù)
    • SetPos(40, 25); 設(shè)置光標(biāo)位置,這是為了讓下一次的 system("pause"); 顯示在一個(gè)美觀的位置。
    • system("pause"); 暫停程序執(zhí)行,等待用戶按下任意鍵繼續(xù)。
  3. 清屏
    • system("cls"); 清空控制臺(tái)屏幕。
  4. 打印游戲操作說明
    • SetPos(25, 12); printf("用 ↑ . ↓ . ← . → 分別控制蛇的移動(dòng), F3為加速,F(xiàn)4為減速\n"); 在指定位置打印游戲操作說明,告訴用戶如何控制蛇的移動(dòng)以及使用 F3 和 F4 來加速或減速。
    • SetPos(25, 13); printf("加速將能得到更高的分?jǐn)?shù)。\n"); 給出一些游戲策略建議。
  5. 再次等待用戶按下任意鍵繼續(xù)
    • SetPos(40, 25); 再次設(shè)置光標(biāo)位置,保證下一次的 system("pause"); 顯示在一個(gè)合適的位置。
    • system("pause"); 再次暫停程序執(zhí)行,等待用戶按下任意鍵繼續(xù)。

這個(gè)函數(shù)的目的是為了向玩家展示游戲的歡迎信息和操作說明,在游戲開始前給玩家提供一些基本的操作提示和規(guī)則。

歡迎界面

記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

提示界面

記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

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)玩家的輸入。

  1. 打印幫助信息
    • PrintHelpInfo() 被調(diào)用來顯示游戲幫助信息。
  2. 游戲循環(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)。
  3. 加速和減速
    • 如果按下 F3,游戲中蛇的移動(dòng)速度會(huì)加快,得分增加。
    • 如果按下 F4,游戲中蛇的移動(dòng)速度會(huì)減慢,得分減少。
  4. 蛇的移動(dòng)
    • 游戲根據(jù)當(dāng)前設(shè)置的移動(dòng)速度來控制蛇的移動(dòng)。
    • 使用 Sleep() 函數(shù)來控制每次蛇移動(dòng)之間的時(shí)間間隔。
    • 調(diào)用 SnakeMove() 函數(shù)來處理蛇的移動(dòng)邏輯。
  5. 循環(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í)的魚佬");
}

記事本寫一個(gè)貪吃蛇游戲,C語言進(jìn)階,數(shù)據(jù)結(jié)構(gòu),c語言

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);
}
  1. 創(chuàng)建下一個(gè)節(jié)點(diǎn):利用 malloc() 分配內(nèi)存來創(chuàng)建一個(gè)新的蛇身節(jié)點(diǎn) pNextNode
  2. 根據(jù)方向確定下一個(gè)節(jié)點(diǎn)的坐標(biāo):根據(jù)蛇頭當(dāng)前的方向,計(jì)算出蛇頭下一個(gè)移動(dòng)的位置。
  3. 檢查下一個(gè)位置是否是食物
    • 如果下一個(gè)位置是食物,調(diào)用 EatFood(pNextNode, ps) 函數(shù)來處理吃食物的邏輯。
    • 如果不是食物,調(diào)用 NoFood(pNextNode, ps) 函數(shù)來處理不吃食物的邏輯。
  4. 檢查游戲結(jié)束的條件
    • 調(diào)用 KillByWall(ps)KillBySelf(ps) 函數(shù)來檢查是否撞墻或者咬到自己,如果游戲結(jié)束,則會(huì)設(shè)定相應(yīng)的游戲狀態(tài)。

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;
}
  1. NextIsFood:該函數(shù)檢查下一個(gè)位置是否有食物。如果坐標(biāo)與食物位置匹配,返回真;否則返回假。
  2. EatFood:當(dāng)下一個(gè)位置有食物時(shí)調(diào)用此函數(shù)。它采用鏈表方法,在蛇的開頭插入表示下一個(gè)位置的新節(jié)點(diǎn),從而延長蛇的身體。它更新蛇的分?jǐn)?shù),移除被吃掉的食物(通過釋放內(nèi)存),然后創(chuàng)建新的食物以繼續(xù)游戲。
  3. 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;
}
  1. KillByWall 檢查蛇頭是否碰到了游戲地圖的邊界。如果蛇頭在地圖的邊緣,函數(shù)將設(shè)置蛇的狀態(tài)為 KILL_BY_WALL 并返回 1(表示蛇碰到了墻壁),否則返回 0。
  2. 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í)行

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)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • docker從安裝到部署項(xiàng)目,一篇文章教會(huì)你

    docker從安裝到部署項(xiàng)目,一篇文章教會(huì)你

    首先看下 Docker 圖標(biāo): 一條小鯨魚上面有些集裝箱,比較形象的說明了 Docker 的特點(diǎn),以后見到這個(gè)圖標(biāo)等同見到了 Docker 1. Docker 是一個(gè)開源的應(yīng)用容器引擎,它基于 Go 語言開發(fā),并遵從 Apache2.0 開源協(xié)議 2. 使用 Docker 可以讓開發(fā)者封裝他們的應(yīng)用以及依賴包到一個(gè)可移植的

    2024年02月08日
    瀏覽(25)
  • C++初階之一篇文章教會(huì)你list(模擬實(shí)現(xiàn))

    C++初階之一篇文章教會(huì)你list(模擬實(shí)現(xiàn))

    成員類型表 這個(gè)表中列出了C++標(biāo)準(zhǔn)庫中l(wèi)ist容器的一些成員類型定義。這些類型定義是為了使list能夠與C++標(biāo)準(zhǔn)庫的其他組件協(xié)同工作,并提供一些通用的標(biāo)準(zhǔn)接口。每個(gè)成員類型的用處: value_type : 這個(gè)成員類型代表list容器中存儲(chǔ)的數(shù)據(jù)類型,即模板參數(shù)T的類型。 allocator_

    2024年02月12日
    瀏覽(26)
  • C++初階之一篇文章教會(huì)你list(理解和使用)

    C++初階之一篇文章教會(huì)你list(理解和使用)

    在C++標(biāo)準(zhǔn)庫中, std::list 是一個(gè)雙向鏈表容器,用于存儲(chǔ)一系列元素。與 std::vector 和 std::deque 等容器不同, std::list 使用鏈表的數(shù)據(jù)結(jié)構(gòu)來組織元素,因此在某些操作上具有獨(dú)特的優(yōu)勢和性能特點(diǎn)。以下是關(guān)于 std::list 的詳細(xì)介紹: 雙向鏈表結(jié)構(gòu): std::list 內(nèi)部使用雙向鏈表來

    2024年02月13日
    瀏覽(29)
  • Vue中的Pinia狀態(tài)管理工具 | 一篇文章教會(huì)你全部使用細(xì)節(jié)

    Vue中的Pinia狀態(tài)管理工具 | 一篇文章教會(huì)你全部使用細(xì)節(jié)

    Pinia(發(fā)音為/pi?nj?/,如英語中的“peenya”)是最接近pi?a(西班牙語中的菠蘿)的詞 ; Pinia開始于大概2019年,最初是 作為一個(gè)實(shí)驗(yàn)為Vue重新設(shè)計(jì)狀態(tài)管理 ,讓它用起來適合組合式API(Composition API)。 從那時(shí)到現(xiàn)在,最初的設(shè)計(jì)原則依然是相同的,并且目前同時(shí)兼容Vue2、

    2024年02月11日
    瀏覽(23)
  • 數(shù)據(jù)結(jié)構(gòu)入門(C語言版)一篇文章教會(huì)你手撕八大排序

    數(shù)據(jù)結(jié)構(gòu)入門(C語言版)一篇文章教會(huì)你手撕八大排序

    排序 :所謂排序,就是使一串記錄,按照其中的某個(gè)或某些的大小,遞增或遞減的排列起來的操作。 穩(wěn)定性 :假定在待排序的記錄序列中,存在多個(gè)具有相同的的記錄,若經(jīng)過排序,這些記錄的相對次序保持不變,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而

    2024年02月01日
    瀏覽(23)
  • 如何用MetaGPT幫你寫一個(gè)貪吃蛇的小游戲項(xiàng)目

    如何用MetaGPT幫你寫一個(gè)貪吃蛇的小游戲項(xiàng)目

    MetaGPT是基于大型語言模型(LLMs)的多智能體寫作框架,目前在Github開源,其Start數(shù)量也是比較高的,是一款非常不錯(cuò)的開源框架。 下面將帶你進(jìn)入MetaGPT的大門,開啟MetaGPT的體驗(yàn)之旅。首先是入門教程,如何安裝及使用MetaGPT。 一、環(huán)境安裝 python 環(huán)境安裝,MetaGPT所需的Python環(huán)

    2024年01月19日
    瀏覽(31)
  • 怎樣在一臺(tái)電腦安裝多個(gè)版本的JDK并切換使用?一篇文章教會(huì)你所有細(xì)節(jié)

    怎樣在一臺(tái)電腦安裝多個(gè)版本的JDK并切換使用?一篇文章教會(huì)你所有細(xì)節(jié)

    目錄 1. 下載安裝JDK版本 2. 配置環(huán)境變量 2. 1 配置環(huán)境變量的步驟 2.2 需要注意的細(xì)節(jié)點(diǎn) 2.3 JDK8,11,17版本切換測試 a . JDK8 下載鏈接: Java Downloads | Oracle https://www.oracle.com/java/technologies/downloads/#java8-windows b.? 這里我先插一句,因?yàn)槲覀円惭b多個(gè)JDK版本,所以我們最好提前創(chuàng)

    2024年04月16日
    瀏覽(20)
  • (python實(shí)現(xiàn))一篇文章教會(huì)你k-means聚類算法(包括最優(yōu)聚類數(shù)目k的確定)

    (python實(shí)現(xiàn))一篇文章教會(huì)你k-means聚類算法(包括最優(yōu)聚類數(shù)目k的確定)

    Kmeans算法中,K值所決定的是在該聚類算法中,所要分配聚類的簇的多少。Kmeans算法對初始值是?較敏感的,對于同樣的k值,選取的點(diǎn)不同,會(huì)影響算法的聚類效果和迭代的次數(shù)。本文通過計(jì)算原始數(shù)據(jù)中的:手肘法、輪廓系數(shù)、CH值和DB值,四種指標(biāo)來衡量K-means的最佳聚類數(shù)

    2024年02月05日
    瀏覽(40)
  • RabbitMQ篇——一篇文章帶你入門RabbitMQ,了解RabbitMQ的角色分類權(quán)限、AMQP協(xié)議以及設(shè)計(jì)第一個(gè)RabbitMQ程序!

    RabbitMQ篇——一篇文章帶你入門RabbitMQ,了解RabbitMQ的角色分類權(quán)限、AMQP協(xié)議以及設(shè)計(jì)第一個(gè)RabbitMQ程序!

    RabbitMQ是一個(gè)開源的消息代理和消息隊(duì)列系統(tǒng),采用AMQP(Advanced Message Queuing Protocol)協(xié)議。它被設(shè)計(jì)用于在分布式系統(tǒng)中進(jìn)行高效,可靠和可擴(kuò)展的消息傳遞。 RabbitMQ基本概念: Producer(生產(chǎn)者):生產(chǎn)者負(fù)責(zé)發(fā)布消息到消息隊(duì)列中。 Consumer(消費(fèi)者):消費(fèi)者從消息隊(duì)列中

    2024年01月16日
    瀏覽(43)
  • Go For Web:一篇文章帶你用 Go 搭建一個(gè)最簡單的 Web 服務(wù)、了解 Golang 運(yùn)行 web 的原理

    Go For Web:一篇文章帶你用 Go 搭建一個(gè)最簡單的 Web 服務(wù)、了解 Golang 運(yùn)行 web 的原理

    本文作為解決如何通過 Golang 來編寫 Web 應(yīng)用這個(gè)問題的前瞻,對 Golang 中的 Web 基礎(chǔ)部分進(jìn)行一個(gè)簡單的介紹。目前 Go 擁有成熟的 Http 處理包,所以我們?nèi)ゾ帉懸粋€(gè)做任何事情的動(dòng)態(tài) Web 程序應(yīng)該是很輕松的,接下來我們就去學(xué)習(xí)了解一些關(guān)于 Web 的相關(guān)基礎(chǔ),了解一些概念,

    2023年04月14日
    瀏覽(28)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包