getch, 這個立即接收輸入的字元. 一個微不足道的函數, 就改變了游戲交互對於linux的認識. 本文構建一個跨平臺的getch, 滿足及時輸入交互. ...
引言 - getch簡述
引用老的TC版本getch說明. (文章介紹點有點窄, 應用點都是一些恐龍游戲時代的開發細節)
#include <conio.h>
/* * 立即從客戶端得到輸入的字元. 出錯返回EOF */ int __cdecl getch(void);
記得三年之前看過一本書 <<C專家編程>> 有一章提到在立即從標準輸入中得到輸入字元(後面還介紹了一種linux實現, 對於現在linux版本不行了).
那位作者評價就是, 由於linux對於''getch''支持的不友好, 導致了linux錯失了很多游戲開發人員.
當然現在版本, window 上也沒有這個函數了. 改成下麵這個挫的樣子
#include <conio.h> _Check_return_ _DCRTIMP int __cdecl _getch(void);
總得而言''立即交互'' 是游戲開發的入口. 很有必要.
前言 - 從實際例子中瞭解getch
現在Visual Studio 2015 Update3 中測試一段 getch 立即得到結果的代碼 main.c
#include <stdio.h> #include <stdlib.h> #include <conio.h> /* * 製作等待, 函數 */ int main(int argc, char * argv[]) { printf("請輸入任意字元結束程式......"); int rt = _getch(); printf("%d => %c\n", rt, rt); rt = _getch(); printf("%d => %c\n", rt, rt); system("pause"); return 0; }
運行結果
從上可以看出, _getch 名字變了, 但是功能和getch沒有變化.
這裡我們封裝一下. 看新的文件, 一個演示小demo
#include <stdio.h> #include <conio.h> /* * 定義統一介面 sh_getch 理解得到玩家輸入 * : 返回 輸入int值, 錯誤為EOF */ #define sh_getch _getch /* * 等待函數 */ static void _pause(void) { printf("請按任意鍵繼續. . .");
rewind(stdin); sh_getch(); } /* * 繼續等待函數 */ int main(int argc, char * argv[]) { _pause(); return 0; }
來替代原先的 window 上 的 system("pause"), linux 上 pause(). rewind 重置文件FILE * 流, 清除輸入流保證當前流是乾凈的.
正文 - linux上實現一個getch, 立即接收
linux 需要藉助 termio.h 終端控制頭文件. 主要實現如下
#include <termio.h> /* * 得到用戶輸入的一個字元 * : 返回得到字元 */ int sh_getch(void) { int cr; struct termios nts, ots; if (tcgetattr(0, &ots) < 0) // 得到當前終端(0表示標準輸入)的設置 return EOF; nts = ots; cfmakeraw(&nts); // 設置終端為Raw原始模式,該模式下所有的輸入數據以位元組為單位被處理 if (tcsetattr(0, TCSANOW, &nts) < 0) // 設置上更改之後的設置 return EOF; cr = getchar(); if (tcsetattr(0, TCSANOW, &ots) < 0) // 設置還原成老的模式 return EOF; return cr; }
主要是設置終端為原始接收字元模式, 可以接收立即返回, 隨後還原老的環境設置. 終端緩衝, 也是出於效率考慮, 否則編程太複雜了.
同樣測試 一個 getch.c
#include <stdio.h> #include <termio.h> /* * 得到用戶輸入的一個字元 * : 返回得到字元 */ int sh_getch(void); /* * 測試標準快速輸入 */ int main(int argc, char * argv[]) { int ch; printf("請按任意鍵繼續. . ."); ch = sh_getch(); printf("%d => %c\n", ch, ch); ch = sh_getch(); printf("%d => %c\n", ch, ch); return 0; }
linux上演示結果
gcc -Wall -ggdb3 -o getch.out getch.c
一切正常.
到這裡我們關閉getch跨平臺實現細節都確定了. 那麼我們實現一個跨平臺的getch版本. 先看頭文件聲明部分(*.h 文件插入).
/* * error => 以後再說 * 跨平臺的醜陋從這裡開始 * __GNUC => linux 平臺特殊操作 * __MSC_VER => window 平臺特殊操作 */ #ifdef __GUNC__ // 下麵是依賴GCC編譯器實現 #include <termio.h> /* * 得到用戶輸入的一個字元 * : 返回得到字元 */ int sh_getch(void); #elif _MSC_VER // 下麵是依賴Visual Studio編譯器實現 #include <conio.h> // window 上用_getch 替代了getch, 這裡為了讓其回來 #define sh_getch _getch #else #error "error : Currently only supports the Visual Studio and GCC!" #endif
再看實現部分 (*.c 文件中插入)
// 為linux擴展一些功能 #if defined(__GUNC__) /* * 得到用戶輸入的一個字元 * : 返回得到字元 */ int sh_getch(void) { int cr; struct termios nts, ots; if (tcgetattr(0, &ots) < 0) // 得到當前終端(0表示標準輸入)的設置 return EOF; nts = ots; cfmakeraw(&nts); // 設置終端為Raw原始模式,該模式下所有的輸入數據以位元組為單位被處理 if (tcsetattr(0, TCSANOW, &nts) < 0) // 設置上更改之後的設置 return EOF; cr = getchar(); if (tcsetattr(0, TCSANOW, &ots) < 0) // 設置還原成老的模式 return EOF; return cr; } #endif
這就是getch跨平臺實現的關鍵了. 從這裡開始,你就可以構建自己喜歡的游戲了, 通過 sh_getch 入口開始.
預備下次重構C字元串,再下次採用simplec框架重寫一個老的滅龍傳說V2.0.0游戲, 讓其支持跨平臺, 並支持配置擴展.
後記 - 未來有 ∞ 次
Get Over http://music.163.com/#/song?id=22771653