引言 - getch簡述
引用老的TC版本getch說明. (文章介紹點有點窄, 應用點都是一些恐龍游戲時代的開發細節)
#include <conio.h>
/* * 立即從客戶端得到輸入的字符. 出錯返回EOF */ int __cdecl getch(void);
記得三年之前看過一本書 <<C專家編程>> 有一章提到在立即從標准輸入中得到輸入字符(後面還介紹了一種linux實現, 對於現在linux版本不行了)。
C專家編程 清晰 (有書簽索引) PDF 下載 http://www.linuxidc.com/Linux/2012-05/60077.htm
那位作者評價就是, 由於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游戲, 讓其支持跨平台, 並支持配置擴展.