基于Ncueses庫的貪吃蛇小游戲
1、項目簡介
-
視頻演示:
https://www.bilibili.com/video/BV1pt421a7Nu/?spm_id_from=333.999.0.0&vd_source=82b65865be0947de29bd55efc8cdb40a
-
編譯環(huán)境:linux(Vmware16.2.4 + Ubantu20.04.3);
-
小蛇??只能在固定的范圍內(nèi)活動;
-
可以利用鍵盤方向鍵控制小蛇??的前進(jìn)方向;
-
活動范圍內(nèi)會隨機(jī)生成食物;
-
小蛇??吃到食物,身體將會變長;
-
小蛇??咬到自己或者碰到墻壁將會死亡,重新開始游戲;
完整代碼文末奉上,歡迎大家一起交流談?wù)摚?/p>
2、項目演示&整體框架
-
-
框架
3、項目介紹
為了方便理解,我將從
main()
函數(shù)程序的順序開始講起,慢慢將各各函數(shù)擴(kuò)展開;
-
main()
函數(shù)主要分為4塊-
initNcuse()
//初始化ncuses,頁面以及按鍵; -
initSnake()
//小蛇??的初始化和食物??的初始化 -
gamePic()
//初始化地形、遍歷各各點、打印邊界、小蛇?? 和食物?? -
pthread兩個進(jìn)程
-
refreshJieMian()
//不斷刷新頁面 -
changeDir()
//不斷的檢測按鍵
-
- else
-
3.1、頭文件&宏定義&全局變量
- 本項目所需的頭文件、宏定義和全局變量程序如下
//頭文件&宏定義&全局變量 #include<curses.h> //圖形庫 #include<stdlib.h> #include<unistd.h> //sheep(1) #include<pthread.h> //多線程的頭文件 /* #define UP 1 #define DOWN 2 #define LEFT 3 #define RIGHT 4 *///利用正負(fù)的差別,方便為后面控制蛇,不能往倒著走 #define UP 1 #define DOWN -1 #define LEFT 2 #define RIGHT -2 struct Snake { int hang; int lie; struct Snake *next; }; int key; int dir; struct Snake *head = NULL; struct Snake *tail = NULL; struct Snake food;
3.2、初始化ncuses,頁面以及按鍵initNcuse()
- 分為兩步,均是ncurse庫的函數(shù)
- 第一:初始化ncurse頁面
- 第二:允許按鍵的使用
-
initNcuse()
void initNcuse() { initscr(); //初始化ncurse頁面 keypad(stdscr,1);//允許使用功能鍵 // noecho();//不把無關(guān)的信息,功能類的信息顯示在頁面上 }
3.3、小蛇??的初始化和食物??的初始化initSnake()
- 分為5步
- 第一:初始化小蛇的運動方向;
- 第二:清空節(jié)點,僅留一個頭;
- 第三:隨機(jī)生成食物;
- 第四:設(shè)置小蛇起始位置;
- 第五:初始化小蛇長度;
-
initSnake()
void initFood() //隨機(jī)生成食物//子函數(shù) { int x = rand()%20; int y = rand()%20; food.hang = x; food.lie = y; } void addNode() //根據(jù)方向添加節(jié)點,一節(jié)??//子函數(shù) { struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake)); new->next = NULL; switch(dir) { case UP: new->hang = tail->hang-1; new->lie = tail->lie; break; case DOWN: new->hang = tail->hang+1; new->lie = tail->lie; break; case LEFT: new->hang = tail->hang; new->lie = tail->lie-1; break; case RIGHT: new->hang = tail->hang; new->lie = tail->lie+1; break; } tail->next = new; tail = new; } void initSnake()//初始化 { struct Snake *p; dir = RIGHT;//剛開始的方向是向右邊 while(head!=NULL) { p = head; //head向下一個結(jié)點移一下,直到最后一個,將所有結(jié)點清空,只留一個 head=NULL head = head->next; free(p); } initFood();//隨機(jī)生成食物 head = (struct Snake*)malloc(sizeof(struct Snake)); head->hang = 1; //??的起始位子 head->lie = 1; head->next = NULL; tail = head; addNode(); addNode(); addNode(); addNode(); }
3.4、初始化地形、遍歷各各點、打印邊界、小蛇?? 和食物??gamePic()
- 分為三步:
- 第一:初始化光標(biāo);
- 第二:遍歷各點,打印所需;
- 第三:打印作者及食物位置;
-
gamePic()
int hasSnakeNode(int i,int j) //這個點有??嗎?有返回1, 沒有返回0 { struct Snake *p; p = head; while(p != NULL) //遍歷??的身子,是不是這個點 { if(p->hang==i && p->lie==j) { return 1; } p = p->next; } return 0; } int hasFood(int i,int j)//這個點是不是食物所在的點,有的返回1; { if(food.hang == i && food.lie == j)//吃到了返回1 { return 1; } return 0;//沒吃到,返回0 } void gamePic() { int hang,lie; move(0,0);//將光標(biāo)設(shè)定在(0,0) for(hang=0;hang<20;hang++) { if(hang==0)//第一行,打印“------” { for(lie=0;lie<20;lie++) { printw("--"); } } if(hang>0 && hang<=19) //打印左右界限 { printw("\n"); for(lie=0;lie<=20;lie++) { if(lie==0 || lie==20) { printw("|"); } else if(hasSnakeNode(hang,lie))//??的位置是不是這個點,打印身子 { printw("[]"); } else if(hasFood(hang,lie))//打印食物的位置 { printw("##"); } else { printw(" "); } } } if(hang == 19)//最后一行,打印邊界“------” { printw("\n"); for(lie=0;lie<20;lie++) { printw("--"); } } } printw("\nBy TP, food.hang=%d,food.lie=%d\n\n",food.hang,food.lie); }
3.5 兩個進(jìn)程pthread
-
refreshJieMian()
//不斷刷新頁面-
moveSnake()
- 添加蛇頭節(jié)點;
-
hasFood()
//有沒有吃到食物 -
ifSnakeDie
//小蛇的死亡條件
-
gamePic()
//同3.4; -
refresh()
//ncurses庫的文件內(nèi)置函數(shù),只有某次緩存與上次不一樣,才輸出緩沖區(qū)的值; -
usleep()
//讓該進(jìn)程休眠指定us;
-
-
changeDir()
//不斷的檢測按鍵-
getch()
//獲取鍵盤輸入; - 根據(jù)鍵盤輸入,改變方向;
-
-
其他
-
while(1)
//鎖定程序,別退了; -
getch()
//等待鍵盤輸入不讓程序推出; -
endwin()
//恢復(fù)終端
-
-
pthread
文章來源:http://www.zghlxwxcb.cn/news/detail-832938.htmlvoid deleNode()//刪除??尾巴的節(jié)點,動起來 { struct Snake *p; p =head; head = head->next; free(p); } void moveSnake() //吃到了食物與沒吃到食物,蛇的長度和位置的變化;蛇死了怎么辦; { addNode(); //添加節(jié)點 if(hasFood(tail->hang,tail->lie)) // 尾結(jié)點(頭),是不是食物所在的點 { initFood(); // 是--重新創(chuàng)建食物--且不刪除head(蛇尾) 結(jié)點 } else { deleNode();//刪除 } if(ifSnakeDie()) //如果??死了 { refresh();//Curses庫,屏幕刷新 usleep(100000);//暫停100000us initSnake();//小蛇??初始化和食物??初始化 } } int ifSnakeDie()//??的死亡條件 { struct Snake *p; p = head; if(tail->hang==0 || tail->lie==0 || tail->hang==20 || tail->lie==20)//撞墻 { return 1; } while(p->next != NULL)//從到了自己嗎 { if(p->hang == tail->hang && p->lie == tail->lie) { return 1; } p = p->next; } return 0; } void turn(int direction)//防止??上下往回走,左右走; { if(abs(dir) != abs(direction))//絕對值不一樣,才可以轉(zhuǎn)變方向 { dir = direction; } } void *refreshJieMian()//進(jìn)程1 刷新界面 { while(1) { moveSnake(); //前進(jìn)一格, gamePic(); refresh(); usleep(100000); //0.1s,通過多線程,刷新界面,也起到了調(diào)速的作用; } } void *changeDir() //進(jìn)程2 通過按鍵改方向 { while(1) { key = getch(); switch(key) { case KEY_DOWN: turn(DOWN); break; case KEY_UP: turn(UP); break; case KEY_LEFT: turn(LEFT); break; case KEY_RIGHT: turn(RIGHT); break; } } } *///將這些放在main函數(shù)里面 pthread_t t1; //定義進(jìn)程 1 pthread_t t2; //定義進(jìn)程 2 pthread_create(&t1,NULL,refreshJieMian,NULL);//不斷的刷新界面 pthread_create(&t2,NULL,changeDir,NULL); //不斷的檢測按鍵輸入 while(1); getch(); //等待鍵盤輸入,不讓程序退出 endwin(); //恢復(fù)終端 */
3.6、main()
int main()
{
pthread_t t1; //定義進(jìn)程 1
pthread_t t2; //定義進(jìn)程 2
initNcuse(); //初始化ncuses,頁面以及按鍵
initSnake(); //??的初始化和食物
gamePic(); //初始化地形,遍歷各各點,打印邊界、??、和食物
pthread_create(&t1,NULL,refreshJieMian,NULL);//不斷的刷新界面
pthread_create(&t2,NULL,changeDir,NULL); //不斷的檢測按鍵輸入
while(1);
getch(); //等待鍵盤輸入,不讓程序退出
endwin(); //恢復(fù)終端
return 0;
}
4、代碼整合
- 283行
//snake.c
#include<curses.h> //圖形庫
#include<stdlib.h>
#include<unistd.h> //sheep(1)
#include<pthread.h>
/*
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
*/
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
int key;
int dir;
struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
void initFood() //隨機(jī)生成食物
{
int x = rand()%20;
int y = rand()%20;
food.hang = x;
food.lie = y;
}
void initNcuse()
{
initscr(); //初始化ncurse頁面
keypad(stdscr,1);//允許使用功能鍵
// noecho();//不把無關(guān)的信息,功能類的信息顯示在頁面上
}
int hasSnakeNode(int i,int j) //這個點有??嗎?有返回1, 沒有返回0
{
struct Snake *p;
p = head;
while(p != NULL) //遍歷??的身子,是不是這個點
{
if(p->hang==i && p->lie==j)
{
return 1;
}
p = p->next;
}
return 0;
}
int hasFood(int i,int j)//這個點是不是食物所在的點,有的返回1;
{
if(food.hang == i && food.lie == j)//吃到了返回1
{
return 1;
}
return 0;//沒吃到,返回0
}
void gamePic()
{
int hang,lie;
move(0,0);//將光標(biāo)設(shè)定在(0,0)
for(hang=0;hang<20;hang++)
{
if(hang==0)//第一行,打印“------”
{
for(lie=0;lie<20;lie++)
{
printw("--");
}
}
if(hang>0 && hang<=19) //打印左右界限
{
printw("\n");
for(lie=0;lie<=20;lie++)
{
if(lie==0 || lie==20)
{
printw("|");
}
else if(hasSnakeNode(hang,lie))//??的位置是不是這個點,打印身子
{
printw("[]");
}
else if(hasFood(hang,lie))//打印食物的位置
{
printw("##");
}
else
{
printw(" ");
}
}
}
if(hang == 19)//最后一行,打印邊界“------”
{
printw("\n");
for(lie=0;lie<20;lie++)
{
printw("--");
}
}
}
printw("\nBy TP, food.hang=%d,food.lie=%d\n\n",food.hang,food.lie);
}
void addNode() //根據(jù)方向添加節(jié)點,一節(jié)??
{
struct Snake *new = (struct Snake*)malloc(sizeof(struct Snake));
new->next = NULL;
switch(dir)
{
case UP:
new->hang = tail->hang-1;
new->lie = tail->lie;
break;
case DOWN:
new->hang = tail->hang+1;
new->lie = tail->lie;
break;
case LEFT:
new->hang = tail->hang;
new->lie = tail->lie-1;
break;
case RIGHT:
new->hang = tail->hang;
new->lie = tail->lie+1;
break;
}
tail->next = new;
tail = new;
}
void initSnake()//初始化
{
struct Snake *p;
dir = RIGHT;//剛開始的方向是向右邊
while(head!=NULL)
{
p = head; //head向下一個結(jié)點移一下,直到最后一個,將所有結(jié)點清空,只留一個 head=NULL
head = head->next;
free(p);
}
initFood();//隨機(jī)生成食物
head = (struct Snake*)malloc(sizeof(struct Snake));
head->hang = 1; //??的起始位子
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deleNode()//刪除??尾巴的節(jié)點,動起來
{
struct Snake *p;
p =head;
head = head->next;
free(p);
}
int ifSnakeDie()//??的死亡條件
{
struct Snake *p;
p = head;
if(tail->hang==0 || tail->lie==0 || tail->hang==20 || tail->lie==20)//撞墻
{
return 1;
}
while(p->next != NULL)//從到了自己嗎
{
if(p->hang == tail->hang && p->lie == tail->lie)
{
return 1;
}
p = p->next;
}
return 0;
}
void moveSnake() //吃到了食物與沒吃到食物,蛇的長度和位置的變化;蛇死了怎么辦;
{
addNode(); //添加節(jié)點
if(hasFood(tail->hang,tail->lie)) // 尾結(jié)點(頭),是不是食物所在的點
{
initFood(); // 是--重新創(chuàng)建食物--且不刪除head(蛇尾) 結(jié)點
}
else
{
deleNode();//刪除
}
if(ifSnakeDie()) //如果??死了
{
refresh();//Curses庫,屏幕刷新
usleep(100000);//暫停100000us
initSnake();//小蛇??初始化和食物??初始化
}
}
void *refreshJieMian()//進(jìn)程1 刷新界面
{
while(1)
{
moveSnake(); //前進(jìn)一格,
gamePic();
refresh();
usleep(100000); //0.1s,通過多線程,刷新界面,也起到了調(diào)速的作用;
}
}
void turn(int direction)//防止??上下往回走,左右走;
{
if(abs(dir) != abs(direction))//絕對值不一樣,才可以轉(zhuǎn)變方向
{
dir = direction;
}
}
void *changeDir() //進(jìn)程2 通過按鍵改方向
{
while(1)
{
key = getch();
switch(key)
{
case KEY_DOWN:
turn(DOWN);
break;
case KEY_UP:
turn(UP);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_RIGHT:
turn(RIGHT);
break;
}
}
}
int main()
{
pthread_t t1; //定義進(jìn)程 1
pthread_t t2; //定義進(jìn)程 2
initNcuse(); //初始化ncuses,頁面以及按鍵
initSnake(); //??的初始化和食物
gamePic(); //初始化地形,遍歷各各點,打印邊界、??、和食物
pthread_create(&t1,NULL,refreshJieMian,NULL);//不斷的刷新界面
pthread_create(&t2,NULL,changeDir,NULL); //不斷的檢測按鍵輸入
while(1);
getch(); //等待鍵盤輸入,不讓程序退出
endwin(); //恢復(fù)終端
return 0;
}
歡迎大家一起交流討論!文章來源地址http://www.zghlxwxcb.cn/news/detail-832938.html
到了這里,關(guān)于【C語言】做一個貪吃蛇小游戲,完整代碼&附帶視頻演示的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!