制作不易,點(diǎn)贊關(guān)注一下唄?。?!
文章目錄
- 前言
- 一.?技術(shù)要點(diǎn)
- 二、WIN32API介紹
- 三、貪吃蛇游戲設(shè)計(jì)與分析?
- ? ? ??1.游戲開(kāi)始前的初始化
- ? ? ??2.游戲運(yùn)行的邏輯?
- 總結(jié)
前言
當(dāng)我們掌握鏈表這樣的數(shù)據(jù)結(jié)構(gòu)之后,我們就可以用它來(lái)做一些小項(xiàng)目,比如童年小游戲貪吃蛇
目標(biāo):在Windows環(huán)境中控制臺(tái)上使用C語(yǔ)言模擬實(shí)現(xiàn)經(jīng)典小游戲貪吃蛇
一.技術(shù)要點(diǎn)
C語(yǔ)言函數(shù),枚舉,結(jié)構(gòu)體,動(dòng)態(tài)內(nèi)存管理,預(yù)處理指令,鏈表,Win32API等。
二.Win32API介紹
1.什么是Win32API
Win32 API是微軟的操作系統(tǒng)Windows提供給開(kāi)發(fā)人員的編程接口,它決定了我們開(kāi)發(fā)的Windows應(yīng)用程序的能力。
使用Win32 API,應(yīng)用程序可以充分挖掘Windows的32位操作系統(tǒng)的潛力。 Mircrosoft的所有32位平臺(tái)都支持統(tǒng)一的API,包括函數(shù)、結(jié)構(gòu)、消息、宏及接口。使用 Win32 API不但可以開(kāi)發(fā)出在各種平臺(tái)上都能成功運(yùn)行的應(yīng)用程序,而且也可以充分利用每個(gè)平臺(tái)特有的功能和屬性。
2.控制臺(tái)程序(Console)
我們平時(shí)運(yùn)行起來(lái)出現(xiàn)的黑框框就是控制臺(tái)。
我們可以通過(guò)mode命令來(lái)設(shè)置窗口大小。
例如:設(shè)置為30行,100列
mode con cols=100 lines=30?
在VS上我們可以借助system函數(shù)(頭文件<windows.h>)來(lái)完成這樣的命令?
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-830396.html
?我們也可以通過(guò)title命令來(lái)修改控制臺(tái)窗口的名稱
title 貪吃蛇
?
3.控制臺(tái)屏幕上的坐標(biāo)?COORD
COORD是Win32API中定義的一個(gè)結(jié)構(gòu)體,表示一個(gè)字符在屏幕緩沖區(qū)上的坐標(biāo),坐標(biāo)為(0,0)的原點(diǎn)位于緩沖區(qū)的頂部最左側(cè)單元格
typedef struct _COORD {
SHORT X; // horizontal coordinate
SHORT Y; // vertical coordinate
} COORD;
4.GetStdHandle?
GetStdHandle 是Win32API的一個(gè)函數(shù),它用于從特定的標(biāo)準(zhǔn)設(shè)備(標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,標(biāo)準(zhǔn)錯(cuò)誤)中獲取一個(gè)句柄(用于標(biāo)識(shí)不同設(shè)備的數(shù)值),使用這個(gè)句柄可以操作設(shè)備。
語(yǔ)法
HANDLE?GetStdHandle(?DWORD?nStdHandle );
?參數(shù)有三種取值
STD_INPUT_HANDLE
標(biāo)準(zhǔn)輸入的句柄
STD_OUTPUT_HANDLE
標(biāo)準(zhǔn)輸出的句柄
STD_ERROR_HANDLE
標(biāo)準(zhǔn)錯(cuò)誤的句柄
實(shí)例:
?
?
5.GetConsoleCursorInfo
功能:檢索有關(guān)指定控制臺(tái)光標(biāo)的大小和可見(jiàn)性
?BOOL WINAPI GetConsoleCursorInfo(
? ?HANDLE ??????????????hConsoleOutput,
? ?PCONSOLE_CURSOR_INFO? lpConsoleCursorInfo
);
??PCONSOLE_CURSOR_INFO是指向?CONSOLE_CURSOR_INFO這個(gè)結(jié)構(gòu)體的指針,該結(jié)構(gòu)接收主機(jī)光標(biāo)的信息。
?
6.SetConsoleCursorInfo?
功能:設(shè)置指定控制臺(tái)屏幕緩沖區(qū)的光標(biāo)大小和可見(jiàn)性
BOOL WINAPI SetConsoleCursorInfo(
? _In_???????HANDLE ?????????????hConsoleOutput,
? _In_?const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);
一般都是配合?GetConsoleCursorInfo函數(shù)使用,先用GetConsoleCursorInfo函數(shù)獲取當(dāng)前光標(biāo)信息,再自己手動(dòng)修改光標(biāo)信息,最后用SetConsoleCursorInfo 函數(shù)設(shè)置進(jìn)去?。?!
?
?7.GetAsyncKeyState?
GetAsyncKeyState是一個(gè)用來(lái)判斷函數(shù)調(diào)用時(shí)指定虛擬鍵的狀態(tài),確定用戶當(dāng)前是否按下了鍵盤上的一個(gè)鍵的函數(shù)。
返回類型為short類型,如果返回的16位short類型數(shù)據(jù)中最高位為1,則表明該鍵被按下,最高位為0則表示該鍵抬起,如果最低位為1表示該鍵被按過(guò),否則為0.
函數(shù)名 返回值類型 備注 GetAsyncKeyState SHORT 用來(lái)判斷函數(shù)調(diào)用時(shí)指定虛擬鍵的狀態(tài) 參數(shù) 類型 說(shuō)明 vKey int 欲測(cè)試的虛擬鍵的鍵碼
每個(gè)鍵都對(duì)應(yīng)一個(gè)虛擬鍵值,可以自行查找。
8.SetConsoleCursorPosition
有時(shí)我們可能不想讓字符打印從(0,0)坐標(biāo)處開(kāi)始打印,這時(shí)就需要用這個(gè)函數(shù)來(lái)設(shè)置坐標(biāo)
使用這個(gè)函數(shù)需要兩個(gè)參數(shù):第一個(gè)參數(shù)類型為HANDLE,第二個(gè)參數(shù)類型為COORD
實(shí)例:
?
三.貪吃蛇游戲設(shè)計(jì)與分析?
1.游戲開(kāi)始前的初始化
數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì):
enum GameStatus
{
OK,
ESC,
KILL_BYWALL,
KILL_BYSELF,
};
enum DIRECTION
{
LEFT,
RIGHT,
UP,
DOWN,
};
//蛇身節(jié)點(diǎn)
typedef struct SnakeNode
{
int x;
int y;
struct SnakeNode* next;
}SnakeNode,*pSnakeNode;
//整條蛇
typedef struct Snake
{
pSnakeNode pSnake;
pSnakeNode pFood;//指向食物的指針
int Score;//當(dāng)前累計(jì)分?jǐn)?shù)
int FoodWeight;//一個(gè)食物的分?jǐn)?shù)
int SleepTime;//休眠時(shí)間,時(shí)間越短,速度越快
enum GameStatus Status;
enum DIRECTION dir;
}Snake,*pSnake;
?
?
接下來(lái)打印歡迎信息 ,這里我們單獨(dú)分裝兩個(gè)函數(shù),一個(gè)用來(lái)設(shè)置光標(biāo)位置,另一個(gè)用來(lái)打印歡迎信息:
?
下一步:創(chuàng)建地圖
#define WALL L'□'
void CreateMap()
{
//上
SetPos(0, 0);
int i = 0;
for (i = 0; i < 69; i += 2)
{
wprintf(L"%lc", WALL);
}
//下
SetPos(0,34);
for (i = 0; i < 69; i += 2)
{
wprintf(L"%lc", WALL);
}
//左
for (i = 0; i < 34; i++)
{
SetPos(0, i);
wprintf(L"%lc", WALL);
}
//右
for (i = 0; i < 34; i++)
{
SetPos(68, i);
wprintf(L"%lc", WALL);
}
}
?
再下一步:初始化蛇
void InitSnake(pSnake ps)
{
pSnakeNode cur = NULL;
//創(chuàng)建5個(gè)蛇身節(jié)點(diǎn)
for (int i = 0; i < 5; i++)
{
cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("createsnake malloc");
exit(1);
}
cur->x = 32 + 2 * i;
cur->y = 7;
cur->next = NULL;
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"%lc", BODY);
cur = cur->next;
}
//貪吃蛇其他信息初始化
ps->dir = RIGHT;
ps->FoodWeight = 10;
ps->pFood = NULL;
ps->Score = 0;
ps->SleepTime = 200;//200毫秒
ps->Status = OK;
}
最后:創(chuàng)建食物?
void CreateFood(pSnake ps)
{
int x = 0;
int y = 0;
again:
do {
x = rand() % 66 + 2;
y = rand() % 33 + 1;
} while (x % 2 != 0);
pSnakeNode cur = ps->pSnake;
while (cur)
{
if (x == cur->x && y == cur->y)
{
goto again;
}
cur = cur->next;
}
pSnakeNode food = (pSnakeNode)malloc(sizeof(pSnake));
if (!food)
{
perror("createfood malloc ");
exit(1);
}
food->x = x;
food->y = y;
ps->pFood = food;
SetPos(x, y);
wprintf(L"%lc", FOOD);
}
?
最后將這些函數(shù)放在GameStart函數(shù)中,一起完成游戲開(kāi)始前的工作?
?
2.游戲運(yùn)行的邏輯?
第一步:打印幫助信息
?
?第二步:循環(huán)檢測(cè)按鍵狀態(tài)
?
?
3.游戲結(jié)束的善后?
?
總結(jié)
所有代碼放在這里:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-830396.html
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<locale.h>
#include<string.h>
#include<stdbool.h>
#include<windows.h>
#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'
#define KEY_PRESS(vk) ((GetAsyncKeyState(vk) & 0x1)? 1: 0)
enum GameStatus
{
OK,
ESC,
KILL_BYWALL,
KILL_BYSELF,
};
enum DIRECTION
{
LEFT,
RIGHT,
UP,
DOWN,
};
//蛇身節(jié)點(diǎn)
typedef struct SnakeNode
{
int x;
int y;
struct SnakeNode* next;
}SnakeNode,*pSnakeNode;
//整條蛇
typedef struct Snake
{
pSnakeNode pSnake;
pSnakeNode pFood;//指向食物的指針
int Score;//當(dāng)前累計(jì)分?jǐn)?shù)
int FoodWeight;//一個(gè)食物的分?jǐn)?shù)
int SleepTime;//休眠時(shí)間,時(shí)間越短,速度越快
enum GameStatus Status;
enum DIRECTION dir;
}Snake,*pSnake;
void SetPos(int x, int y);
void GameStart(pSnake snake);
void Welcome();
void CreateMap();
void InitSnake(pSnake ps);
void CreateFood(pSnake ps);
void printhelpinfo();
//游戲運(yùn)行的邏輯
void GameRun(pSnake ps);
void pause();
void SnakeMove(pSnake ps);
int IsFood(pSnake ps, pSnakeNode next);
void EatFood(pSnake ps, pSnakeNode next);
void NotEatFood(pSnake ps,pSnakeNode next);
void KillByWall(pSnake ps);
void KillBySelf(pSnake ps);
void GameEnd(pSnake ps);
#include"snake.h"
void SetPos(int x, int y)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(handle, pos);
}
void Welcome()
{
//打印歡迎信息
SetPos(48, 15);
printf("歡迎來(lái)到貪吃蛇小游戲\n");
SetPos(50,35);
system("pause");
system("cls");
//打印操作信息
SetPos(35, 20);
printf("用↑. ↓. ←. →來(lái)控制蛇的移動(dòng),F3是加速,F(xiàn)4是減速\n");
SetPos(48, 22);
printf("加速能獲得更多分?jǐn)?shù)\n");
SetPos(50, 35);
system("pause");
system("cls");
}
void CreateMap()
{
//上
SetPos(0, 0);
int i = 0;
for (i = 0; i < 69; i += 2)
{
wprintf(L"%lc", WALL);
}
//下
SetPos(0,34);
for (i = 0; i < 69; i += 2)
{
wprintf(L"%lc", WALL);
}
//左
for (i = 0; i < 34; i++)
{
SetPos(0, i);
wprintf(L"%lc", WALL);
}
//右
for (i = 0; i < 34; i++)
{
SetPos(68, i);
wprintf(L"%lc", WALL);
}
}
void InitSnake(pSnake ps)
{
pSnakeNode cur = NULL;
//創(chuàng)建5個(gè)蛇身節(jié)點(diǎn)
for (int i = 0; i < 5; i++)
{
cur = (pSnakeNode)malloc(sizeof(SnakeNode));
if (cur == NULL)
{
perror("createsnake malloc");
exit(1);
}
cur->x = 32 + 2 * i;
cur->y = 7;
cur->next = NULL;
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"%lc", BODY);
cur = cur->next;
}
//貪吃蛇其他信息初始化
ps->dir = RIGHT;
ps->FoodWeight = 10;
ps->pFood = NULL;
ps->Score = 0;
ps->SleepTime = 200;//200毫秒
ps->Status = OK;
}
void CreateFood(pSnake ps)
{
int x = 0;
int y = 0;
again:
do {
x = rand() % 66 + 2;
y = rand() % 33 + 1;
} while (x % 2 != 0);
pSnakeNode cur = ps->pSnake;
while (cur)
{
if (x == cur->x && y == cur->y)
{
goto again;
}
cur = cur->next;
}
pSnakeNode food = (pSnakeNode)malloc(sizeof(pSnake));
if (!food)
{
perror("createfood malloc ");
exit(1);
}
food->x = x;
food->y = y;
ps->pFood = food;
SetPos(x, y);
wprintf(L"%lc", FOOD);
}
void GameStart(pSnake ps)
{
//修改控制臺(tái)信息
system("mode con cols=120 lines=40");
system("title 貪吃蛇");
//隱藏光標(biāo)
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cursorinfo;
GetConsoleCursorInfo(handle, &cursorinfo);
cursorinfo.bVisible = false;
SetConsoleCursorInfo(handle, &cursorinfo);
//打印歡迎信息
Welcome();
//繪制地圖
CreateMap();
//初始化蛇
InitSnake(ps);
//創(chuàng)建食物
CreateFood(ps);
}
void printhelpinfo()
{
SetPos(80, 18);
printf("不能穿墻,不能咬到自己\n");
SetPos(80, 19);
printf("用↑ . ↓ . ← . →來(lái)控制蛇的移動(dòng)\n");
SetPos(80, 20);
printf("F3是加速,F(xiàn)4是減速\n");
}
void pause()
{
while (1)
{
Sleep(100);
if (KEY_PRESS(VK_SPACE))
{
break;
}
}
}
int IsFood(pSnake ps,pSnakeNode next)
{
if (ps->pFood->x == next->x && ps->pFood->y == next->y)
{
return 1;//是食物
}
else
return 0;
}
void EatFood(pSnake ps, pSnakeNode next)
{
next->next = ps->pSnake;
ps->pSnake = next;
//打印蛇身
pSnakeNode cur = ps->pSnake;
while (cur)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
ps->Score += ps->FoodWeight;
//釋放舊食物
free(ps->pFood);
ps->pFood = NULL;
//創(chuàng)建新食物
CreateFood(ps);
}
void NotEatFood(pSnake ps, pSnakeNode next)
{
//頭插法插入
next->next = ps->pSnake;
ps->pSnake = next;
//釋放尾節(jié)點(diǎn)
pSnakeNode cur = ps->pSnake;
while (cur->next->next)
{
SetPos(cur->x, cur->y);
wprintf(L"%lc", BODY);
cur = cur->next;
}
//將尾節(jié)點(diǎn)的位置打印成空白字符
SetPos(cur->next->x, cur->next->y);
printf(" ");
free(cur->next);
cur->next = NULL;
}
void SnakeMove(pSnake ps)
{
pSnakeNode pnext = (pSnakeNode)malloc(sizeof(SnakeNode));
if (pnext == NULL)
{
perror("snakemove malloc");
exit(1);
}
pnext->next = NULL;
switch (ps->dir)
{
case UP:
pnext->x = ps->pSnake->x;
pnext->y = ps->pSnake->y-1;
break;
case DOWN:
pnext->x = ps->pSnake->x;
pnext->y = ps->pSnake->y + 1;
break;
case LEFT:
pnext->x = ps->pSnake->x-2;
pnext->y = ps->pSnake->y;
break;
case RIGHT :
pnext->x = ps->pSnake->x+2;
pnext->y = ps->pSnake->y;
break;
}
//下一個(gè)坐標(biāo)是否是食物
if (IsFood(ps, pnext))
{
//是食物
EatFood(ps, pnext);
}
else
{
//不是食物
NotEatFood(ps,pnext);
}
}
void KillByWall(pSnake ps)
{
if (ps->pSnake->x == 0 || ps->pSnake->x == 68 || ps->pSnake->y == 0 || ps->pSnake->y == 33)
{
ps->Status = KILL_BYWALL;
}
}
void KillBySelf(pSnake ps)
{
pSnakeNode cur = ps->pSnake->next;
while (cur)
{
if (cur->x == ps->pSnake->x && cur->y == ps->pSnake->y)
{
ps->Status = KILL_BYSELF;
return;
}
cur=cur->next;
}
}
//游戲運(yùn)行的邏輯
void GameRun(pSnake ps)
{
//打印幫助信息
printhelpinfo();
do
{
//打印當(dāng)前分?jǐn)?shù)
SetPos(80, 13);
printf("總分:%d\n", ps->Score);
SetPos(80, 14);
printf("食物的分值:%02d\n", ps->FoodWeight);
//檢測(cè)按鍵
//上下左右,空格,F(xiàn)3,F(xiàn)4, ESC
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_ESCAPE))
{
ps->Status = ESC;
break;
}
else if (KEY_PRESS(VK_SPACE))
{
//暫停和恢復(fù)暫停
pause();
}
else if (KEY_PRESS(VK_F3))
{
if (ps->SleepTime>=80)
{
ps->SleepTime -= 30;
ps->FoodWeight += 2;
}
}
else if (KEY_PRESS(VK_F4))
{
if (ps->FoodWeight > 2)
{
ps->SleepTime += 30;
ps->FoodWeight -= 2;
}
}
//睡眠
Sleep(ps->SleepTime);
//走一步
SnakeMove(ps);
//檢測(cè)撞墻
KillByWall(ps);
//檢測(cè)是否撞到自己
KillBySelf(ps);
} while (ps->Status == OK);
}
void GameEnd(pSnake ps)
{
SetPos(35, 18);
switch (ps->Status)
{
case ESC:
printf("正常退出\n");
break;
case KILL_BYWALL:
printf("很遺憾,撞到墻,游戲結(jié)束\n");
break;
case KILL_BYSELF:
printf("很遺憾,咬到自己了,游戲結(jié)束\n");
break;
}
//釋放貪吃蛇
pSnakeNode cur = ps->pSnake;
while (cur)
{
pSnakeNode next = cur->next;
free(cur);
cur = next;
}
free(ps->pFood);
ps = NULL;
}
void test()
{
int ch = 0;
do
{
Snake snake = { 0 };
GameStart(&snake);
GameRun(&snake);
GameEnd(&snake);
SetPos(35, 18);
printf("再來(lái)一局嗎:Y/N");
ch=getchar();
getchar();
} while (ch == 'y' || ch == 'Y');
}
int main()
{
setlocale(LC_ALL, "");
srand((unsigned int)time(NULL));
test();
SetPos(55, 35);
return 0;
}
到了這里,關(guān)于C語(yǔ)言從零實(shí)現(xiàn)貪吃蛇小游戲的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!