《Windows游戏编程大师技巧》(第二版)第1章(下)

软件文摘 专栏收录该内容
4 篇文章 0 订阅

实例:FreakOut
在沉溺于所讨论的有关Windows、DirectX 和3D 图形之前,应当暂停一下,先给你看一个完整的游戏——虽然简单了一点,但毫无疑问是一个完整的游戏。你会看到一个实际的游戏循环和一些图形功能调用,最后一霎那就可以通过编译。不错吧?跟我来吧!
问题是我们现在才讲到第一章。我不应该使用后面章节中的内容……这有点像作弊,对吧?因此,我决定要做的是让你习惯于使用黑黑(black box)API来进行游戏编程。基于这个要求,我要提一个问题“要制作一个类似Breakout(打砖块)的2D游戏,其最低要求是什么?”我们真正所需要的是下面的功能:
? 在任意图像模式中切换
? 在屏幕上画各种颜色的矩形
? 获取键盘输入
? 使用一些定时函数同步游戏循环
? 在屏幕上画彩色文字串
因此我建了一个名为BLACKBOX.CPP|H的库。它封装了一套DirectX函数集(限于DirectDraw),并且包含实现所需功能的支持代码。妙处是,读者根本不需要看这些代码,只需依照函数原型来使用这些函数就可以了,并与BLACKBOX.CPP|H连接来产生.EXE可执行文件。
以BLACKBOX库为基础,我编写了一个名字为FreakOut的游戏,这个游戏演示了本章中所讨论的许多概念。FreakOut 游戏包含真正游戏的全部主要组成部分,包括:游戏循环、计分、关卡,甚至还有为球而写的迷你物理模型。真是可爱。图1.9 是一幅游戏运行中的屏幕画面。显然它比不上Arkanoid(经典的打砖块类游戏),但4 个小时的工作有此成果也不赖!
图1.9 FreakOut游戏的截屏
 
在阅读游戏源代码之前,我希望读者能看一下工程和游戏各组成部分是如何协调一致的。参见图1.10。
图1.10 FreakOut 的结构
 
从图中可以看到,游戏由下面文件构成:
FREAKOUT.CPP——游戏的主要逻辑,使用BLACKBOX.CPP,创建一个最小化的Win32应用程序。
BLACKBOX.CPP——游戏库(请不要偷看:)。
BLACKBOX.H——游戏库的头文件。
DDRAW.LIB——用于生成应用程序的DirectDraw输入库。其中并不含有真正的DirectX代码。它主要是用作让用户调用的中间库,然后轮流调用进行实际工作的DDRAW.DLL动态链接库。它可以在DirectX SDK 安装目录下的LIB子目录内被找到。
DDRAW.DLL——运行时(Runtime)的DirectDraw 库,实际上含有通过DDRAW.LIB 输入库调用DirectDraw 接口函数的COM 执行程序。不必为此担心;只要确认已经安装了DirectX运行时文件即可。
为了通过编译,需要将BLACKBOX.CPP和FREAKOUT.CPP加入工程里面,连接上DDRAW.LIB库文件,并确保BLACKBOX.H在头文件搜索路径或工作目录里,以便编译器可以正确地找到它。
现在我们已大致了解了FreakOut的结构。让我们看一下BLACKOUT.H头文件,看看它包含了哪些函数。
程序清单1.2 BLACKOUT.H 头文件
// BLACKBOX.H - Header file for demo game engine library

// watch for multiple inclusions
#ifndef BLACKBOX
#define BLACKBOX

// DEFINES

// default screen size
#define SCREEN_WIDTH    640  // size of screen
#define SCREEN_HEIGHT   480
#define SCREEN_BPP      8    // bits per pixel
#define MAX_COLORS      256  // maximum colors

// MACROS /

// these read the keyboard asynchronously
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
#define KEY_UP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)

// initializes a direct draw struct
#define DD_INIT_STRUCT(ddstruct) {memset(&ddstruct,0,sizeof(ddstruct));
ddstruct.dwSize=sizeof(ddstruct); }

// TYPES //

// basic unsigned types
typedef unsigned short USHORT;
typedef unsigned short WORD;
typedef unsigned char  UCHAR;
typedef unsigned char  BYTE;

// EXTERNALS //

extern LPDIRECTDRAW7         lpdd;             // dd object
extern LPDIRECTDRAWSURFACE7  lpddsprimary;     // dd primary surface
extern LPDIRECTDRAWSURFACE7  lpddsback;        // dd back surface
extern LPDIRECTDRAWPALETTE   lpddpal;          // a pointer dd palette
extern LPDIRECTDRAWCLIPPER   lpddclipper;      // dd clipper
extern PALETTEENTRY          palette[256];     // color palette
extern PALETTEENTRY          save_palette[256]; // used to save palettes
extern DDSURFACEDESC2        ddsd;    // a ddraw surface description struct
extern DDBLTFX               ddbltfx;           // used to fill
extern DDSCAPS2              ddscaps; // a ddraw surface capabilities struct
extern HRESULT               ddrval;            // result back from dd calls
extern DWORD                 start_clock_count; // used for timing
// these defined the general clipping rectangle
extern int min_clip_x,                             // clipping rectangle
           max_clip_x,
           min_clip_y,
           max_clip_y;

// these are overwritten globally by DD_Init()
extern int screen_width,                            // width of screen
           screen_height,                           // height of screen
           screen_bpp;                              // bits per pixel

// PROTOTYPES /

// DirectDraw functions
int DD_Init(int width, int height, int bpp);
int DD_Shutdown(void);
LPDIRECTDRAWCLIPPER DD_Attach_Clipper(LPDIRECTDRAWSURFACE7 lpdds,
                                      int num_rects, LPRECT clip_list);
int DD_Flip(void);
int DD_Fill_Surface(LPDIRECTDRAWSURFACE7 lpdds,int color);

// general utility functions
DWORD Start_Clock(void);
DWORD Get_Clock(void);
DWORD Wait_Clock(DWORD count);

// graphics functions
int Draw_Rectangle(int x1, int y1, int x2, int y2,
                   int color,LPDIRECTDRAWSURFACE7 lpdds=lpddsback);

// gdi functions
int Draw_Text_GDI(char *text, int x,int y,COLORREF color,
                  LPDIRECTDRAWSURFACE7 lpdds=lpddsback);
int Draw_Text_GDI(char *text, int x,int y,int color,
                  LPDIRECTDRAWSURFACE7 lpdds=lpddsback);

#endif
现在,不要花费太多时间绞尽脑汁研究这里的程序代码,搞清楚那些神秘的全局变量究竟表示什么并不重要。让我们来看一看这些函数。如你所想,这里有实现我们的简单图形界面所需的全部函数。基于这个图形界面和最小化的Win32 应用程序(我们要做的Windows 编程工作越少越好)的基础上,我创建了游戏FREAKOUT.CPP,如清单1.3 所示。请认真地看一看,尤其是游戏主循环和对游戏处理功能的调用。
程序清单1.3 FREAKOUT.CPP 源文件

// INCLUDES ///

#define WIN32_LEAN_AND_MEAN // include all macros
#define INITGUID            // include all GUIDs

#include         // include important windows stuff
#include
#include

#include        // include important C/C++ stuff
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include           // directX includes
#include "blackbox.h"       // game library includes

// DEFINES

// defines for windows
#define WINDOW_CLASS_NAME "WIN3DCLASS"  // class name

#define WINDOW_WIDTH            640     // size of window
#define WINDOW_HEIGHT           480

// states for game loop
#define GAME_STATE_INIT         0
#define GAME_STATE_START_LEVEL  1
#define GAME_STATE_RUN          2
#define GAME_STATE_SHUTDOWN     3
#define GAME_STATE_EXIT         4

// block defines
#define NUM_BLOCK_ROWS          6
#define NUM_BLOCK_COLUMNS       8

#define BLOCK_WIDTH             64
#define BLOCK_HEIGHT            16
#define BLOCK_ORIGIN_X          8
#define BLOCK_ORIGIN_Y          8
#define BLOCK_X_GAP             80
#define BLOCK_Y_GAP             32

// paddle defines
#define PADDLE_START_X          (SCREEN_WIDTH/2 - 16)
#define PADDLE_START_Y          (SCREEN_HEIGHT - 32);
#define PADDLE_WIDTH            32
#define PADDLE_HEIGHT           8
#define PADDLE_COLOR            191

// ball defines
#define BALL_START_Y            (SCREEN_HEIGHT/2)
#define BALL_SIZE                4

// PROTOTYPES /

// game console
int Game_Init(void *parms=NULL);
int Game_Shutdown(void *parms=NULL);
int Game_Main(void *parms=NULL);

// GLOBALS

HWND main_window_handle  = NULL; // save the window handle
HINSTANCE main_instance  = NULL; // save the instance
int game_state           = GAME_STATE_INIT; // starting state

int paddle_x = 0, paddle_y = 0; // tracks position of paddle
int ball_x   = 0, ball_y   = 0; // tracks position of ball
int ball_dx  = 0, ball_dy  = 0; // velocity of ball
int score    = 0;               // the score
int level    = 1;               // the current level
int blocks_hit = 0;             // tracks number of blocks hit

// this contains the game grid data

UCHAR blocks[NUM_BLOCK_ROWS][NUM_BLOCK_COLUMNS];

// FUNCTIONS //

LRESULT CALLBACK WindowProc(HWND hwnd,
                      UINT msg,
                            WPARAM wparam,
                            LPARAM lparam)
{
// this is the main message handler of the system
PAINTSTRUCT    ps;           // used in WM_PAINT
HDC            hdc;       // handle to a device context

// what is the message
switch(msg)
    {
    case WM_CREATE:
        {
    // do initialization stuff here
    return(0);
    }  break;

    case WM_PAINT:
         {
         // start painting
         hdc = BeginPaint(hwnd,&ps);

         // the window is now validated

         // end painting
         EndPaint(hwnd,&ps);
         return(0);
        }  break;

    case WM_DESTROY:
         {
           // kill the application
         PostQuitMessage(0);
         return(0);
           }  break;

    default:break;

    }  // end switch

// process any messages that we didn't take care of
return (DefWindowProc(hwnd, msg, wparam, lparam));

}  // end WinProc

// WINMAIN

int WINAPI WinMain(HINSTANCE hinstance,
            HINSTANCE hprevinstance,
            LPSTR lpcmdline,
            int ncmdshow)
{
// this is the winmain function

WNDCLASS winclass;  // this will hold the class we create
HWND     hwnd;         // generic window handle
MSG     msg;         // generic message
HDC      hdc;       // generic dc
PAINTSTRUCT ps;     // generic paintstruct
// first fill in the window class structure
winclass.style    = CS_DBLCLKS | CS_OWNDC |
                 CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WindowProc;
winclass.cbClsExtra        = 0;
winclass.cbWndExtra        = 0;
winclass.hInstance        = hinstance;
winclass.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor        = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground    = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName    = NULL;
winclass.lpszClassName    = WINDOW_CLASS_NAME;

// register the window class
if (!RegisterClass(&winclass))
    return(0);

// create the window, note the use of WS_POPUP
if (!(hwnd = CreateWindow(WINDOW_CLASS_NAME,    // class
        "WIN3D Game Console",    // title
        WS_POPUP | WS_VISIBLE,
        0,0,                    // initial x,y
        GetSystemMetrics(SM_CXSCREEN),  // initial width
        GetSystemMetrics(SM_CYSCREEN),  // initial height
        NULL,        // handle to parent
        NULL,        // handle to menu
        hinstance,   // instance
        NULL)))      // creation parms
return(0);

// hide mouse
ShowCursor(FALSE);

// save the window handle and instance in a global
main_window_handle = hwnd;
main_instance      = hinstance;

// perform all game console specific initialization
Game_Init();

// enter main event loop
while(1)
    {
    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
    // test if this is a quit
        if (msg.message == WM_QUIT)
           break;

    // translate any accelerator keys
    TranslateMessage(&msg);

    // send the message to the window proc
    DispatchMessage(&msg);
    } // end if
       // main game processing goes here
       Game_Main();

    }  // end while

// shutdown game and release all resources
Game_Shutdown();

// show mouse
ShowCursor(TRUE);

// return to Windows like this
return(msg.wParam);

}  // end WinMain

// T3DX GAME PROGRAMMING CONSOLE FUNCTIONS

int Game_Init(void *parms)
{
// this function is where you do all the initialization
// for your game

// return success
return(1);

}  // end Game_Init

///

int Game_Shutdown(void *parms)
{
// this function is where you shutdown your game and
// release all resources that you allocated

// return success
return(1);

}  // end Game_Shutdown

///

void Init_Blocks(void)
{
// initialize the block field
for (int row=0; row < NUM_BLOCK_ROWS; row++)
    for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
         blocks[row][col] = row*16+col*3+16;

}  // end Init_Blocks
///

void Draw_Blocks(void)
{
// this function draws all the blocks in row major form
int x1 = BLOCK_ORIGIN_X, // used to track current position
    y1 = BLOCK_ORIGIN_Y;

// draw all the blocks
for (int row=0; row < NUM_BLOCK_ROWS; row++)
    {
    // reset column position
    x1 = BLOCK_ORIGIN_X;

    // draw this row of blocks
    for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
        {
        // draw next block (if there is one)
        if (blocks[row][col]!=0)
            {
            // draw block
            Draw_Rectangle(x1-4,y1+4,
                 x1+BLOCK_WIDTH-4,y1+BLOCK_HEIGHT+4,0);

            Draw_Rectangle(x1,y1,x1+BLOCK_WIDTH,
                 y1+BLOCK_HEIGHT,blocks[row][col]);
            }  // end if

        // advance column position
        x1+=BLOCK_X_GAP;
        }  // end for col

    // advance to next row position
    y1+=BLOCK_Y_GAP;

    }  // end for row

}  // end Draw_Blocks

///

void Process_Ball(void)
{
// this function tests if the ball has hit a block or the paddle
// if so, the ball is bounced and the block is removed from
// the playfield note: very cheesy collision algorithm :)

// first test for ball block collisions

// the algorithm basically tests the ball against each
// block's bounding box this is inefficient, but easy to
// implement, later we'll see a better way

int x1 = BLOCK_ORIGIN_X, // current rendering position
    y1 = BLOCK_ORIGIN_Y;

int ball_cx = ball_x+(BALL_SIZE/2),  // computer center of ball
    ball_cy = ball_y+(BALL_SIZE/2);

// test of the ball has hit the paddle
if (ball_y > (SCREEN_HEIGHT/2) && ball_dy > 0)
   {
   // extract leading edge of ball
   int x = ball_x+(BALL_SIZE/2);
   int y = ball_y+(BALL_SIZE/2);

   // test for collision with paddle
   if ((x >= paddle_x && x <= paddle_x+PADDLE_WIDTH) &&
       (y >= paddle_y && y <= paddle_y+PADDLE_HEIGHT))
       {
       // reflect ball
       ball_dy=-ball_dy;

       // push ball out of paddle since it made contact
       ball_y+=ball_dy;

       // add a little english to ball based on motion of paddle
       if (KEY_DOWN(VK_RIGHT))
          ball_dx-=(rand()%3);
       else
       if (KEY_DOWN(VK_LEFT))
          ball_dx+=(rand()%3);
       else
          ball_dx+=(-1+rand()%3);

       // test if there are no blocks, if so send a message
       // to game loop to start another level
       if (blocks_hit >= (NUM_BLOCK_ROWS*NUM_BLOCK_COLUMNS))
          {
          game_state = GAME_STATE_START_LEVEL;
          level++;
          }  // end if

       // make a little noise
       MessageBeep(MB_OK);

       // return
       return;

       }  // end if

   }  // end if
// now scan thru all the blocks and see if ball hit blocks
for (int row=0; row < NUM_BLOCK_ROWS; row++)
    {
    // reset column position
    x1 = BLOCK_ORIGIN_X;

    // scan this row of blocks
    for (int col=0; col < NUM_BLOCK_COLUMNS; col++)
        {
        // if there is a block here then test it against ball
        if (blocks[row][col]!=0)
           {
           // test ball against bounding box of block
           if ((ball_cx > x1) && (ball_cx < x1+BLOCK_WIDTH) &&
               (ball_cy > y1) && (ball_cy < y1+BLOCK_HEIGHT))
               {
               // remove the block
               blocks[row][col] = 0;

               // increment global block counter, so we know
               // when to start another level up
               blocks_hit++;

               // bounce the ball
               ball_dy=-ball_dy;

               // add a little english
               ball_dx+=(-1+rand()%3);

               // make a little noise
               MessageBeep(MB_OK);

               // add some points
               score+=5*(level+(abs(ball_dx)));

               // that's it -- no more block
               return;

               }  // end if

           }  // end if

        // advance column position
        x1+=BLOCK_X_GAP;
        }  // end for col

    // advance to next row position
    y1+=BLOCK_Y_GAP;

    }  // end for row
}  // end Process_Ball

///

int Game_Main(void *parms)
{
// this is the workhorse of your game it will be called
// continuously in real-time this is like main() in C
// all the calls for your game go here!

char buffer[80]; // used to print text

// what state is the game in?
if (game_state == GAME_STATE_INIT)
    {
    // initialize everything here graphics
    DD_Init(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP);

    // seed the random number generator
    // so game is different each play
    srand(Start_Clock());

    // set the paddle position here to the middle bottom
    paddle_x = PADDLE_START_X;
    paddle_y = PADDLE_START_Y;

    // set ball position and velocity
    ball_x = 8+rand()%(SCREEN_WIDTH-16);
    ball_y = BALL_START_Y;
    ball_dx = -4 + rand()%(8+1);
    ball_dy = 6 + rand()%2;

    // transition to start level state
    game_state = GAME_STATE_START_LEVEL;

    }  // end if

else
if (game_state == GAME_STATE_START_LEVEL)
    {
    // get a new level ready to run

    // initialize the blocks
    Init_Blocks();

    // reset block counter
    blocks_hit = 0;

    // transition to run state
    game_state = GAME_STATE_RUN;
    }  // end if
///
else
if (game_state == GAME_STATE_RUN)
    {
    // start the timing clock
    Start_Clock();

    // clear drawing surface for the next frame of animation
    Draw_Rectangle(0,0,SCREEN_WIDTH-1, SCREEN_HEIGHT-1,200);

    // move the paddle
    if (KEY_DOWN(VK_RIGHT))
       {
       // move paddle to right
       paddle_x+=8;

       // make sure paddle doesn't go off screen
       if (paddle_x > (SCREEN_WIDTH-PADDLE_WIDTH))
          paddle_x = SCREEN_WIDTH-PADDLE_WIDTH;

       }  // end if
    else
    if (KEY_DOWN(VK_LEFT))
       {
       // move paddle to right
       paddle_x-=8;

       // make sure paddle doesn't go off screen
       if (paddle_x < 0)
          paddle_x = 0;

       }  // end if

    // draw blocks
    Draw_Blocks();

    // move the ball
    ball_x+=ball_dx;
    ball_y+=ball_dy;

    // keep ball on screen, if the ball hits the edge of
    // screen then bounce it by reflecting its velocity
    if (ball_x > (SCREEN_WIDTH - BALL_SIZE) || ball_x < 0)
       {
       // reflect x-axis velocity
       ball_dx=-ball_dx;

       // update position
       ball_x+=ball_dx;
       }  // end if
    // now y-axis
    if (ball_y < 0)
       {
       // reflect y-axis velocity
       ball_dy=-ball_dy;

       // update position
       ball_y+=ball_dy;
       }  // end if
   else
   // penalize player for missing the ball
   if (ball_y > (SCREEN_HEIGHT - BALL_SIZE))
       {
       // reflect y-axis velocity
       ball_dy=-ball_dy;

       // update position
       ball_y+=ball_dy;

       // minus the score
       score-=100;

       }  // end if

    // next watch out for ball velocity getting out of hand
    if (ball_dx > 8) ball_dx = 8;
    else
    if (ball_dx < -8) ball_dx = -8;

    // test if ball hit any blocks or the paddle
    Process_Ball();

    // draw the paddle and shadow
    Draw_Rectangle(paddle_x-8, paddle_y+8,
                   paddle_x+PADDLE_WIDTH-8,
                   paddle_y+PADDLE_HEIGHT+8,0);

    Draw_Rectangle(paddle_x, paddle_y,
                   paddle_x+PADDLE_WIDTH,
                   paddle_y+PADDLE_HEIGHT,PADDLE_COLOR);

    // draw the ball
    Draw_Rectangle(ball_x-4, ball_y+4, ball_x+BALL_SIZE-4,
                   ball_y+BALL_SIZE+4, 0);
    Draw_Rectangle(ball_x, ball_y, ball_x+BALL_SIZE,
                   ball_y+BALL_SIZE, 255);

    // draw the info
    sprintf(buffer,"F R E A K O U T           Score %d   //
          Level %d",score,level);
    Draw_Text_GDI(buffer, 8,SCREEN_HEIGHT-16, 127);
    // flip the surfaces
    DD_Flip();

    // sync to 33ish fps
    Wait_Clock(30);

    // check if user is trying to exit
    if (KEY_DOWN(VK_ESCAPE))
       {
       // send message to windows to exit
       PostMessage(main_window_handle, WM_DESTROY,0,0);

       // set exit state
       game_state = GAME_STATE_SHUTDOWN;

       }  // end if

    }  // end if
///
else
if (game_state == GAME_STATE_SHUTDOWN)
   {
   // in this state shut everything down and release resources
   DD_Shutdown();

   // switch to exit state
   game_state = GAME_STATE_EXIT;

   }  // end if

// return success
return(1);

}  // end Game_Main
哈哈,酷吧?这就是一个完整的Win32/DirectX游戏了,至少几乎是完整的了。BLACKOUT.CPP源文件中有好几百行代码,但是我们可以将其视为某人(我!)编写的DirectX的一部分。不管怎样说,还是让我们迅速浏览一下程序清单1.3的内容吧。
首先,Windows 需要一个事件循环。这是所有Windows程序的标准结构,因为Windows几乎完全是事件驱动的。但是游戏却不是事件驱动的,无论用户在干什么,它们都在一直运行。因此,我们至少需要支持小型事件循环以配合Windows。执行这项功能的代码位于WinMain()中。WinMain() 是所有Windows 程序的主要入口点,就好比main()是所有DOS/UNIX 程序中的入口点一样。FreakOut 的WinMain()创建一个窗口并进入事件循环。当Windows需要作某些工作时,就随它去。当所有的基本事件处理都结束时,调用Game_Main()。Game_Main是实际运行游戏程序的部分。
如果愿意的话,你可以不停地在Game_Main()中循环,而不释放回到WinMain()主事件循环体中。但这样做不是件好事,因为Windows会得不到任何信息。哎,我们该做的是让游戏在运行一帧时间的动画和逻辑之后,返回到WinMain()。这样的话,Windows可以继续响应和处理信息。如果所有这些听起来像是幻术的话,请不要担心——在下一章中情况还会更糟。
进入Game_Main()后,FreakOut的游戏逻辑开始被执行。游戏图像被渲染到一个不直接显示出来的工作缓冲区,尔后通过调用DD_FLIP()而在循环结束时在显示屏上显示出来。因此我希望你阅读一下全部的游戏状态,一行一行地过一遍一遍游戏循环的每一部分,了解工作原理。要启动游戏,只须双击FREAKOUT.EXE,游戏程序会立即启动。游戏控制方式如下:
右箭头键——向右移动挡板。
左箭头键——向左移动挡板。
Esc键——退回Windows。
还有,如果你错过一个球的话,将被罚掉100分,可要仔细盯紧啊!
如果你已经明白了游戏代码和玩法,不妨试着修改一下游戏。你可以增加不同的背景颜色(0~255 是有效的颜色)、增加更多的球、可以改变挡板的大小以及加上更多的声音效果(目前我只用到了Win32 API 中的MessageBeep()函数)
总结
这大概是我所写的最快的一章游戏编程入门教程了!我们提及了大量的基础内容,但是还只能算作是本书的缩略版本(就像印在封底的那样)。我只想让读者对本书中我们将学习和讨论的内容有一个感性认识。另外,阅读一个完整的游戏总是有益的,因为这带来许多需要读者思考的问题。
在进入第二章之前,请先确保你能够轻松编译FreakOut游戏。如果还不行的话,请立即翻开编译器的书并且RTFM(阅读那恼人的使用手册!)。我等着你们。

  • 0
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
编辑推荐 作为核心计算机技术成熟,完整的参考书籍,Primer Plus系列历经十数年不衰,因为它能够满足那些渴望通过全面理解相关技术继续深造的程序员和开发者的需要。通过学习C Primer Plus五版中文版,你将奠定坚实的C编程基础。 与以前的版本一样,作者的目标仍旧是为读者提供一本入门性、条理清晰、见解深刻的C语言教程。作者把编程概念和C语言的细节很好地融合在一起。通过大量短小精焊的范例演示一两个概念,为读者提供了很好的练习机会,有助于读者迅速掌握所讲的知识。每都提供了复习题和编程练习,进一步强调了重要的信息,有助于读者消化那些难于理解的概念。本书不仅适用于希望系统学习C语言编程的学生,也适用于那些精通其他高级语言编程,但渴望更好地掌握C语言这门核心语言的开发人员。 C Primer Plus五版中文版专门针对C语言的最新标准C99而作,它覆盖了这个新标准所引入的所有重要的新特性,并对读者关注的以几个主题提供了详尽的信息。 扩展的整数类型。 扩展的字符支持。 布尔类型的支持。 变长数组。 复合文字。 指定初始化项目。 扩展的计算支持。 内联函数。 内容简介 本书全面讲述了C语言编程的相关概念和知识。 全书共171、2学习C语言编程所需的预备知识。3到15介绍了C语言的相关知识,包括数据类型、格式化输入输出、运算符、表达式、流程控制语句、函数、数组和指针、字符串操作、内存管理、位操作等等,知识内容都针对C99标准;另外,10强化了对指针的讨论,12引入了动态内存分配的概念,这些内容更加适合读者的需求。1617讨论了C预处理器和C库函数、高级数据表示数据结构方面的内容。附录给出了各后面复习题、编程练习的答案和丰富的C编程参考资料。 本书适合希望系统学习C语言的读者,也适用于精通其他编程语言并希望进一步掌握和巩固C编程技术的程序员。 作者简介 Stephen Prata在加利福尼亚州的Kentfield的Marin学院教授天文学、物理学和程序设计课程。他在加州工业学院获得学士学位,从加州大学伯克利分校获得博士学位。他最早接触计算机,始于对星河的计算机建模。Stephen已经编写或与他人合作编写了十多本书。其中包括C++Primer Plus和Unix Prinmer Plus. 目录 1 概览 1.1 C语言的起源 1.2 使用C语言的理由 1.3 C语言的发展方向 1.4 计算机工作的基本原理 1.5 高级计算机语言和编译器 1.6 使用C语言的7个步骤 1.7 编程机制 1.8 语言标准 1.9 本书的组织结构 1.10 本书体例 1.11 总结 1.12 复习题 1.13 编程练习 2 C语言概述 2.1 C语言的一个简单实例 2.2 实例说明 2.3 一个简单程序的结构 2.4 使程序可读的技巧 2.5 更进一步 2.6 多个函数 2.7 调试 2.8 关键字和保留标识符 2.9 关键概念 2.10 总结 2.11 复习题 2.12 编程练习 3 数据和C 3.1 示例程序 3.2 变量与常量数据 3.3 数据:数据类型关键字 3.4 C数据类型 3.5 使用数据类型 3.6 参数和易犯的错误 3.7 另一个例子:转义序列 3.8 关键概念 3.9 总结 3.10 复习题 3.11 编程练习 4 字符串和格式化输入/输出 4.1 前导程序 4.2 字符串简介 4.3 常量和C预处理器 4.4 研究和利用printf和scanf 4.5 关键概念 4.6 总结 4.7 复习题 4.8 编程练习 5 运算符、表达式和语句 5.1 循环简介 5.2 基本运算符 5.3 其他运算符 5.4 表达式和语句 5.5 类型转换 5.6 带有参数的函数 5.7 一个示例程序 5.8 关键概念 5.9 总结 5.10 复习题 5.11 编程练习 6 C控制语句:循环 6.1 再探while循环 6.2 while语句 6.4 不确定循环与计数循环 6.5 for循环 6.6 更多赋值运算符:+=、-=、*=、/=和%= 6.7 逗号运算符 6.8 退出条件循环:do while 6.9 选择哪种循环 6.10 嵌套循环 6.11 数组 6.12 使用函数返回值的循环例子 6.13 关键概念 6.14 总结 6.15 复习题 6.16 编程练习 7 C控制语句:分支和跳转 7.1 if语句 7.2 在if语句中添加else关键字 7.3 获得逻辑性 7.4 一个统计字数的程序 7.5 条件运算符?: 7.6 循环辅助手段:continue和break 7.7 多重选择:switch和break 7.8 goto语句 7.9 关键概念 7.10 总结 7.11 复习题 7.12 编程练习 8 字符输入/输出和输入确认 8.1 单字符I/O:getchar和putchar 8.2 缓冲区 8.3 终止键盘输入 8.5 创建一个更友好的用户界面 8.6 输入确认 8.7 菜单浏览 8.8 关键概念 8.9 总结 8.10 复习题 8.11 编程练习 9 函数 9.1 函数概述 9.2 ANSI C的函数原型 9.3 递归 9.4 多源代码文件程序的编译 9.5 地址运算符:& 9.6 改变调用函数中的变量 9.7 指针简介 9.8 关键概念 9.9 总结 9.10 复习题 9.11 编程练习 10 数组和指针 10.1 数组 10.2 多维数组 10.3 指针和数组 10.4 函数、数组和指针 10.5 指针操作 10.6 保护数组内容 10.7 指针和多维数组 10.8 变长数组VLA 10.9 复合文字 10.10 关键概念 10.11 总结 10.12 复习题 10.13 编程练习 11 字符串和字符串函数 11.1 字符串表示和字符串I/O 11.2 字符串输入 11.3 字符串输出 11.4 自定义字符串输入/输出函数 11.5 字符串函数 11.6 字符串例子:字符串排序 11.7 ctype.h字符函数和字符串 11.8 命令行参数 11.9 把字符串转换为数字 11.10 关键概念 11.11 总结 11.12 复习题 11.13 编程练习 12 存储类、链接和内存管理 12.1 存储类 12.2 存储类说明符 12.3 存储类和函数 12.4 随机数函数和静态变量 12.5 掷骰子 12.6 分配内存:malloc和free 12.7 ANSI C的类型限定词 12.8 关键概念 12.9 总结 12.10 复习题 12.11 编程练习 13 文件输入/输出 13.1 和文件进行通信 13.2 标准I/O 13.3 一个简单的文件压缩程序 13.4 文件I/O:fprintf ( )、fscanf ( )、fgets ( )和fputs ( )函数 13.5 随机存取:fseek和ftell函数 13.6 标准I/O内幕 13.7 其他标准I/O函数 13.8 关键概念 13.9 总结 13.10 复习题 13.11 编程练习 14 结构和其他数据形式 14.1 示例问题:创建图书目录 14.2 建立结构声明 14.3 定义结构变量 14.4 结构数组 14.5 嵌套结构 14.6 指向结构的指针 14.7 向函数传递结构信息 14.8 把结构内容保存到文件中 14.9 结构:一步是什么 14.10 联合简介 14.11 枚举类型 14.12 typedef简介 14.13 奇特的声明 14.14 函数和指针 14.15 关键概念 14.16 总结 14.17 复习题 14.18 编程练习 15 位操作 15.1 二进制数、位和字节 15.2 其他基数 15.3 C的位运算符 15.4 位字段 15.5 关键概念 15.6 总结 15.7 复习题 15.8 编程练习 16 C预处理器和C库 16.1 翻译程序的一步 16.2 明显常量:#define 16.3 在#define中使用参数 16.4 宏,还是函数 16.5 文件包含:#include 16.6 其他指令 16.7 内联函数 16.8 C库 16.9 数学库 16.10 通用工具库 16.11 诊断库 16.12 string.h库中的memcpy和memmove 16.13 可变参数:stdarg.h 16.14 关键概念 16.15 总结 16.16 复习题 16.17 编程练习 17 高级数据表示 17.1 研究数据表示 17.2 从数组到链表 17.3 抽象数据类型ADT 17.4 队列ADT 17.5 用队列进行模拟 17.6 链表与数组 17.7 二叉搜索树 17.8 其他说明 17.9 关键概念 17.10 总结 17.11 复习题 17.12 编程练习 附录A 复习题答案 附录B 参考资料
作为核心计算机技术成熟、完整的参考书籍,Primer Plus系列历经十数年不衰,因为它能够满足那些渴望通过全面理解相关技术继续深造的程序员和开发者的需要。通过学习C Primer Plus(五版)中文版,你将奠定坚实的C编程基础。 与以前的版本一样,作者的目标仍旧是为读者提供一本入门性、条理清晰。见解深刻的C语言教程。作者把编程概念和C语言的细节很好地融合在一起。通过大量短小精悍的范例演示一两个概念,为读者提供了很好的练习机会,有助于读者迅速掌握所讲的知识。每都提供了复习题和编程练习,进一步强调了最重要的信息,有助于读者消化那些难于理解的概念。本书不仅适用于希望系统学习C语言编程的学生,也适用于那些精通其他高级语言编程,但渴望更好地掌握C语言这门核心语言的开发人员。 C Primer Plus(五版)中文版专门针对C语言的最新标准C99而作,它覆盖了这个新标准所引入的所有重要的新特性,并对读者所关注的以几个主题提供了详尽的信息: ● 扩展的整数类型。 ● 扩展的字符支持。 ● 布尔类型的支持。 ● 变长数组。 ● 复合文字。 ● 指定初始化项目。 ● 扩展的计算支持。 ● 内联函数。 本书全面讲述了C语言编程的相关概念和知识。 全书共171、2学习C语言编程所需的预备知识。3到15介绍了C语言的相关知识,包括数据类型、格式化输入输出、运算符、表达式、流程控制语句、函数、数组和指针、字符串操作、内存管理、位操作等等,知识内容都针对C99标准;另外,10强化了对指针的讨论,12引入了动态内存分配的概念,这些内容更加适合读者的需求。1617讨论了C预处理器和C库函数、高级数据表示(数据结构)方面的内容。附录给出了各后面复习题、编程练习的答案和丰富的C编程参考资料。 本书适合希望系统学习C语言的读者,也适用于精通其他编程语言并希望进一步掌握和巩固C编程技术的程序员。
作为核心计算机技术成熟,完整的参考书籍,Primer Plus系列历经十数年不衰,因为它能够满足那些渴望通过全面理解相关技术继续深造的程序员和开发者的需要。通过学习C Primer Plus五版中文版,你将奠定坚实的C编程基础。    与以前的版本一样,作者的目标仍旧是为读者提供一本入门性、条理清晰、见解深刻的C语言教程。作者把编程概念和C语言的细节很好地融合在一起。通过大量短小精焊的范例演示一两个概念,为读者提供了很好的练习机会,有助于读者迅速掌握所讲的知识。每都提供了复习题和编程练习,进一步强调了重要的信息,有助于读者消化那些难于理解的概念。本书不仅适用于希望系统学习C语言编程的学生,也适用于那些精通其他高级语言编程,但渴望更好地掌握C语言这门核心语言的开发人员。    C Primer Plus五版中文版专门针对C语言的最新标准C99而作,它覆盖了这个新标准所引入的所有重要的新特性,并对读者关注的以几个主题提供了详尽的信息。    扩展的整数类型。    扩展的字符支持。    布尔类型的支持。    变长数组。    复合文字。    指定初始化项目。    扩展的计算支持。    内联函数。 ************************************************************* 更多linux、ARM和C语言资源请参考: http://blog.csdn.net/arkofnoach/archive/2010/10/23/5960560.aspx
c primer plus虽然是一部很经典的书籍,但是他所讲述的内容是适用于大部分C的理论。基本上不会针对某一种编译器该怎么写,有一个比较清晰的代码。全书共171、2学习C语言编程所需的预备知识。3到15介绍了C语言的相关知识,包括数据类型、格式化输入输出、运算符、表达式、流程控制语句、函数、数组和指针、字符串操作、内存管理、位操作等等,知识内容都针对C99标准;另外,10强化了对指针的讨论,12引入了动态内存分配的概念,这些内容更加适合读者的需求。1617讨论了C预处理器和C库函数、高级数据表示(数据结构)方面的内容。附录给出了各后面复习题、编程练习的答案和丰富的C编程参考资料。 c primer plus(五版)中文版目录 1 概览 1.1 C语言的起源 1.2 使用C语言的理由 1.3 C语言的发展方向 1.4 计算机工作的基本原理 1.5 高级计算机语言和编译器 1.6 使用C语言的7个步骤 1.7 编程机制 1.8 语言标准 1.9 本书的组织结构 1.10 本书体例 1.11 总结 1.12 复习题 1.13 编程练习 2 C语言概述 2.1 C语言的一个简单实例 2.2 实例说明 2.3 一个简单程序的结构 2.4 使程序可读的技巧 2.5 更进一步 2.6 多个函数 2.7 调试 2.8 关键字和保留标识符 2.9 关键概念 2.10 总结 2.11 复习题 2.12 编程练习 3 数据和C 3.1 示例程序 3.2 变量与常量数据 3.3 数据:数据类型关键字 3.4 C数据类型 3.5 使用数据类型 3.6 参数和易犯的错误 3.7 另一个例子:转义序列 3.8 关键概念 3.9 总结 3.10 复习题 3.11 编程练习 4 字符串和格式化输入/输出 4.1 前导程序 4.2 字符串简介 4.3 常量和C预处理器 4.4 研究和利用printf和scanf 4.5 关键概念 4.6 总结 4.7 复习题 4.8 编程练习 5 运算符、表达式和语句 5.1 循环简介 5.2 基本运算符 5.3 其他运算符 5.4 表达式和语句 5.5 类型转换 5.6 带有参数的函数 5.7 一个示例程序 5.8 关键概念 5.9 总结 5.10 复习题 5.11 编程练习 6 C控制语句:循环 6.1 再探while循环 6.2 while语句 6.4 不确定循环与计数循环 6.5 for循环 6.6 更多赋值运算符:+=、-=、*=、/=和%= 6.7 逗号运算符 6.8 退出条件循环:do while 6.9 选择哪种循环 6.10 嵌套循环 6.11 数组 6.12 使用函数返回值的循环例子 6.13 关键概念 6.14 总结 6.15 复习题 6.16 编程练习 7 C控制语句:分支和跳转 7.1 if语句 7.2 在if语句中添加else关键字 7.3 获得逻辑性 7.4 一个统计字数的程序 7.5 条件运算符?: 7.6 循环辅助手段:continue和break 7.7 多重选择:switch和break 7.8 goto语句 7.9 关键概念 7.10 总结 7.11 复习题 7.12 编程练习 8 字符输入/输出和输入确认 8.1 单字符I/O:getchar和putchar 8.2 缓冲区 8.3 终止键盘输入 8.5 创建一个更友好的用户界面 8.6 输入确认 8.7 菜单浏览 8.8 关键概念 8.9 总结 8.10 复习题 8.11 编程练习 9 函数 9.1 函数概述 9.2 ANSI C的函数原型 9.3 递归 9.4 多源代码文件程序的编译 9.5 地址运算符:& 9.6 改变调用函数中的变量 9.7 指针简介 9.8 关键概念 9.9 总结 9.10 复习题 9.11 编程练习 10 数组和指针 10.1 数组 10.2 多维数组 10.3 指针和数组 10.4 函数、数组和指针 10.5 指针操作 10.6 保护数组内容 10.7 指针和多维数组 10.8 变长数组VLA 10.9 复合文字 10.10 关键概念 10.11 总结 10.12 复习题 10.13 编程练习 11 字符串和字符串函数 11.1 字符串表示和字符串I/O 11.2 字符串输入 11.3 字符串输出 11.4 自定义字符串输入/输出函数 11.5 字符串函数 11.6 字符串例子:字符串排序 11.7 ctype.h字符函数和字符串 11.8 命令行参数 11.9 把字符串转换为数字 11.10 关键概念 11.11 总结 11.12 复习题 11.13 编程练习 12 存储类、链接和内存管理 12.1 存储类 12.2 存储类说明符 12.3 存储类和函数 12.4 随机数函数和静态变量 12.5 掷骰子 12.6 分配内存:malloc和free 12.7 ANSI C的类型限定词 12.8 关键概念 12.9 总结 12.10 复习题 12.11 编程练习 13 文件输入/输出 13.1 和文件进行通信 13.2 标准I/O 13.3 一个简单的文件压缩程序 13.4 文件I/O:fprintf ( )、fscanf ( )、fgets ( )和fputs ( )函数 13.5 随机存取:fseek和ftell函数 13.6 标准I/O内幕 13.7 其他标准I/O函数 13.8 关键概念 13.9 总结 13.10 复习题 13.11 编程练习 14 结构和其他数据形式 14.1 示例问题:创建图书目录 14.2 建立结构声明 14.3 定义结构变量 14.4 结构数组 14.5 嵌套结构 14.6 指向结构的指针 14.7 向函数传递结构信息 14.8 把结构内容保存到文件中 14.9 结构:一步是什么 14.10 联合简介 14.11 枚举类型 14.12 typedef简介 14.13 奇特的声明 14.14 函数和指针 14.15 关键概念 14.16 总结 14.17 复习题 14.18 编程练习 15 位操作 15.1 二进制数、位和字节 15.2 其他基数 15.3 C的位运算符 15.4 位字段 15.5 关键概念 15.6 总结 15.7 复习题 15.8 编程练习 16 C预处理器和C库 16.1 翻译程序的一步 16.2 明显常量:#define 16.3 在#define中使用参数 16.4 宏,还是函数 16.5 文件包含:#include 16.6 其他指令 16.7 内联函数 16.8 C库 16.9 数学库 16.10 通用工具库 16.11 诊断库 16.12 string.h库中的memcpy和memmove 16.13 可变参数:stdarg.h 16.14 关键概念 16.15 总结 16.16 复习题 16.17 编程练习 17 高级数据表示 17.1 研究数据表示 17.2 从数组到链表 17.3 抽象数据类型ADT 17.4 队列ADT 17.5 用队列进行模拟 17.6 链表与数组 17.7 二叉搜索树 17.8 其他说明 17.9 关键概念 17.10 总结 17.11 复习题 17.12 编程练习
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值