這篇文章原先部署於github, 后來覺得博客園比較快, 就用博客園了.
https://coinsjack.github.io/2017/03/01/C%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%B4%AA%E5%90%83%E8%9B%87%EF%BC%88ncurses%EF%BC%89/
聲明: 以下內容可能會引起某些讀者不適, 請小心閱讀. 有些內容並沒有詳細介紹, 可能簡單理解也是錯誤的, 但是這都是為了盡量簡單。
前言: 代碼是很久之前寫的,屬於邊想邊寫的那種,很混亂。
推薦材料:
貪吃蛇應該是我們這代人都玩過的游戲。而如果我們要寫一個貪吃蛇最需要考慮的就是貪吃蛇是如何移動的。其實貪吃蛇的移動就是尾部的減少和頭部的增加。
這篇文章:
- 介紹一些ncurses庫的基礎內容
- 貪吃蛇游戲的代碼解釋
介紹一些ncurses庫的基礎內容
ncurses
curses是一個在命令行下面的圖形函數庫,而ncurses的意思是 new curses。
ncurses的一些函數的簡單解釋
#include <ncurses.h> int main() { int ch; initscr(); /* Start curses mode */ raw(); /* Line buffering disabled */ keypad(stdscr, TRUE); /* We get F1, F2 etc.. */ noecho(); /* Don't echo() while we do getch */ printw("Type any character to see it in bold\n"); ch = getch(); /* If raw() hadn't been called * we have to press enter before it * gets to the program */ if(ch == KEY_F(1)) /* Without keypad enabled this will */ printw("F1 Key pressed");/* not get to us either */ /* Without noecho() some ugly escape * charachters might have been printed * on screen */ else { printw("The pressed key is "); attron(A_BOLD); printw("%c", ch); attroff(A_BOLD); } refresh(); /* Print it on to the real screen */ getch(); /* Wait for user input */ endwin(); /* End curses mode */ return 0; }
我們應該注意到這個程序並沒有導入標准輸入輸出庫,而是導入了ncurses.h文件。在這個程序里面也沒有使用printf(format printing),而是使用了printw這個函數(print window)就是打印到標准屏幕的意思這是我從這里拿到的一個例子。
- initscr:初始化屏幕,啟動curses模式
- raw:關閉行緩沖,一般的我們在命令行中輸入數據的時候,只有當我們按下回車之后,才可以把數據提交給程序
- noecho: 負責按鍵事務,我不太懂
- noecho: 關閉回顯(按下一個字符之后,屏幕並不顯示。比如說在普通的情況下按下a,a會被顯示在屏幕上)
- getch: 獲取一個字符
- attron: 在后面的打印中增加打印屬性,這里是粗體的意思
- attroff: 移除屬性
- refresh:之前的printw只是打印到邏輯屏幕,而refresh函數會依據邏輯屏幕的內容,刷新到物理屏幕。
- endwin:退出curses模式
其他的可以自己慢慢去學習
ncurses庫的安裝和使用
安裝
如果你是使用debian系列的linux發行版,例如ubuntu,應該直接鍵入命令:
sudo apt-get install libncurses5-dev
就可以了。
如果是別的系列的版本,建議下載源碼包,編譯安裝。或者百度搜索正確的答案。
使用
一般的,只需要在gcc編譯命令的后面加上-lncurses就可以正確導入ncurses庫了。
1
|
gcc -o demo demo.c -std=c99 -lncurses
|
-std=c99的意思是使用c99的標准來編譯代碼。
沒有使用過命令行編譯操作的同學需要自己去學習。
貪吃蛇的代碼解釋
從哪里得到
點擊這里。這份代碼是我寫的,也確實寫的不好。但也可以隨便看看。
代碼結構
snake.h
|-- hungry_snake // 可執行文件, |-- main.c |-- snake.c |-- snake.h `-- wellcome.h 0 directories, 5 files
hungry_snake是一個可執行文件,在debian下編譯的,在centos下面可能需要重新編譯。
main.c是程序的主要邏輯部分。
wellcome.h包含了歡迎界面的代碼實現。
/* * snake.h */ #ifndef SNAKE_H #define SNAKE_H #include <ncurses.h> // 這個枚舉類型我其實沒有用 typedef enum state { goleft, goright, goup, godown }state; // 這個是蛇的一節,也就是一個坐標 typedef struct body { int y; int x; struct body *prev; struct body *next; }Body ,*Pbody; // 這個是蛇的結構定義,有一個虛蛇頭,蛇頭和蛇尾,還有蛇的長度啊,運動狀態啊 typedef struct snake { Pbody vir_head; Pbody head; Pbody tail; int length; state movement; // 我沒有用 char uping; char downing; char lefting; char righting; }Snake, *Psnake; int FOOD[1000][1000]; // 食物坐標,標記哪個位置有食物 int food_num; // 食物數量 /* ** body part */ Pbody init_body(); // 初始化蛇身 void add_body(Pbody jack, Pbody bd); // 增加一節 Pbody bd(int lines,int cols); // 構造一個蛇節 void print_snake(Pbody jack); // 顯示那條蛇 void print_bd(Pbody bd); // 顯示蛇節 Pbody get_head(Pbody jack); // 得到蛇頭部節點 Pbody add_head(Pbody oldhead,Pbody newhead); // 增加節點到頭部 Pbody del_tail(Pbody jack); // 刪除蛇尾 Pbody get_tail(Pbody jack); // 得到蛇尾節點指針 /* ** snake part */ Psnake init_snake(); //初始化蛇 int snake_go_right(Psnake snake); // 蛇的移動 int snake_go_left(Psnake jack); void show_snake(Psnake jack); // 屏幕上顯示蛇 void init_show_snake(Psnake jack); // 忘了 int snake_go_down(Psnake jack); // 蛇的移動 int snake_go_up(Psnake jack); int snake_can_move(Psnake jack, char ch); // 判斷蛇能否往目標方向移動 int body_in_snake(int row, int col, Psnake jack); // 判斷蛇身是否覆蓋坐標 /* food part */ void putfood(Psnake jack); // 隨機投放食物 int count_food(); // 食物技術 /* game_over */ void game_over(Psnake jack); // 游戲結束 #endif // SNAKE_H
main.c這里有一些結構定義和函數聲明。
下面是程序主體邏輯部分:
#include <unistd.h> #include <stdlib.h> #include "snake.h" #include <ncurses.h> #include "wellcome.h" #include <pthread.h> int ch; int GAMEOVER; /* void get_command(void *i) { ch = getch(); } */ int main() { initscr(); start_color(); keypad(stdscr,TRUE); curs_set(0); noecho(); halfdelay(4); init_pair(1,COLOR_WHITE,COLOR_BLACK); init_pair(2,COLOR_BLACK,COLOR_WHITE); print_snake_logo(stdscr); mvprintw(LINES-1,COLS-20,"Producted by Jack"); mvprintw(LINES-4,(COLS-21)/2,"|-> START"); printw(" (? for help)"); refresh(); int choice; char goout = 0; while (!goout){ switch (choice = getch() ){ case 'q': endwin(); return 0; case 'e': goout = 1; break; } } /* pthread_t id; pthread_create(&id,NULL,get_command,NULL); */ Psnake jack = init_snake(); init_show_snake(jack); ch = mvgetch(LINES-1,0); srand((unsigned)time(0)); for (int i = 0; i<10; i++) putfood(jack); refresh(); int noout = 1; int c = 0; while(noout){ ch = getch(); switch (ch){ case KEY_RIGHT: c = snake_go_right(jack); if ( c == -1){ noout = 0; GAMEOVER = 1; break; } continue; case KEY_DOWN : c = snake_go_down(jack); if ( c == -1){ noout = 0; GAMEOVER = 1; break; } continue; case KEY_LEFT: c = snake_go_left(jack); if ( c == -1){ noout = 0; GAMEOVER = 1; break; } continue; case KEY_UP: c = snake_go_up(jack); if ( c == -1){ noout = 0; GAMEOVER = 1; break; } continue; case 'q': game_over(jack); endwin(); return 0; } if (jack->righting){ c = snake_go_right(jack); if (c == -1){ GAMEOVER = 1; break; } } else if (jack->lefting){ c = snake_go_left(jack); if (c == -1){ GAMEOVER = 1; break; } } else if(jack->uping){ c = snake_go_up(jack); if (c == -1){ GAMEOVER = 1; break; } } else if(jack->downing){ c = snake_go_down(jack); if (c == -1){ GAMEOVER = 1; break; } } if (food_num < 30) putfood(jack); mvprintw(LINES-1,2,"-- socre %d --",jack->length); refresh(); } if(GAMEOVER) game_over(jack); refresh(); endwin(); return 0; }
編譯
gcc -o snake.out snake.h snake.c main.c wellcome.h -lncurses -std=c99