目錄
一、ncurse
1.1 為什么需要用ncurse:
1.2 ncurse的輸入輸出:
1.2.1 如何使用ncurse:
1.2.2 編譯ncurse的程序:
1.2.3 測試輸入一個按鍵ncurse的響應(yīng)速度:
1.3 ncurse上下左右鍵獲?。?/p>
1.3.1 如何查看宏定義的.h文件:
1.3.2 ncurse上下左右鍵獲?。?/p>
二、地圖規(guī)劃
2.1 地圖規(guī)劃算法顯示第一行:
2.2 實現(xiàn)貪吃蛇完整地圖:
2.3 優(yōu)化貪吃蛇地圖:
三、顯示貪吃蛇身子
3.1 顯示貪吃蛇身子的一個節(jié)點:
3.2 顯示貪吃蛇完整身子:
3.3 顯示貪吃蛇完整身子改進:
四、貪吃蛇移動
4.1 按下?貪吃蛇向右移動:
4.2 貪吃蛇撞墻重新開始:?
4.3 貪吃蛇脫韁自由向右行走
五、Linux線程引入
5.1 貪吃蛇方向移動和刷新界面一起實現(xiàn)面臨的問題:
5.2 線程的基本用法:
5.3 線程demo案例:
5.4 使用線程解決貪吃蛇方向移動和刷新界面一起實現(xiàn)面臨的問題:
六、貪吃蛇跑起來
6.1 實現(xiàn)貪吃蛇四方向的風(fēng)騷走位:
6.2 用絕對值方式來解決不合理的走位:
6.3 貪吃蛇吃飯了(食物的位置是隨機的):
七、項目代碼
-
項目運行環(huán)境:Linux,基于Ncurse圖形庫的C語言小游戲
-
項目的目的和意義:起到承上啟下的作用,對于前面學(xué)的C語言的基礎(chǔ)和數(shù)據(jù)結(jié)構(gòu)鏈表做一個比較好的鞏固,對于后面的Linux系統(tǒng)編程的開發(fā)做鋪墊
-
項目基礎(chǔ)要求:C語言基礎(chǔ)、Linux基本操作
/*項目步驟*/ (1)選擇ncurses庫的原因 在進行貪吃蛇游戲時,貪吃蛇的行進方向需要你按下上下左右鍵進行操控,如果使用C語言自帶的函數(shù),例如:scanf或者getchar之類的,需要你按下回車鍵,程序才能進行響應(yīng),而這顯然是十分不方便的,但是ncurses庫就很好的解決了這個問題。ncurses庫自帶的函數(shù)getch就能實現(xiàn)迅速便捷的貪吃蛇方向響應(yīng)。 (2)ncurses庫的基本入門 對于該項目而言,ncurses庫我們不需要進行過于深入的學(xué)習(xí),只需要知道一些基本的函數(shù)使用即可。下列程序中的函數(shù),就是一個基于ncurses庫的基本代碼框架。 #include <curses.h> int main() { initscr();//ncurse界面的初始化函數(shù) printw("this is a curses window\n");//在ncurse模式下的printf getch();//等待用戶的輸入,如果沒有這句話,程序就退出了,看不到運行的結(jié)果,也就是無法看到上面那句話 endwin();//程序退出,恢復(fù)shell終端的顯示,如果沒有這句話,shell終端字亂碼,壞掉 return 0; } (3)貪吃蛇地圖的整體規(guī)劃 整個貪吃蛇地圖的大小將它設(shè)置成一個20*20的近似正方形,使用"|"來表示左右邊框,使用"--"來表示上下邊框。 (4)實現(xiàn)貪吃蛇第一個節(jié)點的顯示 (5)顯示貪吃蛇的完整身子 注意,在這里,我們設(shè)置兩個全局變量,struct Snake *head和struct Snake *tail,一個指向貪吃蛇的頭,一個指向貪吃蛇的尾。在將第一個節(jié)點打印完后,將尾指向頭,即:head = tail。每一次節(jié)點的添加,我們調(diào)用一個單獨的函數(shù)去執(zhí)行,并其使用尾插法實現(xiàn)。 (6)實現(xiàn)貪吃蛇的右移 貪吃蛇的移動,整體來說就是鏈表節(jié)點的刪除與添加。我們首先實現(xiàn)貪吃蛇的右移,每當(dāng)按鍵按下時,貪吃蛇右移一格,即左側(cè)的頭結(jié)點刪除head = head->next,右側(cè)再次添加一個新的節(jié)點。新節(jié)點的坐標(biāo)應(yīng)該是行不變,列加一。注意:不要忘記清楚垃圾節(jié)點。 (7)實現(xiàn)貪吃蛇的撞墻死亡 將貪吃蛇的尾節(jié)點坐標(biāo)進行判斷,判斷其是否達(dá)到邊界坐標(biāo)。滿足條件時,將貪吃蛇重新初始化。注意:不要忘記清楚垃圾節(jié)點。 (8)實現(xiàn)貪吃蛇的自由行走 在這里,我們發(fā)現(xiàn)了一個問題,地圖需要實時刷新進行貪吃蛇位置的變更,這是一個while(1)的死循環(huán),而獲取鍵值也是一個實時讀取的操作,因此也是一個while(1)死循環(huán),代碼執(zhí)行邏輯上出現(xiàn)了問題,所以我們引入了線程的概念。 (9)了解什么是線程 (10)用線程解決上述問題,實現(xiàn)貪吃蛇的分騷走位 開辟兩個線程,一個用來執(zhí)行地圖刷新操作,一個用來獲取鍵值。 pthread_create(&t1,NULL,refreshScreen,NULL); pthread_create(&t2,NULL,changeDir,NULL); (11)解決貪吃蛇的不合理走位 在這里,我們使用絕對值法來解決問題,abs函數(shù)的作用便是取絕對值,我們將上下左右鍵,兩兩對應(yīng),宏定義為1,-1,2,-2之類的數(shù)就能成功解決問題。 (12)實現(xiàn)貪吃蛇食物的打印 (13)實現(xiàn)食物的隨機出現(xiàn) 取隨機數(shù),C語言有一個自帶的函數(shù)可以解決這個問題,rand()函數(shù)可以實現(xiàn)隨機取數(shù),我們只要再對它進行取余操作,便可以防止食物出現(xiàn)在地圖以外的位置。 (14)實現(xiàn)貪吃蛇咬到自己結(jié)束游戲,重新開始的操作 當(dāng)貪吃蛇的尾節(jié)點與自身除尾巴節(jié)點以外的其他節(jié)點進行比較后,若行列數(shù)相同,則初始化整個貪吃蛇,注意:不要忘記垃圾節(jié)點的清除(我們可以在每次貪吃蛇初始化之前進行這個操作)。
一、ncurse
1.1 為什么需要用ncurse:
-
因為的按鍵響應(yīng)牛逼哄哄
-
1.2 ncurse的輸入輸出:
-
ncurse用的最多的地方是在Linux內(nèi)核編譯之前的內(nèi)核配置
-
1.2.1 如何使用ncurse:
1.2.2 編譯ncurse的程序:
1.2.3 測試輸入一個按鍵ncurse的響應(yīng)速度:
1 #include <curses.h>
2
3 int main()
4 {
5 char c;
6
7 initscr();
8 c = getch();
9 printw("you Input :%c\n",c);
10 getch();
11 endwin();
12 return 0;
13 }
-
使用ncurse的好處是:按下一個按鍵不需要按下回車,直接就可以輸出c的值,和我們C語言的其他輸入函數(shù)好用
1.3 ncurse上下左右鍵獲?。?/h4>
1.3.1 如何查看宏定義的.h文件:
vi /usr/include/curses.h?? ?//查看宏定義.h文件的指令
:q?? ??? ??? ??? ??? ??? ??? ?//退出查看
vi /usr/include/curses.h?? ?//查看宏定義.h文件的指令
:q?? ??? ??? ??? ??? ??? ??? ?//退出查看
1.3.2 ncurse上下左右鍵獲?。?/h5>
1 #include <curses.h>
2
3 int main()
4 {
5 int key;
6
7 initscr();
8 keypad(stdscr,1); //這個函數(shù)允許使用功能鍵,例如:F1、F2、方向鍵等功能鍵。幾乎所有的交互式程序都需要使用功能 鍵,因為絕大多數(shù)用戶界面主要用方向鍵進行操作。使用keypad(stdscr,TURE)就為“標(biāo)準(zhǔn)屏幕”(stdscr)激活了功能鍵。
9
10 while(1){
11 key = getch();
12 switch(key){
13 case KEY_DOWN:
14 printw("DOWN\n");
15 break;
16 case KEY_UP:
17 printw("up\n");
18 break;
19 case KEY_LEFT:
20 printw("LEFT\n");
21 break;
22 case KEY_RIGHT:
23 printw("RIGHT\n");
24 break;
25 }
26
27
28 }
29 endwin();
30 return 0;
31 }
-
我們按下上下左右▲▼??之后,可以獲取到上下左右的打印信息
二、地圖規(guī)劃
2.1 地圖規(guī)劃算法顯示第一行:
#include <curses.h>
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamPic()
{
int hang;
int lie;
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else{
printw(" ");
}
}
}
}
}
int main()
{
initNcurse(); //初始化Ncurse
gamPic(); //地圖規(guī)劃顯示第一行
getch();
endwin();
return 0;
}
2.2 實現(xiàn)貪吃蛇完整地圖:
#include <curses.h>
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamPic()
{
int hang;
int lie;
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang>0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
int main()
{
initNcurse(); //初始化Ncurse
gamPic(); //實現(xiàn)貪吃蛇地圖
getch();
endwin();
return 0;
}
2.3 優(yōu)化貪吃蛇地圖:
#include <curses.h>
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamPic()
{
int hang;
int lie;
for(hang=0; hang<20; hang++){
if(hang == 0){ //第0行打“--”
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) { //第0行-19行的第0列和第20列打“|”
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){ //第19行打“--”
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n"); //作者
}
}
}
int main()
{
initNcurse();
gamPic();
getch();
endwin();
return 0;
}
//實現(xiàn)的貪吃蛇地圖和上面一樣,只不過是優(yōu)化了一下代碼
三、顯示貪吃蛇身子
3.1 顯示貪吃蛇身子的一個節(jié)點:
#include <curses.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake node1 = {2,2,NULL};
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
void gamPic()
{
int hang;
int lie;
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(node1.hang == hang && node1.lie == lie){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
int main()
{
initNcurse();
gamPic();
getch();
endwin();
return 0;
}
3.2 顯示貪吃蛇完整身子:
#include <curses.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake node1 = {2,2,NULL};
struct Snake node2 = {2,3,NULL};
struct Snake node3 = {2,4,NULL};
struct Snake node4 = {2,5,NULL};
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = &node1;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
int main()
{
initNcurse();
node1.next = &node2;
node2.next = &node3;
node3.next = &node4;
gamPic();
getch();
endwin();
return 0;
}
3.3 顯示貪吃蛇完整身子改進:
#include <curses.h>
#include <stdlib.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL; //指向鏈表頭
struct Snake *tail = NULL; //指向鏈表尾
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic() //地圖規(guī)劃
{
int hang;
int lie;
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake)); //創(chuàng)建新節(jié)點
if(new == NULL){
printw("malloc error\n");
}
new->hang = tail->hang; //新節(jié)點的行等于鏈表尾的行
new->lie = tail->lie+1; //新節(jié)點的行等于鏈表尾的列+1
new->next = NULL;
tail->next = new; //從鏈表尾部插入新節(jié)點
tail = new; //新節(jié)點當(dāng)作尾部
}
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake)); //創(chuàng)建鏈表頭
if(head == NULL){
printw("malloc error\n");
}
head->hang = 2;
head->lie = 2;
head->next = NULL;
tail = head; //第一個節(jié)點鏈表頭和鏈表尾是一樣的
addNode(); //調(diào)用一次代表增加一個節(jié)點
addNode();
addNode();
}
int main()
{
initNcurse();
initSnake();
gamPic();
getch();
endwin();
return 0;
}
四、貪吃蛇移動
4.1 按下?貪吃蛇向右移動:
#include <curses.h>
#include <stdlib.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
new->hang = tail->hang;
new->lie = tail->lie+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
head = (struct Snake *)malloc(sizeof(struct Snake));
if(head == NULL){
printw("malloc error\n");
}
head->hang = 2;
head->lie = 2;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode(); //增加一個節(jié)點
deletNode(); //刪除頭節(jié)點
}
int main()
{
int con;
initNcurse();
initSnake();
gamPic();
while(1){
con = getch(); //con獲取鍵值
if(con == KEY_RIGHT){ //如果是右鍵
moveSnake(); //向右移動
gamPic(); //必須刷新一下界面,否則看不到??移動
}
}
getch();
endwin();
return 0;
}
4.2 貪吃蛇撞墻重新開始:?
#include <curses.h>
#include <stdlib.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
if(new == NULL){
printw("malloc error\n");
}
new->hang = tail->hang;
new->lie = tail->lie+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
struct Snake *p;
while(head != NULL){ 判斷蛇是否為空,清理內(nèi)存
p = head;
head = head->next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
if(head == NULL){
printw("malloc error\n");
}
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode();
deletNode();
//判斷蛇的尾巴碰到上下左右的四個邊框后就重新開始
if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){
initSnake();
}
}
int main()
{
int con;
initNcurse();
initSnake();
gamPic();
while(1){
con = getch();
if(con == KEY_RIGHT){
moveSnake();
gamPic();
}
}
getch();
endwin();
return 0;
}
4.3 貪吃蛇脫韁自由向右行走
#include <curses.h>
#include <stdlib.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
if(new == NULL){
printw("malloc error\n");
}
new->hang = tail->hang;
new->lie = tail->lie+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
struct Snake *p;
while(head != NULL){
p = head;
head = head->next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
if(head == NULL){
printw("malloc error\n");
}
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode();
deletNode();
if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){
initSnake();
}
}
int main()
{
int con;
initNcurse();
initSnake();
gamPic();
while(1){ //之前受方向鍵控制,現(xiàn)在自由行走
moveSnake();
gamPic();
refresh(); //刷新界面
usleep(100000); //延時100ms
}
getch();
endwin();
return 0;
}
五、Linux線程引入
5.1 貪吃蛇方向移動和刷新界面一起實現(xiàn)面臨的問題:
#include <curses.h>
#include <stdlib.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!\n");
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
if(new == NULL){
printw("malloc error\n");
}
new->hang = tail->hang;
new->lie = tail->lie+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
struct Snake *p;
while(head != NULL){
p = head;
head = head->next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
if(head == NULL){
printw("malloc error\n");
}
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode();
deletNode();
if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){
initSnake();
}
}
int main()
{
int key;
initNcurse();
initSnake();
gamPic();
while(1){
moveSnake();
gamPic();
refresh();
usleep(100000);
}
while(1){
key = getch();
switch(key){
case KEY_DOWN:
printw("DOWN\n");
break;
case KEY_UP:
printw("UP\n");
break;
case KEY_LEFT:
printw("LEFT\n");
break;
case KEY_RIGHT:
printw("RIGHT\n");
break;
}
}
getch();
endwin();
return 0;
}
-
在上面的程序中main函數(shù)中有兩個while(1)循環(huán),這樣就會出現(xiàn)問題,程序運行的現(xiàn)象是:獲取按鍵值的這個while循環(huán)根本不會執(zhí)行,那該如何解決?于是引入“Linux線程”!
-
在貪吃蛇運動過程中,我們需要改變蛇的移動方向,這是就需要不停掃描鍵盤輸入的值來判斷方向,同時還需要不停的刷新界面,為了多個while循環(huán)并存這里需要引入linux線程。
5.2 線程的基本用法:
#include <pthread.h> // 頭文件
pthread_t:當(dāng)前Linux中可理解為:typedef unsigned long int pthread_t;
如:pthread_t t1; //多線程定義
pthread_create(&t1,NULL,refreshInterface,NULL);
參數(shù)1:傳出參數(shù),保存系統(tǒng)為我們分配好的線程ID
參數(shù)2:通常傳NULL,表示使用線程默認(rèn)屬性。若想使用具體屬性也可以修改該參數(shù)。
參數(shù)3:函數(shù)指針,指向線程主函數(shù)(線程體),該函數(shù)運行結(jié)束,則線程結(jié)束。
參數(shù)4:線程主函數(shù)執(zhí)行期間所使用的參數(shù),如要傳多個參數(shù), 可以用結(jié)構(gòu)封裝。
使用多線程的函數(shù)必須返回指針型,如void *refreshInterface()
注:gcc xxx.c -lcurses -lpthead //編譯需要連接pthead庫
5.3 線程demo案例:
/*
在這個程序當(dāng)中只有func1一個函數(shù)會被執(zhí)行,func2函數(shù)根本不會執(zhí)行
想要解決這個問題就需要引入Linux的線程
*/
#include <stdio.h>
void pfunc1()
{
while(1){
printf("this is a pfunc1\n");
sleep(1);
}
}
void pfunc2()
{
while(1){
printf("this is a pfunc2\n");
sleep(1);
}
}
int main()
{
pfunc1();
pfunc2();
return 0;
}
/*
引入Linux線程修改代碼,func1和func2兩個函數(shù)都可以執(zhí)行
*/
#include <stdio.h>
#include <pthread.h> //線程頭文件
void* func1()
{
while(1){
printf("this is a func1\n");
sleep(1);
}
}
void* func2()
{
while(1){
printf("this is a func2\n");
sleep(1);
}
}
int main()
{
pthread_t th1; //定義一個th1線程
pthread_t th2; //定義一個th2線程
pthread_create(&th1, NULL, func1, NULL);
pthread_create(&th2, NULL, func2, NULL);
while(1);
return 0;
}
5.4 使用線程解決貪吃蛇方向移動和刷新界面一起實現(xiàn)面臨的問題:
#include <curses.h>
#include <stdlib.h>
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!key = %d\n",key);
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
new->hang = tail->hang;
new->lie = tail->lie+1;
new->next = NULL;
tail->next = new;
tail = new;
}
void initSnake()
{
struct Snake *p;
while(head != NULL){
p = head;
head = head->next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode();
deletNode();
if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){
initSnake();
}
}
void* refreshJieMian()
{
while(1){
moveSnake();
gamPic();
refresh();
usleep(100000);
}
}
void* changeDir()
{
while(1){
key = getch();
switch(key){
case KEY_DOWN:
printw("DOWN\n");
break;
case KEY_UP:
printw("UP\n");
break;
case KEY_LEFT:
printw("LEFT\n");
break;
case KEY_RIGHT:
printw("RIGHT\n");
break;
}
}
}
int main()
{
initNcurse();
initSnake();
//注意:線程創(chuàng)建要放在初始化后面,不然就會導(dǎo)致程序段錯誤(答疑老師解決)
pthread_t t1;
pthread_t t2;
pthread_create(&t1, NULL, refreshJieMian, NULL);
pthread_create(&t2, NULL, changeDir, NULL);
gamPic();
while(1);
getch();
endwin();
return 0;
}
文章來源:http://www.zghlxwxcb.cn/news/detail-732498.html
-
蛇在向右移動的同時也可以按方向鍵,這就是引入線程之后的牛逼之處!文章來源地址http://www.zghlxwxcb.cn/news/detail-732498.html
六、貪吃蛇跑起來
6.1 實現(xiàn)貪吃蛇四方向的風(fēng)騷走位:
#include <curses.h>
#include <stdlib.h>
#include <pthread.h>
#define UP 1
#define DOWN 2
#define LEFT 3
#define RIGHT 4
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;
void initNcurse()
{
initscr();
keypad(stdscr,1);
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");:
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!key = %d\n",key);
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
new->hang = tail->hang;
new->lie = tail->lie+1;
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 = head->next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode();
deletNode();
if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){
initSnake();
}
}
void* refreshJieMian()
{
while(1){
moveSnake();
gamPic();
refresh();
usleep(100000);
}
}
void* changeDir()
{
while(1){
key = getch();
switch(key){
case KEY_DOWN:
dir = DOWN;
break;
case KEY_UP:
dir = UP;
break;
case KEY_LEFT:
dir = LEFT;
break;
case KEY_RIGHT:
dir = RIGHT;
break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurse();
initSnake();
gamPic();
pthread_create(&t1, NULL, refreshJieMian, NULL);
pthread_create(&t2, NULL, changeDir, NULL);
while(1);
getch();
endwin();
return 0;
}
6.2 用絕對值方式來解決不合理的走位:
#include <curses.h>
#include <stdlib.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;
void initNcurse()
{
initscr();
keypad(stdscr,1);
noecho();
}
int hasSnakeNode(int i, int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
for(lie=0; lie<=20; lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(hasSnakeNode(hang,lie)){
printw("[]");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao!key = %d\n",key);
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
new->hang = tail->hang;
new->lie = tail->lie+1;
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 = head->next;
free(p);
}
head = (struct Snake *)malloc(sizeof(struct Snake));
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode();
deletNode();
if(tail->hang == 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){
initSnake();
}
}
void refreshJieMian()
{
while(1){
moveSnake();
gamPic();
refresh();
usleep(100000);
}
}
void turn(int direction) 通過絕對值判斷相反方向不觸發(fā)
{
if(abs(dir) != abs(direction)){
dir = direction;
}
}
void changeDir()
{
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;
pthread_t t2;
initNcurse();
initSnake();
gamPic();
pthread_create(&t1, NULL, refreshJieMian, NULL);
pthread_create(&t2, NULL, changeDir, NULL);
while(1);
getch();
endwin();
return 0;
}
6.3 貪吃蛇吃飯了(食物的位置是隨機的):
#include <curses.h>
#include <stdlib.h>
#include <pthread.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct Snake
{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
int key;
int dir;
struct Snake food;
void initFood()
{
int x = rand()%20;
int y = rand()%20;
food.hang = x;
food.lie = y;
}
void initNcurse()
{
initscr();
keypad(stdscr,1);
noecho();
}
int hasSnakeNode(int i, int j)
{
struct Snake *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)
{
if(food.hang == i && food.lie == j){
return 1;
}
return 0;
}
void gamPic()
{
int hang;
int lie;
move(0,0);
for(hang=0; hang<20; hang++){
if(hang == 0){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19) {
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(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0; lie<20; lie++){
printw("--");
}
printw("\n");
printw("By ShiYaHao! food.hang = %d,food.lie = %d\n",food.hang,food.lie);
}
}
}
void addNode()
{
struct Snake *new =(struct Snake *) malloc(sizeof(struct Snake));
new->hang = tail->hang;
new->lie = tail->lie+1;
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 = head->next;
free(p);
}
initFood();
head = (struct Snake *)malloc(sizeof(struct Snake));
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
addNode();
}
void deletNode()
{
struct Snake *p;
p = head;
head = head->next;
free(p);
}
void moveSnake()
{
addNode();
if(hasFood(tail->hang,tail->lie)){
initFood();
}else{
deletNode();
}
if(tail->hang < 0 || tail->lie == 0 || tail->hang == 20 || tail->lie == 20){
initSnake();
}
}
void* refreshJieMian()
{
while(1){
moveSnake();
gamPic();
refresh();
usleep(100000);
}
}
void turn(int direction)
{
if(abs(dir) != abs(direction)){
dir = direction;
}
}
void* changeDir()
{
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;
pthread_t t2;
initNcurse();
initSnake();
gamPic();
pthread_create(&t1, NULL, refreshJieMian, NULL);
pthread_create(&t2, NULL, changeDir, NULL);
while(1);
getch();
endwin();
return 0;
}
七、項目代碼
#include <curses.h>
#include <stdlib.h>
#include <pthread.h>
#define UP 1
#define DOWN -1
#define LEFT 2
#define RIGHT -2
struct Snake{
int hang;
int lie;
struct Snake *next;
};
struct Snake *head = NULL;
struct Snake *tail = NULL;
struct Snake food;
int key;
int dir;
void addNode(); /*從尾部插入新節(jié)點*/
//void initNcurses(); /*ncurses庫的初始化函數(shù)*/
//void gameMap(); /*貪吃蛇地圖的初始化*/
//int printSnakeNode(int i,int j); /*在地圖上打印貪吃蛇的節(jié)點*/
//void initSnake(); /*初始化貪吃蛇*/
//void deletNode(); /*刪除頭結(jié)點*/
//void moveSnake(); /*實現(xiàn)貪吃蛇的移動*/
//void *refreshScreen(); /*線程實現(xiàn)圖像刷新*/
//void *changeDir(); /*線程實現(xiàn)貪吃蛇方向的改變*/
//void turn(int direction); /*防止出現(xiàn)不合理走位*/
//void creatFood(); /*隨機出現(xiàn)食物*/
//int hasFood(int i,int j); /*打印食物*/
//int ifSnakeDie(); /*判斷貪吃蛇是否死亡*/
/*隨機出現(xiàn)食物*/
void creatFood()
{
int x = rand()%20;
int y = rand()%19+1;
food.hang = x;
food.lie = y;
}
int hasFood(int i,int j)
{
if(food.hang == i && food.lie == j){
return 1;
}
return 0;
}
/*ncurses庫的初始化函數(shù)*/
void initNcurses()
{
initscr();//ncurse界面的初始化函數(shù)
keypad(stdscr,1);//使用keypad函數(shù),才可以使用鍵盤功能鍵
noecho();//防止打印無關(guān)鍵值
}
/*貪吃蛇地圖的初始化*/
void gameMap()
{
int hang;
int lie;
move(0,0);//把光標(biāo)的位置移到頭,實現(xiàn)地圖刷新時的覆蓋
for(hang=0;hang<20;hang++){
if(hang == 0){
for(lie=0;lie<20;lie++){
printw("--");
}
printw("\n");
}
if(hang>=0 && hang<=19){
for(lie=0;lie<=20;lie++){
if(lie == 0 || lie == 20){
printw("|");
}else if(printSnakeNode(hang,lie)){
printw("[]");
}else if(hasFood(hang,lie)){
printw("##");
}else{
printw(" ");
}
}
printw("\n");
}
if(hang == 19){
for(lie=0;lie<20;lie++){
printw("--");
}
printw("\n");
}
}
printw("By ShiYaHao!,food.hang = %d,food.lie = %d\n",food.hang,food.lie);
}
/*在地圖上打印貪吃蛇的節(jié)點*/
int printSnakeNode(int i,int j)
{
struct Snake *p = head;
while(p != NULL){
if(p->hang == i && p->lie == j){
return 1;
}
p = p->next;
}
return 0;
}
/*初始化貪吃蛇*/
void initSnake()
{
struct Snake *p = NULL;
if(head != NULL){ //當(dāng)貪吃蛇死亡后,把多余節(jié)點釋放
p = head;
head = head->next;
free(p);
}
creatFood();
dir = RIGHT;
head = (struct Snake *)malloc(sizeof(struct Snake));
head->hang = 1;
head->lie = 1;
head->next = NULL;
tail = head;
addNode();
addNode();
addNode();
}
/*從尾部插入新節(jié)點*/
void addNode()
{
struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));
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;
}
new->next = NULL;
tail->next = new;
tail = new;
}
/*刪除頭結(jié)點*/
void deletNode()
{
struct Snake *p = head;
head = head->next;
free(p);
}
/*判斷貪吃蛇是否死亡*/
int ifSnakeDie()
{
struct Snake *p;
p = head;
if(tail->hang < 0 || tail->hang == 20 || tail->lie == 0 || 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;
}
/*實現(xiàn)貪吃蛇的移動*/
void moveSnake()
{
addNode();
if(hasFood(tail->hang,tail->lie)){
creatFood();
}else{
deletNode();
}
if(ifSnakeDie()){
initSnake();
}
}
/*線程實現(xiàn)圖像刷新*/
void *refreshScreen()
{
usleep(100000);
while(1){
moveSnake();
gameMap();//刷新地圖
refresh();//界面刷新函數(shù)
usleep(100000);
}
}
/*防止不合理走位*/
void turn(int direction)
{
if(abs(dir) != abs(direction)){
dir = direction;
}
}
/*線程實現(xiàn)貪吃蛇方向的改變*/
void *changeDir()
{
while(1){
key = getch();
switch(key){
case KEY_UP:
turn(UP);
break;
case KEY_DOWN:
turn(DOWN);
break;
case KEY_LEFT:
turn(LEFT);
break;
case KEY_RIGHT:
turn(RIGHT);
break;
}
}
}
int main()
{
pthread_t t1;
pthread_t t2;
initNcurses();
initSnake();
gameMap();
pthread_create(&t1,NULL,refreshScreen,NULL);
pthread_create(&t2,NULL,changeDir,NULL);
while(1);
getch();//等待用戶的輸入,如果沒有這句話,程序就退出了,看不到運行的結(jié)果,也就是無法看到上面那句話
endwin();//程序退出,恢復(fù)shell終端的顯示,如果沒有這句話,shell終端字亂碼,壞掉
return 0;
}
到了這里,關(guān)于C語言——貪吃蛇小游戲的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!