小学期作业总结报告

游戏: flappy bird(草稿版)

游戏素材

  1. 在网上找了几张素材图片
  2. 将图片转成bmp格式
  3. 对图片进行一些处理,方便后面操作

代码部分

先放图:


小学期作业总结报告_第1张图片

大体来说,用到的函数就是这些

代码详解

#include 

#pragma comment(lib,"winmm.lib")        //调用PlaySound函数所需库文件
#pragma  comment(lib,"Msimg32.lib")     //添加使用TransparentBlt函数所需的库文件

#define WINDOW_WIDTH    450     //为窗口宽度定义的宏,以方便在此处修改窗口宽度
#define WINDOW_HEIGHT   650     //为窗口高度定义的宏,以方便在此处修改窗口高度
#define TUBE_WIDTH      52      //管道的宽度为52像素
#define TUBE_HEIGHT     320     //管道的高度为320像素
#define BIRD_WIDTH      48      //鸟的宽度
#define BIRD_HEIGHT     48      //鸟的高度
#define BIRD_X          100     //鸟的X坐标
#define GRAVITY         2       //重力加速度
#define PASSWAY         130     //管道中间距离130

这部分就是进行一些库的包括,以及宏的定义

int tube_x[3] = { WINDOW_WIDTH ,WINDOW_WIDTH + 167,WINDOW_WIDTH + 167 * 2 }, i; //管道左上角的水平位置
int randomNumber[3] = { 0 }, loopNumber = 0, score = 0, rgb[3] = {0};
int bird_y = 250, birdSpeed = 0;
HDC g_hdc = NULL, g_mdc = NULL, g_bufdc = NULL; //全局变量环境句柄和全局内存DC句柄
DWORD g_tPre = 0, g_tNow = 0, g_tGra = 0, g_tStart = 0,g_tRGB=0;
HBITMAP g_hBackground = NULL, g_hGround = NULL, g_hTubeDown = NULL, g_hTubeUp = NULL, g_hBird = NULL;

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//窗口过程函数
BOOL Game_Init(HWND hwnd);      //在此函数中进行资源的初始化
VOID Game_Paint(HWND hwnd);     //在此函数中进行绘图代码的书写
BOOL Game_CleanUp(HWND hwnd);   //在此函数中进行资源的清理

声明变量和函数

WinMain函数内部

    PlaySound("GameMedia\\8bitMusic.wav", NULL, SND_FILENAME | SND_ASYNC);

播放背景音乐

        WNDCLASSEX wndClass = { 0 };
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WndProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;
    wndClass.hIcon = (HICON)::LoadImage(NULL, "icon.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = "myWindow";

窗口类的设计

if (!RegisterClassEx(&wndClass))    //注册窗口
    {
        return -1;
    }

窗口类的注册

HWND hwnd = CreateWindow("myWindow", "GameBy1zhou",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
        WINDOW_HEIGHT, NULL, NULL, hInstance, NULL);    //创建窗口

窗口类的创建

MoveWindow(hwnd, 250, 80, WINDOW_WIDTH, WINDOW_HEIGHT, true);
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);

窗口的显示、更新

if (!Game_Init(hwnd))
    {
        MessageBox(hwnd, "资源初始化失败", "ERROR", 0); //使用MessageBox函数,创建一个消息窗口
        return FALSE;
    }

游戏的初始化

MSG msg = { 0 };                //定义并初始化msg
    while (msg.message != WM_QUIT)      //使用while循环,如果消息不是WM_QUIT消息,就继续循环
    {

        if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))   //查看应用程序消息队列,有消息时将队列中的消息派发出去。
        {
            TranslateMessage(&msg);     //将虚拟键消息转换为字符消息
            DispatchMessage(&msg);          //分发一个消息给窗口程序。
        }
        else
        {
            g_tNow = GetTickCount();   //获取当前系统时间
            if (g_tNow - g_tPre >= 20) //当此次循环运行与上次绘图时间相差0.02秒时再进行重绘操作
            {
                Game_Paint(hwnd);
            }
        }
    }

消息循环(这里我用的是PeekMessage,方便游戏画面的刷新)

UnregisterClass("myWindow", wndClass.hInstance);  //程序准备结束,注销窗口类

窗口类的注销

窗口过程函数

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{


    switch (message)
    {
    case WM_KEYUP:
        switch (wParam)
        {
        case VK_UP:
            birdSpeed = -3;
            break;
        }
    case WM_KEYDOWN:
        switch (wParam)
        {

        case VK_ESCAPE:
            Game_CleanUp(hwnd);
            DestroyWindow(hwnd);
            PostQuitMessage(0);

            break;
        }

        break;
    case WM_DESTROY:
        Game_CleanUp(hwnd);
        PostQuitMessage(0);

        break;

    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }

    return 0;
}

Game_Init函数

srand((unsigned)timeGetTime());

    for (i = 0; i < 3; i++)
    {
        randomNumber[i] = 80 + rand() % 240;
    }

管道长度的随机产生

HBITMAP bmp;

g_hdc = GetDC(hwnd);
g_mdc = CreateCompatibleDC(g_hdc); //内存DC
g_bufdc = CreateCompatibleDC(g_hdc); //缓存DC
bmp = CreateCompatibleBitmap(g_hdc, WINDOW_WIDTH, WINDOW_HEIGHT);

SelectObject(g_mdc, bmp);

三缓存(先把资源放进缓存DC中,然后在贴到内存DC上,所有资源全部贴好后,最后在直接把内存DC的画面直接贴到HDC)

g_hBackground = (HBITMAP)LoadImage(NULL, "GameMedia\\background.bmp", IMAGE_BITMAP, WINDOW_WIDTH, WINDOW_HEIGHT, LR_LOADFROMFILE);
g_hGround = (HBITMAP)LoadImage(NULL, "GameMedia\\ground.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
g_hTubeDown = (HBITMAP)LoadImage(NULL, "GameMedia\\tubeDown.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
g_hTubeUp = (HBITMAP)LoadImage(NULL, "GameMedia\\tubeUp.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
g_hBird = (HBITMAP)LoadImage(NULL, "GameMedia\\bird.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

加载游戏资源

HFONT hFont;
hFont = CreateFont(20, 0, 0, 0, 700, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("微软雅黑"));
SelectObject(g_mdc, hFont);
SetBkMode(g_mdc, TRANSPARENT);

创建字体

Game_Paint函数

(贴图部分我用的是透明色彩法 TransparentBlt )

SelectObject(g_bufdc, g_hBackground);
BitBlt(g_mdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, g_bufdc, 0, 0, SRCCOPY);

SelectObject(g_bufdc, g_hGround);
BitBlt(g_mdc, 0, 530, 225, 120, g_bufdc, 0, 0, SRCCOPY);
BitBlt(g_mdc, 225, 530, 225, 120, g_bufdc, 0, 0, SRCCOPY);

先贴上背景和地面(地面的素材图片长度不够,贴了两次)

for (i = 0; i < 3; i++)
    {
        SelectObject(g_bufdc, g_hTubeDown);
        TransparentBlt(g_mdc, tube_x[i], 0, TUBE_WIDTH, randomNumber[i], g_bufdc, 0, TUBE_HEIGHT- randomNumber[i], TUBE_WIDTH, randomNumber[i], RGB(255, 255, 255));

        SelectObject(g_bufdc, g_hTubeUp);
        TransparentBlt(g_mdc, tube_x[i], 130 + randomNumber[i], TUBE_WIDTH, 400 - randomNumber[i], g_bufdc, 0, 0, TUBE_WIDTH, 400 - randomNumber[i], RGB(255, 255, 255));

        if (BIRD_X >= tube_x[i] - BIRD_WIDTH + 10 && BIRD_X <= tube_x[i] + TUBE_WIDTH - 10)
        {
            //这里的加10、减10是因为图片本身边缘有一定的空隙,所以通过这样来使判断更精确一点 =_=
            if (bird_y <= randomNumber[i] - 10 || bird_y >= randomNumber[i] + PASSWAY - BIRD_HEIGHT + 10)
            {
                char result[100];

                PlaySound(NULL, NULL, SND_FILENAME); //结束播放音乐

                wsprintf(result, "GAME OVER!\n你的分数是:%d", score);
                MessageBox(hwnd, result, "游戏结束", 0);

                Game_CleanUp(hwnd);
                DestroyWindow(hwnd);
                PostQuitMessage(0);
            }
        }

        if (bird_y >= 530 - BIRD_HEIGHT + 10)
        {
            char result[100];

            PlaySound(NULL, NULL, SND_FILENAME);
            
            wsprintf(result, "GAME OVER!\n你的分数是:%d", score);
            MessageBox(hwnd, result, "游戏结束", 0);
            Game_CleanUp(hwnd);
            DestroyWindow(hwnd);
            PostQuitMessage(0);
        }

贴上管道、碰撞检测以及游戏结束相应的处理

if (tube_x[i] > -52)
    {
        tube_x[i] -= 1;
    }
    else if (tube_x[i] <= -52)
    {

        tube_x[i] = WINDOW_WIDTH;
        randomNumber[i] =80 + rand() % 240;
    }

管道的重复生成

g_tNow = GetTickCount();
    if (g_tNow - g_tGra >= 500)
    {
        birdSpeed += GRAVITY;
        g_tGra = GetTickCount();

    }

重力的模拟

SelectObject(g_bufdc, g_hBird);

    TransparentBlt(g_mdc, BIRD_X, bird_y, BIRD_WIDTH, BIRD_HEIGHT, g_bufdc, BIRD_WIDTH*loopNumber, 0, BIRD_WIDTH, BIRD_HEIGHT, RGB(0, 0, 0));


    if (loopNumber < 2)
    {
        loopNumber += 1;
    }
    else
    {
        loopNumber = 0;
    }

    bird_y += birdSpeed;

贴上小鸟

if (GetTickCount() - g_tRGB >= 200) //控制颜色的刷新时间
    {
        for (i = 0; i < 3; i++)
        {
            rgb[i] = rand() % 255;
        }

        g_tRGB = GetTickCount();
    }


    if (BIRD_X == tube_x[1]+TUBE_WIDTH || BIRD_X == tube_x[2] + TUBE_WIDTH || BIRD_X == tube_x[0] + TUBE_WIDTH)
    {
        score++;
    }

    char str[3][10];
    wsprintf(str[0], "得");
    wsprintf(str[1], "分");
    wsprintf(str[2], "%d", score);
    SetTextColor(g_mdc, RGB(rgb[0], rgb[1], rgb[2]));
    TextOut(g_mdc, 20, 20, str[0], strlen(str[0]));

    SetTextColor(g_mdc, RGB(rgb[1], rgb[2], rgb[0]));
    TextOut(g_mdc, 40, 20, str[1], strlen(str[1]));

    SetTextColor(g_mdc, RGB(rgb[2], rgb[1], rgb[0]));
    TextOut(g_mdc, 60, 20, str[2], strlen(str[2]));

得分的实时输出

BitBlt(g_hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, g_mdc, 0, 0, SRCCOPY);

将g_mdc的内容全部贴到g_hdc中去,将画面显示在窗口中

Game_CleanUp函数

DeleteObject(g_hBackground);
    DeleteObject(g_hBird);
    DeleteObject(g_hGround);
    DeleteObject(g_hTubeDown);
    DeleteObject(g_hTubeUp);
    

    ReleaseDC(hwnd, g_hdc);
    DeleteDC(g_mdc);
    DeleteDC(g_bufdc);

这里总结一下就是,如果是自己创建的,就需要删除;如果是跟系统要的,就需要释放

总结

  • win32窗口化编程
  • windows系统的消息机制
  • GDI

你可能感兴趣的:(小学期作业总结报告)