C图形化第一步

  之前的贪吃蛇都是在cmd下实现,每次都要调用cls刷新屏幕,简直是闪瞎了我的狗眼。

  度娘得知有一种方法可以避免闪烁,即:双缓冲。原理是先在内存中作图,然后将做好的图复制到前台,同时禁止背景刷新。

  主要使用函数:

    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

    int WINAPI WinMain(HINSTANCE, HINSTANCE,PSTR, int);

  涉及主要方法:

WNDCLASS wndclass; 
wndclass.style=CS_HREDRAW|CS_VREDRAW;//位置改变时重绘 
wndclass.lpfnWndProc=(WNDPROC)WndProc;//消息处理函数 
wndclass.hInstance=0;//当前实例句柄 
wndclass.hbrBackground=(HBRUSH)COLOR_WINDOWFRAME;//背景色 
wndclass.lpszClassName=szWindowClass;//参窗口类名 
wndclass.hIcon=0;//图标
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);//光标 
wndclass.lpszMenuName=0;//菜单名称 
wndclass.hIconSm=0;//最小化图标 
RegisterClassEx(&wndclass);//注册窗口类

  CreateWindow:

HWND CreateWindow(
LPCTSTR lpClassName,    // 如果lpClassName是一个字符串,它指定了窗口的类名
LPCTSTR lpWindowName,   // 指向一个指定窗口名的空结束的字符串指针,可使用lpWindowName来指定控制文本
DWORD dwStyle,          // 指定创建窗口的风格, WS_CAPTION:创建一个有标题框的窗口
                        // WS_SYSMENU:创建一个在标题条上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格
int x,    // 指定窗口的初始水平位置
        // 如果该参数被设为CW_USEDEFAULT则系统为窗口选择缺省的左上角坐标并忽略Y参数。
        // CW_USEDEFAULT只对层叠窗口有效,如果为弹出式窗口或子窗口设定,则X和y参数被设为零
int y,  // 
int nWidth, // 以设备单元指明窗口的宽度
            // 若nWidth是CW_USEDEFAULT,则系统为窗口选择一个缺省的高度和宽度:
            // 缺省宽度为从初始X坐标开始到屏幕的右边界,缺省高度为从初始Y坐标开始到目标区域的顶部。
            // CW_USEDEFAULT只对层叠窗口有效;如果为弹出式窗口和子窗口设定CW_USEDEFAULT标志则nWidth和nHeight被设为零。
int nHeight, //
HWND hWndParent, // 指向被创建窗口的父窗口或所有者窗口的句柄
HMENU hMenu,     // 菜单句柄,或依据窗口风格指明一个子窗口标识
HANDLE hlnstance, // 与窗口相关联的模块实例的句柄
LPVOID lpParam   // 指向一个值的指针,该值传递给窗口WM_CREATE消息
)
// 返回值:如果函数成功,返回值为新窗口的句柄:如果函数失败,返回值为NULL。

 

  将字符贪吃蛇迁移过来,gcc 编译时需加 -mwindows 参数,如:

  gcc snake.c -mwindows

  代码如下:  

#include <windows.h>
#define ID_TIMER 1
#define WIDTH 12     //
#define HEIGHT 8     //
#define DEBUG 0
const char FENCE ='*';    // 栅栏
const char HEAD  ='@';  // 蛇头
const char BODY  ='#';  // 蛇身
const char FOOD  ='O';  // 食物
const char BLANK =' ';  // 空白
char arr[HEIGHT][WIDTH];
struct Snake{
    int y,x;
}snake[WIDTH*HEIGHT];  // 结构体,保存坐标点
int len=0,food=0,key=0,score=0,alive=1,direct=4,speed=1,full=(WIDTH-2)*(HEIGHT-2);  // 长度、食物、按键、总分、存活、方向、速度
int uldr[5][2]={{},{-1,0},{0,-1},{1,0},{0,1}};  // 上、左、下、右

void init(){           // 初始化游戏地图
    int y=0,x=0,start_pos=HEIGHT/2;
    for(y=0;y<HEIGHT;y++)
        for(x=0;x<WIDTH;x++)
            if( y == 0 || y == HEIGHT-1 || x == 0 || x == WIDTH-1 ){
                    arr[y][x] = FENCE;
            }
            else{
                arr[y][x] = BLANK;
            }    
    snake[0].y=start_pos;
    snake[0].x=3;
    len++;
    snake[1].y=start_pos;
    snake[1].x=2;
    len++;    
    arr[ snake[0].y ][ snake[0].x ] = HEAD ;
    arr[ snake[1].y ][ snake[1].x ] = BODY ;    
}

void make_food(){
    int rx,ry;
    while(!food){
        rx = rand()%WIDTH;
        ry = rand()%HEIGHT;
        if(    arr[ry][rx] == BLANK ){
            arr[ry][rx]=FOOD;
            food=1;
            break;
        }
    }
}

void move(){
    int cnt=0;
    len++;  // 准备将当前位置放在snake数组首部
    if(DEBUG)printf("len:%d\n",len);
    for(cnt=len-1;cnt>0;cnt--){
        snake[cnt].x=snake[cnt-1].x;
        snake[cnt].y=snake[cnt-1].y;    // 1234 变为 51234
    }
    snake[0].y+=uldr[direct][0];
    snake[0].x+=uldr[direct][1];    // 移动蛇头
}

void check_head(){
    int y=snake[0].y;
    int x=snake[0].x;
    int i=0;
    int cnt=0;
    if(y < 1 || y > HEIGHT-2 || x < 1 || x > WIDTH-2){  // 是否越界
        alive=0;
    }
    if( arr[y][x] == BODY ){  // 是否吃到自己
        alive=0;
    }
    if( arr[y][x] == BLANK){
        arr[y][x] = HEAD;
    }
    if( arr[y][x] == FOOD ){  // 吃到食物
        arr[y][x] = HEAD;
        score++;
        len++;
        food=0;
        snake[len-1].y=snake[len-2].y;  // 蛇尾增加一节蛇身
        snake[len-1].x=snake[len-2].x;
        make_food();
    }
    if(DEBUG)printf("len:%d\n",len);
    if(DEBUG){
        for(;i<len;i++){
            printf("y,x:(%d,%d)\n",snake[i].y,snake[i].x);
        }
    }
    arr[ snake[len-1].y ][ snake[len-1].x ]=BLANK;    // 先清除蛇尾显示
    len--;
    for(cnt=1;cnt<=len-1;cnt++){
        arr[ snake[cnt].y ][ snake[cnt].x ]=BODY;   // 蛇身显示
    }
}

void handle(){
    init();
    make_food();
    while(alive && len<full){
        move();
        check_head();
        speed=(speed > 9)?9:(score/5+1);
        if(alive){
            Sleep(1000-speed*100);  // 多久刷新一次
        }
    }
    if( len == full){
        printf("congratulations!\n");
    }
    else{
        printf("you lose\n");
    }
}

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
            PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT ("snake") ;
    HWND        hwnd ;
    MSG        msg ;
    WNDCLASS    wndclass ;

    wndclass.style        = CS_HREDRAW | CS_VREDRAW ;
    wndclass.lpfnWndProc    = WndProc ;
    wndclass.cbClsExtra    = 0 ;
    wndclass.cbWndExtra    = 0 ;
    wndclass.hInstance    = hInstance ;
    wndclass.hIcon        = LoadIcon (NULL, IDI_APPLICATION) ;
    wndclass.hCursor    = LoadCursor (NULL, IDC_ARROW) ;
    wndclass.hbrBackground    = (HBRUSH) GetStockObject (BLACK_BRUSH) ;
    wndclass.lpszMenuName    = NULL ;
    wndclass.lpszClassName    = szAppName ;

    if(!RegisterClass (&wndclass))
    {
        MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ;
        return 0;
    }

    hwnd = CreateWindow (szAppName, TEXT("snake"),
                WS_CAPTION | WS_SYSMENU ,  //| WS_THICKFRAME,
                0, 0,
                240, 240,
                NULL, NULL, hInstance,
                NULL) ;

    ShowWindow (hwnd, SW_SHOW) ; //显示
    UpdateWindow (hwnd) ;
    //ShowCursor(FALSE); //隐藏鼠标光标
    
    while (GetMessage (&msg, NULL, 0, 0))
    {
        TranslateMessage (&msg) ;
        DispatchMessage (&msg) ;
    }
    ShowCursor(TRUE); //显示鼠标光标
    return msg.wParam ;
}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
      HDC      hdc ;
      static  HDC hdcMem;
      HFONT    hFont;
      static  HBITMAP hBitmap;
      static  int cxScreen, cyScreen; //屏幕的宽度 高度.
      static  int iFontWidth=10, iFontHeight=15; //字体的宽度 高度

      switch (message)
      {
    case WM_CREATE:
        cxScreen = GetSystemMetrics(SM_CXSCREEN) ; //屏幕宽度
        cyScreen = GetSystemMetrics(SM_CYSCREEN) ;

        hdc = GetDC(hwnd);
        hdcMem = CreateCompatibleDC(hdc);
        hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
        SelectObject(hdcMem, hBitmap);
        ReleaseDC(hwnd, hdc);
        //创建字体
        hFont = CreateFont(iFontHeight, iFontWidth-5, 0, 0, FW_NORMAL, 0, 0, 0,
                DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
                DRAFT_QUALITY, FIXED_PITCH | FF_SWISS, TEXT("Fixedsys"));
        SelectObject(hdcMem, hFont);
        DeleteObject (hFont) ;
        SetBkMode(hdcMem, TRANSPARENT); //设置背景模式为 透明
        init();
        make_food();

    case WM_TIMER:
        hdc = GetDC(hwnd);
        PatBlt (hdcMem, 0, 0, cxScreen, cyScreen, BLACKNESS) ; //将内存设备映像刷成黑色
        int y,x,size_s,size_p,size_l,size_w;
        TCHAR sText[64],pText[64],lText[64],wText[64];
        
        move();
        check_head();
        speed=(speed > 9)?9:(score/5+1);
        SetTimer (hwnd, ID_TIMER, (1000-speed*100), NULL) ;
        size_s = wsprintf( sText,TEXT("your score: %d"),score);   // 计算字符长度
        size_p = wsprintf( pText,TEXT("current speed: %d"),speed);
        size_l = wsprintf( lText,TEXT("you lose"));
        size_w = wsprintf( wText,TEXT("you win"));
        SetTextColor(hdcMem, RGB(220, 220, 220));   // 字体颜色
        
        TextOut(hdcMem, 0, 0 , sText, size_s);        // hdc、x坐标、y坐标、字符地址、字符长度
        TextOut(hdcMem, 0, 15, pText, size_p);
        if(alive && len<full){
            for(y=0 ; y<HEIGHT ; y++ ){
                for(x=0 ; x<WIDTH ; x++ ){               
                    TextOut(hdcMem, x*10, (y+2)*15, &arr[y][x], 1);
                }
            }
        }
        else{  
            for(y=0 ; y<HEIGHT ; y++ )
                for(x=0 ; x<WIDTH ; x++ )
                    if( y==0 || y==HEIGHT-1 || x==0 || x==WIDTH-1){               
                        TextOut(hdcMem, x*10, (y+2)*15, &arr[y][x], 1); 
                    }      
            if(alive == 0){
                TextOut(hdcMem, 30, (2+HEIGHT/2)*15,  lText, size_l);
            }else{
                TextOut(hdcMem, 30, (2+HEIGHT/2)*15,  wText, size_w);
            }
            BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY);
            KillTimer (hwnd, ID_TIMER) ;
        }
        BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY);
        ReleaseDC(hwnd, hdc);          
        return 0;

    case WM_RBUTTONDOWN:
        KillTimer (hwnd, ID_TIMER) ;
        return 0;

    case WM_RBUTTONUP:
        SetTimer (hwnd, ID_TIMER, 10, NULL) ;
        return 0;

    // 按下 w a s d 时
    case WM_CHAR:
        switch (wParam){
        case 'w' : direct=(direct == 3)?3:1;break;
        case 'a' : direct=(direct == 4)?4:2;break;
        case 's' : direct=(direct == 1)?1:3;break;
        case 'd' : direct=(direct == 2)?2:4;break;
        default : direct;
        }
        return 0;
    //处理善后工作
    case WM_DESTROY:
        KillTimer (hwnd, ID_TIMER) ;
        DeleteObject(hBitmap);
        DeleteDC(hdcMem);
        PostQuitMessage (0) ;
        return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}
View Code

 

posted @ 2015-06-26 15:20  葡萄不吐皮  阅读(219)  评论(0编辑  收藏  举报