Chinese people are not willing! Let us work together to struggle for China's game career!
DOS game programming twenty-one
(THE 21 FAQ of PC DOS GAME)
I am in a game company for many years, now I will talk about my programming experience, I hope to help everyone, all examples in this article are debugged under Watcom C / C 10.6.
1, find a good programming language:
Of course, the game can be written in any language. This is certain, I used Turbo Basic to write a horse game, but also written a semi-finished network arch pig game, but a good programming language can reach good The effect, this is unquestionable. A game programmer, dreaming is a convenient, perfect, high-speed language.
Compilation is a high-speed language, but it is not convenient enough. If it is convenient, you must use a lot of macros, the author used a large number of speakers in the 6502 assembly language, all macro, but the corresponding memory overhead, time The overhead is increased, it is not cost-effective.
Watcom C / C is a good language, you can access large memory, fast, easy, but debugging is not convenient, you can only solve the problem with your write debug function. There is also a 32-bit environmental program that must call DOS / 4GW each time, which is not easy to be inconvenient and places.
MSC7.0 is also good, you can access big memory through its virtual memory mechanism, but unfortunately 16 simulation, the speed is too slow.
Djgpp is also very good, the key is shared, and there is also a shared game library of Alleg, very easy to use, recommended, but it generates the program code too large, not optimized.
As a game programmer, what we pursue is fast, just a little, hurry, if there is a faster language, I hope everyone will introduce it to me.
2, to write a proprietary program, don't write universal, universal, means slow, even the next time it is back, you can't delay this time. Similarly, all systems give you a function, call, have a resolutely unused idea to write a set.
3, the program written, you must brush a screen for more than 70 times per second, and then limit the clock to 30 times (not shake), the rest is the time, it is time to run your game program, count, not much .
4, there must be the engine concept, the engine contains the system underlayer, data structure, call method, etc. These directly limit your future games, generally, we have a game for half a year, two months old Two months of priests, the remaining two months of debugging, the visible engine is important. Always remember, you write the program, on the computer, it is to move, put a bunch of data, handle it to another place, it is so simple, then, how important you have, you know Let it be. 5, try to use a lot of time, floating point, if necessary, use shift multiplication. Here is an example of shift multiplication, you can refer to:
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * /
// xiaoge Made Under Watcom C / C 10.6
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------- * / int count_offest (int _width, int x, int y) // shift multiplication Calculate display offset value
{
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- = mod [i];
IF (_WIDTH <1) Break;
}
}
Return (Offest X);
}
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * /
Result = Screen width * y x. (This is just an example of shift multiplication. When it is truly used, it is necessary to choose according to the specific situation.
6, no one will use mathematical description to write a game graphic, all graphs come from the PCX, BMP graphics of the art paint, to find a group, or steal a batch of graphics. Read and write PCX, the function of BMP is required.
7, the game programmer quotations: give me a drawpoint function, I can describe the whole world. At any time, a high-speed painting point program is necessary, here is given an example (no multiplication):
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * /
// xiaoge Made Under Watcom C / C 10.6
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * /
Void Point (int X, int y, unsigned char color) // high speed drawing point
{
IF ((Color! = no_color) &&
(x> = 0) && (x (Y> = 0) && (Y { #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, the hollow algorithm is a lot, and all the And Mask Or, don't To handle it, every point is to handle twice, including three read memory, two logical operations, write memory, too slow, specify a transparent color in your color, do not depends on it when drawing points. (In an example). Remember, less processing at each point, you can do at least a circle dance. 9. The double buffer is necessary, but it is not all, and the unique method of the double buffer is to flash in many teaching books. This is wrong, because as long as the screen refresh cycle is tracked, it will not flash, double buffer directly It is your program drawing point must draw twice, once again to the buffer, another heavy buffer to the screen. When I do optimization, I often optimize the double buffer. There is no need to waste time, even if it is a bit flash, the game can sacrifice the effect, in exchange time. Here is a function of tracking the screen refresh cycle, as long as you add it before your brush program, the effect is basically possible. / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / // xiaoge Made Under Watcom C / C 10.6 / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / Void Wait (Void) // VGA screen refresh cycle test { While (INP (0x3Da) & 0x08); While (! (INP (0x3Da) & 0x08)); } / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / 10. Brush screen programs should include: background screen refresh, wizard animation refresh, mouse processing, keyboard processing, etc., and must run more than 70 times per second, if you can't do it, optimize your program. 11. Optimization is necessary, a game engine, at least 7-10 times, my engine is optimized 14 times, speed from 12.1 per second to 70 screen. Also, don't use the compiler's optimization, unless you think your users can't use your program. 12. Calculate your steps to use a few steps, this is especially important in C, because C is too convenient, concealing a lot of details, as in the following example: From * (VIDEO K) = * (p [1] j); K ; To * (Video (K )) = * (p [1] j); // Reduce a K-read memory operation To * (Video (K )) = * (* (p 1) j); // Reduce the conversion of p-converted into an array 13. Reduce the cycle, overhead over the loop once a accumulation (read / write memory), once compare (read memory 1 logic), as in the case: From for (i = 0; i <10000; i ) { * (p i) = 0; } To for (i = 0; i <10000; i = 10) // The number of cycles is reduced by 9000 times { * (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; } Go to for (i = 0; i <10000; i = 10) // 20 read variable memory reduction is 12 times, 1 write { 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; } Go to FOR (i = 0; i <1000; i = 10) // 10 read value memory is reduced to 1 time, the rest is register variable { J = P I; * (j 0) = * (j 1) = * (j 3) = * (j 4) = * (j 5) = * (j 6) = * (j 7) = * (j 8) = * (j 9) = 0; } Of course, if allowable, you can write 10,000, but there is no need to lose more than half. The key to reach a balance on speed and program capacity. In addition, DO ... While is less logic than for and while. 14. Specifically, when processing a tile, many people use x, y two cycles, which is worth studying. According to the screen characteristics, only the Y loop should only be retained, and the X direction is directly linearly accumulated. 15. Don't save the judgment statement, it may bring you a spending overhead, but it may reduce the overhead of hundreds of statements, 1 pay 100, gambling. 16, don't give yourself, develop a good writing habit, let the compiler check for you, as in the following example: IF (i == 1) Write into IF (i = 1) compile, but it means wrong Write into IF (1 = i) compile error, you can check out 17. The game program has no main loop. The main loop is often just a dead cycle including the brush screen. More Dongdong is put in the clock. To be skilled to intercept the clock, change its frequency, your picture will be smooth, nature . The following is an example of intercepting clocks because of the use of clock cycles, so you must use a large number of Switch / Case structures to prepare ideas. / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / // 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); // Note: The system input and output function cannot be called in the interrupt function, try to use your own program. Void_Interrupt Timer (Void) { Timer_Program (); // Call the user 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 / / Change the timer frequency function // Note: More than 1000 Hz, conflict with Windows Void change_timer (unsigned short new_count) { OUTP (Ctrl_8253, Ctrl_Word); OUTP (counter_0, low_byte); OUTP (Counter_0, Hi_Byte (New_COUNT)); } / / Install the clock Void install_timer (int HZ) { Short time_hz; Time_hz = short (1193180 / hz); Timer_counter = 0; Change_timer (TIME_HZ); OLD_TIME_IM = _dos_getvect (time_keeper_int); _dos_setvect (time_keeper_int, timer); } // Uninstall the clock Void uninstall_timer () { Change_timer (TIME_18HZ); _dos_setvect (time_keeper_int, old_time_isr); } / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / 18. Don't believe that the mouse program will do everything for you, read the status of 0x33, the cursor is displayed by yourself, otherwise, hey ... example: / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / // xiaoge Made Under Watcom C / C 10.6 / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / Unsigned short cursor [] = { 0x0000, / * 000000000000000000 * / / * 16 Words of Cursor Mask * / 0x4000, / * 0100000000000000 * / 0x6000, / * 0110000000000000 * / 0x7000, / * 0111000000000000 * / 0x7800, / * 0111100000000000 * / 0x7c00, / * 011110000000000 * / 0x7e00, / * 0111111000000000 * / 0x7f00, / * 0111111100000000 * / 0x7c00, / * 011110000000000 * / 0x4600, / * 0100011000000000 * / 0x0600, / * 0000011000000000 * / 0x0300, / * 0000001100000000 * / 0x0300, / * 0000001100000000 * / 0x0180, / * 0000000110000000 * / 0x0180, / * 0000000110000000 * / 0x00c0, / * 0000000011000000 * / } Struct Mouse { Char show; // MOUSE cursor display / not displayed Char left; // mouse left button Char right // mouse Right-click CHAR MIDDLE; / / MOUSE INT X; // mousex coordinates Int y; // mousey coordinates Unsigned char color; // mouse cursor color } 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 { 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.y = out.w.dx; IF (0> mouse.y) mouse.y = 0; IF (SCR_V IF (mouse.show) HARD_DISP_MOUSE (); } / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / 19. Keyboard operation To block the keyboard interrupt, you can't use the function given by the system, the game programmer rings: Any system is given, it is inevitable. The following is an example. When using, monitor Key_ASCII directly in your loop. / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / #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_Scrollock, '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 } / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / 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, '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 } / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / 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,13, key_rightctrl, 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 } / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------- * / 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. In any case, pay attention to debugging, Watcom C / C programmers can debug with the following functions: transfer: Debug ("Test.dbg", "I =% D / N", I); Debug_print (); INT debug_count = 0; Struct debug_type { Char * fname [255]; Char * fcoment [255]; Int value [255]; } debug_i; // Error 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 ; } } // Defective written program 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.fcome [i], debug_i.value [i]); Fclose (debug_file); } } INIT_DEBUG (); } 21, the last one, don't write, don't write, the game development is a long process, there is no game you can write, at least I have never seen it, I have been too tired, I will play, don't want it. I don't want to consume my enthusiasm. If you don't have a simple game. Point Studio (2001-2002)