這是我1999年寫的一篇文章了,應該說,經過了這麼多年的進步,文中很多與DOS相關的內容,現在看起來已經不合時宜了,不過,我認為裡面的一些理念,到任何時候都不會過時。

最近看到tr110223網友的《C語言編寫簡單2D遊戲》(http://282761713.blog.51cto.com/1495050/334024),對於裡面的AND演算法略有分歧,因此找出這篇老文來探討。希望能對大家有所幫助。

有興趣的朋友也可以瞭解一下,當年我們在DOS下,沒有任何圖形引擎的幫助下,是如何開發遊戲程式的。

注:那個時侯我剛剛開始學習寫文章沒多久,呵呵,文中筆鋒顯得很粗糙、稚嫩,可讀性並不是很好,請各位網友見諒。

DOS遊戲程式設計二十一條(The 21 FAQ of PC DOS GAME)

     我在一家遊戲公司多年,現就自己的程式設計經驗談一點體會,希望能對大家有點幫助,
本文中所有例子均在WATCOM C/C++ 10.6下除錯通過。
   
1、找一種好的程式語言:
    當然,遊戲可以用任何語言編寫,這是可以肯定的,我就使用過Turbo Basic編寫過
跑馬機遊戲,還用VB寫過一個半成品的網路拱豬遊戲,但是,一個好的程式語言能夠達到
好的效果,這是毋庸置疑的。一個遊戲程式設計師,夢寐以求的就是一個方便、完美、高速的
語言。
    彙編是一種高速語言,但不夠方便,如果要方便,就必須大量使用巨集,筆者就曾經在
6502組合語言中大量使用FOR,NEXT,PRINT等語句,全部是巨集,但是相應的記憶體開銷,時間
開銷都加大了,不划算。
    WATCOM C/C++是一種好語言,可以訪問大記憶體,速度快,也夠方便,但是除錯不夠方
便,只能用自己寫的除錯函式解決問題。還有每次執行必須呼叫DOS/4GW這個32位環境程式,
既累贅又不方便,還佔地方。
    MSC7.0也不錯,通過它的虛擬記憶體機制也可以訪問大記憶體,但可惜是16位模擬的,速
度太慢。
    DJGPP也是很不錯,關鍵它是共享的,同時還帶有一個Alleg的共享遊戲庫,非常好用,
推薦使用,但它生成的程式程式碼太大,不夠優化。
    作為遊戲程式設計師,我們追求的就是快一點、快一點、再快一點,如果還有更快的語言,
希望大家介紹給我。

2、要寫專有程式,不要寫通用的,通用,意味著慢,哪怕下次重新來過,也不能為了下次
耽誤這次。同理,凡是系統給你的函式,呼叫,要有堅決不用的思想準備,要自己寫一套。

3、寫出來的程式,每秒鐘必須刷屏70次以上,再通過時鐘限制在30次(不抖),剩下的時
間,就是執行你的遊戲程式內容的時間,算一算,不多。

4、要有引擎的概念,引擎包含系統底層的程式,資料結構,呼叫方法等,這些直接限制你
以後的遊戲好不好編,一般說來,我們做一個遊戲半年時間,其中兩個月編引擎,兩個月編
遊戲,剩下兩個月除錯,可見引擎的重要。永遠記著,你寫的程式,就計算機而言,就是在
搬數,把一堆資料提出來,處理一下搬到另外一塊地方,就這麼簡單,那麼,搬數的方法有
多重要,你知道了吧。

5、絕對不能用乘除法、浮點數,我在一個遊戲中,15萬行程式,沒有使用一個乘除法和浮
點數,一直很自豪。在這之前,我經常因為程式中的一個乘法在夢中嚇醒,直到有一天學會
使用移位乘法。另外記住,遊戲只用整數,如果你用了浮點數,改你的程式。這裡給一個移
位乘法的例子,大家可以參考:
/*——————————————————————————-*/
//XiaoGe Made under WATCOM C/C++ 10.6
/*——————————————————————————-*/
int count_offest(int _width,int x,int y) //移位乘法計算顯示偏移值
{
 int mode[17]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
 int i,offest=0;
 for (i=16;i>=0;i–)
 {
  if (_width>=mode[i])
  {
   offest+=(y<<i);
   _width-=mode[i];
   if (_width<1) break;
  }
 }
 return(offest+x);
}
/*——————————————————————————-*/
結果=螢幕寬度*y+x,很常用的。

6、沒人會用數學描述去寫一幅遊戲圖形,所有的圖形都來自於美工畫的PCX,BMP圖形,去找一個
美工,或者偷一批圖形吧。讀寫PCX,BMP的函式是必須的。

7、遊戲程式設計師語錄:給我一個畫點函式吧,我能描繪出整個世界。任何時候下,一個最高速的
畫點程式都是必要的,這裡給出一個例子(沒有乘法的):
/*——————————————————————————-*/
//XiaoGe Made under WATCOM C/C++ 10.6
/*——————————————————————————-*/
void point(int x,int y,unsigned char color) //高速畫點
{
 if ((color!=NO_COLOR)&&
  (x>=0)&&(x<SCR_H)&&
  (y>=0)&&(y<SCR_V))
 {
  #ifdef VESA_320_200
  *(buffer+(y<<8)+(y<<6)+x)=color;
  #endif
  #ifdef VESA_640_480
  *(buffer+(y<<9)+(y<<7)+x)=color;
  #endif
  #ifdef VESA_800_600
  *(buffer+(y<<9)+(y<<8)+(y<<5)+x)=color;
  #endif
  #ifdef VESA_1024_768
  *(buffer+(y<<10)+x)=color;
  #endif
  #ifdef VESA_1280_1024
  *(buffer+(y<<10)+(y<<8)+x)=color;
  #endif
 }
}
/*——————————————————————————-*/

8、鏤空演算法很多,用的都是AND MASK+OR方式,不要去理它,每一個點要處理兩遍,包含三次
讀記憶體,兩次邏輯運算,一次寫記憶體,太慢了,在你的顏色中規定一種透明色,畫點時不去管
它就行了(上例)。記住,每個點上少處理一次,你至少可以多跳一圈舞。

9、雙緩衝是必要的,但也不全是,很多教課書上把雙緩衝作為消除螢幕閃爍的唯一方法,這不
對,因為只要跟蹤了螢幕重新整理週期,就不會閃,雙緩衝直接帶來的就是你的程式畫點必須畫兩次
,一次向buffer,另一次重buffer搬到螢幕。我在做優化時,往往首先把雙緩衝優化掉。沒必要
浪費時間,就算有點閃,遊戲是可以犧牲效果,換取時間的。這裡給一個跟蹤螢幕重新整理週期的函
數,只要在你的刷屏程式前加上,效果基本上就可以了。
/*——————————————————————————-*/
//XiaoGe Made under WATCOM C/C++ 10.6
/*——————————————————————————-*/
void wait (void) //VGA螢幕重新整理週期的測試
{
 while (inp(0x3DA)&0x08);
 while (!(inp(0x3DA)&0x08));
}
/*——————————————————————————-*/

10、刷屏程式應該包含:背景螢幕重新整理、精靈動畫重新整理、滑鼠處理、鍵盤處理等,並且,每秒鐘
必須能執行70次以上,如果做不到,優化你的程式。

11、優化是必須的,一個遊戲引擎,至少應該優化7-10次,我的一個引擎,就優化了14次,速度
從每秒鐘12.1屏到70屏。還有,不要使用編譯器的優化,除非你想你的使用者無法使用你的程式。

12、計算你的每一步使用了幾步操作,這一點在C中尤其重要,因為C太方便了,隱瞞了很多細節,
如下例:
從 *(Video+k)=*(p[1]+j);
 k++;
到 *(Video+(k++))=*(p[1]+j); //減少了一次k讀記憶體操作
到 *(Video+(k++))=*(*(p+1)+j); //減少了把p轉化成陣列操作

13、減少迴圈,迴圈中多開銷了一次累加(讀寫記憶體),一次比較(讀記憶體+1次邏輯),如下例:
從 for (i=0;i<10000;i++)
 {
  *(p+i)=0;
 }
到 for (i=0;i<10000;i+=10)  //迴圈次數減少9000次
 {
  *(p+i+0)=0;
  *(p+i+1)=0;
  *(p+i+2)=0;
  *(p+i+3)=0;
  *(p+i+4)=0;
  *(p+i+5)=0;
  *(p+i+6)=0;
  *(p+i+7)=0;
  *(p+i+8)=0;
  *(p+i+9)=0;
 }
到 for (i=0;i<10000;i+=10)  //20次讀變數記憶體減少為12次讀,1次寫
 {
  j=p+i;
  *(j+0)=0;
  *(j+1)=0;
  *(j+2)=0;
  *(j+3)=0;
  *(j+4)=0;
  *(j+5)=0;
  *(j+6)=0;
  *(j+7)=0;
  *(j+8)=0;
  *(j+9)=0;
 }
到 for (i=0;i<10000;i+=10)  //10次讀值記憶體減少為1次,其餘為暫存器變數
 {
  j=p+i;
  *(j+0)=*(j+1)=*(j+2)=*(j+3)=*(j+4)=*(j+5)=*(j+6)=*(j+7)=*(j+8)=*(j+9)=0;
 }
當然,如果允許,可以寫10000個,不過也沒必要,減掉一多半就行了。關鍵在速度和程式容量上達成
平衡。另外,DO…WHILE比FOR和WHILE要少一次邏輯比較。

14、具體的說,處理一個圖塊時,很多人採用x,y兩重迴圈,這是很值得研究的,根據螢幕特點,應該
只保留y迴圈,x方向直接線性累加處理即可。

15、不要節約判斷語句,它可能給你帶來多一條語句的開銷,但是卻可能減少幾百條語句的開銷,1賠
100,賭了。

16、別給自己找病,養成良好的書寫習慣,讓編譯程式為你檢查錯誤,如下例
 if (i==1) 
寫成 if (i=1) 編譯不出錯,但意思錯了
寫成 if (1=i) 編譯就出錯,可以檢查出來

17、遊戲程式沒有主迴圈,主迴圈往往只是包含刷屏的一個死迴圈,更多的東東放在時鐘裡頭,
要熟練攔截時鐘,改變它的頻率,你的畫面就會動得流暢、自然。下面是一個攔截時鐘的例子,
因為採用時鐘迴圈,所以必須大量使用switch/case結構,要有思想準備。

/*——————————————————————————-*/
//XiaoGe Made under WATCOM C/C++ 10.6
/*——————————————————————————-*/
#define TIME_KEEPER_INT 0x1c
long timer_counter;
void (_interrupt far *Old_Time_Isr)();
void timer_program(void);
////////////////////////////////////////////////////////////////
//注意:中斷函式中不能呼叫系統輸入輸出函式,應儘量使用自己的程式
void _interrupt Timer(void)
{
 timer_program(); //呼叫使用者程式
 timer_counter++;
 Old_Time_Isr();
}
////////////////////////////////////////////////////////////////
#define CTRL_8253 0x43
#define CTRL_WORD 0x3c
#define COUNTER_0 0x40
#define COUNTER_1 0x41
#define COUNTER_2 0x42
#define LOW_BYTE(n) (n&0x00ff)
#define HI_BYTE(n) ((n>>8)&0x00ff)

#define TIME_18HZ 0xFFFF
//改變定時器頻率函式
//注意:超過1000Hz,與Windows將發生衝突
void Change_Timer(unsigned short new_count)
{
 outp(CTRL_8253,CTRL_WORD);
 outp(COUNTER_0,LOW_BYTE(new_count));
 outp(COUNTER_0,HI_BYTE(new_count));
}
////////////////////////////////////////////////////////////////
//安裝時鐘
void install_timer(int Hz)
{
 short time_hz;
 time_hz=short(1193180/Hz);
 timer_counter=0;
 Change_Timer(time_hz);
 Old_Time_Isr=_dos_getvect(TIME_KEEPER_INT);
 _dos_setvect(TIME_KEEPER_INT,Timer);
}
////////////////////////////////////////////////////////////////
//解除安裝時鐘
void uninstall_timer()
{
 Change_Timer(TIME_18HZ);
 _dos_setvect(TIME_KEEPER_INT,Old_Time_Isr);
}
////////////////////////////////////////////////////////////////
/*——————————————————————————-*/

18、不要去相信mouse程式會為你做到一切,去讀0x33的狀態,游標由自己顯示,否則,哼哼……
例子:
/*——————————————————————————-*/
//XiaoGe Made under WATCOM C/C++ 10.6
/*——————————————————————————-*/
unsigned short cursor[] =
{
    0x0000,  /*0000000000000000*/ /* 16 words of cursor mask */
    0x4000,  /*0100000000000000*/
    0x6000,  /*0110000000000000*/
    0x7000,  /*0111000000000000*/
    0x7800,  /*0111100000000000*/
    0x7c00,  /*0111110000000000*/
    0x7e00,  /*0111111000000000*/
    0x7f00,  /*0111111100000000*/
    0x7c00,  /*0111110000000000*/
    0x4600,  /*0100011000000000*/
    0x0600,  /*0000011000000000*/
    0x0300,  /*0000001100000000*/
    0x0300,  /*0000001100000000*/
    0x0180,  /*0000000110000000*/
    0x0180,  /*0000000110000000*/
    0x00c0,  /*0000000011000000*/
};
struct Mouse
{
 char show;                              //mouse 游標顯示/不顯示
 char left;                              //mouse左鍵
 char right                              //mouse右鍵
 char middle;                            //mouse中鍵
 int x;                                  //mouseX座標
 int y;                                  //mouseY座標
 unsigned char color;            //mouse游標顏色
}mouse;
int mouse_page;
/*——————————————————————————-*/
void set_mouse_xy(int x_min,int x_max,int y_min,int y_max)
{
 REGS regs;
 if (x_min<0) x_min=0;
 if (x_max>SCR_H) x_max=SCR_H;
 if (y_min<0) y_min=0;
 if (y_max>SCR_V) y_max=SCR_V;
//Define H min-max
 regs.w.ax=0x07;
 regs.w.cx=x_min;
 regs.w.dx=x_max;
 int386(0x33,&regs,&regs);
//Define V min-max
 regs.w.ax=0x08;
 regs.w.cx=y_min;
 regs.w.dx=y_max;
 int386(0x33,&regs,&regs);
//POSITION MOUSE CURSOR
 regs.w.ax=0x04;
 regs.w.cx=(x_max-x_min)>>1;
 regs.w.dx=(y_max-y_min)>>1;
 int386(0x33,&regs,&regs);
}
/*——————————————————————————-*/
void init_mouse(void)
{
 REGS regs;
 mouse.x=SCR_H/2;
 mouse.y=SCR_V/2;
 mouse.left=0;
 mouse.right=0;
 mouse.middle=0;
 mouse.color=255;
 mouse.show=0;
//mouse reset
 regs.w.ax=0x00;
 int386(0x33,&regs,&regs);
//old mouse hidden
 regs.w.ax=0x01;
 int386(0x33,&regs,&regs);
 set_mouse_xy(0,SCR_H,0,SCR_V);
//Define Mic/Piexl
 regs.w.ax=0x0F;
 regs.w.cx=4;
 regs.w.dx=4;
 int386(0x33,&regs,&regs);
}
/*——————————————————————————-*/
void hard_disp_mouse(void)
{
    int i,j,x,y;
    long addr,addr1,page;
    unsigned short temp;
    unsigned char color;
    unsigned int b[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
    unsigned char *video=(unsigned char *)0xA0000;

    color=mouse.color;
    y=mouse.y-1;
    addr=count_offest(SCR_H,mouse.x,mouse.y);
    mouse_page=addr>>16;
    set_page(mouse_page);
 for(i=0;i<16;i++)
 {
  x=mouse.x-1;
  temp=cursor[i];
  addr=count_offest(SCR_H,x,y);
  for(j=16;j>=0;j–)
  {
      if((b[j]&temp)&&(x>0)&&(x<SCR_H)&&(y>0)&&(y<SCR_V))
      {
    page=addr>>16;
    addr1=addr-(page<<16);
    if (mouse_page!=page)
    {
        mouse_page=page;
        set_page(mouse_page);
    }
    *(video+addr1)=color;
    *(buffer+addr)=color;
      }
   x++;
   addr++;
  }
  y++;
 }
}
/*——————————————————————————-*/
void read_mouse(void)
{

 REGS in,out;
 mouse.color=255;
 in.w.ax=0x03;
 int386(0x33,&in,&out);
 mouse.left=(out.w.bx&0x01);
 mouse.right=(out.w.bx&0x02);
 mouse.middle=(out.w.bx&0x04);
 
 mouse.x=out.w.cx;
 if (0>mouse.x) mouse.x=0;
 if (SCR_H<mouse.x) mouse.x=SCR_H;
 mouse.y=out.w.dx;
 if (0>mouse.y) mouse.y=0;
 if (SCR_V<mouse.y) mouse.y=SCR_V;
 if (mouse.show)   hard_disp_mouse();
}
/*——————————————————————————-*/

19、鍵盤操作要攔截鍵盤中斷,可不能用系統給的函式,遊戲程式設計師戒條:凡是系統給的,必然
是不合用的。下面是例子。使用時,在你的迴圈中直接監測key_ascii就行了。
/*——————————————————————————-*/
#define SCAN_ALT    56
#define SCAN_CTRL     29
#define SCAN_caps 58
#define SCAN_LEFTSHIFT 42
#define SCAN_RIGHTSHIFT 54
#define SCAN_SHIFT (keyflag[SCAN_RIGHTSHIFT]||keyflag[SCAN_LEFTSHIFT])
#define KEY_END 255
#define KEY_LEFT 254
#define KEY_RIGHT 253
#define KEY_PAGEUP 252
#define KEY_UP 251
#define KEY_DOWN 250
#define KEY_HOME 249
#define KEY_CTRLBREAK 248
#define KEY_F1 247
#define KEY_F2 246
#define KEY_F3 245
#define KEY_F4 244
#define KEY_F5 243
#define KEY_F6 242
#define KEY_F7 241
#define KEY_F8 240
#define KEY_F9 239
#define KEY_F10 238
#define KEY_PAGEDOWN 237
#define KEY_INSERT 236
#define KEY_DELETE 235
#define KEY_LEFTALT 234
#define KEY_RIGHTALT 233
#define KEY_RIGHTCTRL 232
#define KEY_LEFTCTRL 231
#define KEY_caps 230
#define KEY_F11 229
#define KEY_F12 228
#define KEY_PRINTSCREEN 228
#define KEY_NUMLOCK 227
#define KEY_SCROLLLOCK 226
#define KEY_LEFTSHIFT 225
#define KEY_RIGHTSHIFT 224
#define KEY_WINDOWS 223
/*——————————————————————————-*/
static unsigned char  asciinames[]={
 0,27,`1`,`2`,`3`,`4`,`5`,`6`,`7`,`8`,`9`,`0`,`-`,`=`,8,9,
 `q`,`w`,`e`,`r`,`t`,`y`,`u`,`i`,`o`,`p`,`[`,`]`,13,KEY_LEFTCTRL,`a`,`s`,
 `d`,`f`,`g`,`h`,`j`,`k`,`l`,`;`,39,“`,0,92,`z`,`x`,`c`,`v`,
 `b`,`n`,`m`,`,`,`.`,`/`,0,`*`,KEY_LEFTALT,` `,KEY_caps,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,
 KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10,KEY_NUMLOCK,KEY_SCROLLLOCK,`7`,`8`,`9`,`-`,`4`,`5`,`6`,`+`,`1`,
 `2`,`3`,`0`,127,0,0,`\`,KEY_F11,KEY_F12,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
     };
/*——————————————————————————-*/
static unsigned char  shiftnames[]={
 0,27,`!`,`@`,`#`,`$`,`%`,`^`,`&`,`*`,`(`,`)`,`_`,`+`,8,9,
 `Q`,`W`,`E`,`R`,`T`,`Y`,`U`,`I`,`O`,`P`,`{`,`}`,13,1,`A`,`S`,
 `D`,`F`,`G`,`H`,`J`,`K`,`L`,`:`,34,`~`,KEY_LEFTSHIFT,`|`,`Z`,`X`,`C`,`V`,
 `B`,`N`,`M`,`<`,`>`,`?`,KEY_RIGHTSHIFT,`*`,1,` `,0,0,0,0,0,0,
 0,0,0,0,0,0,0,`7`,`8`,`9`,`-`,`4`,`5`,`6`,`+`,`1`,
 `2`,`3`,`0`,127,0,0,`|`,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0
     };
/*——————————————————————————-*/
static char specialnames[]={
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,13,KEY_RIGHTCTRL,0,0,
 0,0,0,0,0,0,0,0,0,0,KEY_WINDOWS,0,0,0,0,0,
 0,0,0,0,0,`/`,0,KEY_PRINTSCREEN,KEY_RIGHTALT,0,0,0,0,0,0,0,
 0,0,0,0,0,0,KEY_CTRLBREAK,KEY_HOME,KEY_UP,KEY_PAGEUP,0,KEY_LEFT,0,KEY_RIGHT,0,KEY_END,
 KEY_DOWN,KEY_PAGEDOWN,KEY_INSERT,KEY_DELETE,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
    };
/*——————————————————————————-*/
unsigned char pause_key;
unsigned char key_ascii;
unsigned char key_scan;
unsigned char keyflag[128];
static unsigned char caps;
static unsigned char cur_code,key_code;
static void (_interrupt far *_old_key_interrupt)(void);
/*——————————————————————————-*/
void setkeyspeed()
{
 REGS regs;
 regs.w.bx=0x0;
 regs.w.ax=0x0A05;
 int386 (0x16,&regs,&regs);
}
/*——————————————————————————-*/
void clearkey()
{
 int i;
 key_scan=0;
 key_ascii=0;
 memset(keyflag,0,sizeof(keyflag));
}
/*——————————————————————————-*/
static void interrupt key_interrupt(void)
{
static unsigned char specialflag;
unsigned char k,c,temp;
int i;
k=inp(0x60);
outp(0x61,(temp=inp(0x61))|0x80);
outp(0x61,temp);
if(k==0xe0)specialflag=1;
else if(k==0xe1)pause_key=1;
else
{
 if(k&0x80)
 {
  k&=0x7f;
  keyflag[k]=0;
 }
 else
 {
  key_code=cur_code;
  cur_code=key_scan=k;
  keyflag[k]=1;
  if(specialflag)c=specialnames[k];
  else
  {
   if(k==SCAN_caps)
   {
    caps=(~caps)&1;
   }
   if(SCAN_SHIFT)
   {
    c=shiftnames[k];
    if((c>=`A`)&&(c<=`Z`)&&caps)
    c+=`a`-`A`;
   }
   else
   {
    c=asciinames[k];
    if((c>=`a`)&&(c<=`z`)&&caps)
    c-=`a`-`A`;
   }
  }
  if(c)key_ascii=c;
 }
 specialflag=0;
}
outp(0x20,0x20);
}
/*——————————————————————————-*/
static void initkey(void)
{
 clearkey();
 setkeyspeed();
 _old_key_interrupt=_dos_getvect(9);
 _dos_setvect(9,key_interrupt);
}
/*——————————————————————————-*/
static void closekey(void)
{
 _dos_setvect(9,_old_key_interrupt);
}
/*——————————————————————————-*/
unsigned char getscan(void)
{
 unsigned char result;
 while((result=key_scan)!=0);
 key_scan=0;
 return(result);
}
/*——————————————————————————-*/
unsigned char getkey(void)
{
 unsigned char result;
 while((result=key_ascii)==0);
 key_ascii=0;
 return(result);
}
/*——————————————————————————-*/

20、任何情況下要注意除錯,WATCOM C/C++程式設計師可以用以下幾條函式除錯:
呼叫:
 debug(“test.dbg”,”i=%d
“,i);
 debug_print();

int debug_count=0;
struct debug_type
{
 char *fname[255];
 char *fcoment[255];
 int value[255];
}debug_i;

//除錯程式
void debug(char *file_name,char *coment,int debug_v)
{
 if (debug_count<255)
 {
  debug_i.fname[debug_count]=file_name;
  debug_i.fcoment[debug_count]=coment;
  debug_i.value[debug_count]=debug_v;
  debug_count++;
 }
}

//除錯寫盤程式
void debug_print(void)
{
 FILE *debug_file;
 int i;
 if (debug_count!=0)
 {
  for (i=0;i<=debug_count;i++)
  {
   debug_file=fopen(debug_i.fname[i],”a”);
   fprintf(debug_file,”debug[%3d]  “,i);
   fprintf(debug_file,debug_i.fcoment[i],debug_i.value[i]);
   fclose(debug_file);
  }
 }
 init_debug();
}

21、最後一條,寫不下去了,就不要寫了,遊戲開發是一個漫長的過程,沒有一天能寫成的遊戲,
至少我沒見過,寫得太累了,就玩去吧,千萬不要把自己的熱情消耗沒了,那樣的話,再簡單的
遊戲也寫不出來。

肖舸
1999.11.1.

===================================================
肖舸 《0bug-C/C++商用工程之道》
QQ:712123
MSN/Email:tonyxiaohome@hotmail.com
個人主頁:http://g.51cto.com/tonyxiaohome