MFC飞机大战开发之绘制图像

这一节来演示一种绘图的方法,让我们的程序真正的看得见~ 哈哈哈,而不再是命令行的黑框框。 采用的是二重缓冲的方式

先说一下当前工程的启动过程,省略一部分,直接看到我们的ChildView.cpp这个文件,里边有两个函数BOOL CChildView::PreCreateWindow(CREATESTRUCT& cs)void CChildView::OnPaint() ,可以简单的理解为,前者是窗口出现之间,程序所做的事情,所以你可以把游戏的一些初始化的操作放在这个函数里边,后者就是绘制这个窗口,每执行一次这个函数,就会重新绘制一下当前的窗口,你所看到的动画啊,游戏啊 都是通过一直执行这个函数实现的动画的效果的~,好啦~ 说了这么一堆话了,来开始贴上我们的飞机们把~

第一步:

找到ChildView.h 这个头文件,在里边添加一些我们所需要的属性,

// 特性
public:
    //绘图相关
    CRect m_client;//记录客户区大小
    CDC m_cacheDC;   //缓冲DC  
    CBitmap m_cacheCBitmap;//缓冲位图

然后转到ChildView.cpp这个文件,修改OnPaint函数

void CChildView::OnPaint() 
{
    CPaintDC dc(this); // 用于绘制的设备上下文

    // TODO: 在此处添加消息处理程序代码

    // 不要为绘制消息而调用 CWnd::OnPaint()

    //获取窗口DC指针  
    CDC *cDC = this->GetDC();
    //获取窗口大小  
    GetClientRect(&m_client);
    //创建缓冲DC  
    m_cacheDC.CreateCompatibleDC(NULL);
    m_cacheCBitmap.CreateCompatibleBitmap(cDC, m_client.Width(), m_client.Height());
    m_cacheDC.SelectObject(&m_cacheCBitmap);

    //————————————————————开始绘制——————————————————————  

    //把要绘制的内容都贴到m_cacheDC当中就可以了

    Manager.Draw(&m_cacheDC);

    //最后将缓冲DC内容输出到窗口DC中  
    cDC->BitBlt(0, 0, m_client.Width(), m_client.Height(), &m_cacheDC, 0, 0, SRCCOPY);

    //————————————————————绘制结束—————————————————————  

    //在绘制完图后,使窗口区有效  
    ValidateRect(&m_client);
    //释放缓冲DC  
    m_cacheDC.DeleteDC();
    //释放对象  
    m_cacheCBitmap.DeleteObject();
    //释放窗口DC  
    ReleaseDC(cDC);
    ReleaseDC(cDC);

}

我这段代码当中的Manager.Draw(&m_cacheDC); 就是把所有的内容都贴到缓冲DC当中(m_cachaeDC),Manager是一个类的实例对象,该类可以存储多个物体(比如:飞机,敌机,导弹等等),可以通过该类的Draw方法一次绘制所有的内容。 看到这里你可能说 哎呀 我不知道怎么写Manager这个类呀,我还是不知道怎么贴图啊,别着急~ 接下来我就给你们演示如何贴图~

我们试着新建一个类,就叫他Plane 类吧, 一个飞机有什么属性呢,嗯~ 有他的外观(贴图),长,宽,在游戏世界中还有一个属性是坐标,好,我们的类就长这个样子:

#pragma once
#include 
#include 

class Plane
{
private:
    CImage Image_;   //物体的贴图
    int Height_;     //物体的高度
    int Width_;      //物体的宽度
    CPoint Postion_; //物体的位置坐标

public:
    Plane(){};
    ~Plane(){};
};

CImage Image_ ; 就是储存物体的模样(外观)的,我们把一张图片放进去就好了~ 来看看怎么放一张图片呢

一般来讲是通过 CImage的load方法加载图片的,比如Image_.Load("Plane.png"); ,然而我们想要最后生成的exe文件可以独立的运行就需要把图片加载到我们的资源当中,那么如何从我们的资源文件中加载图片呢,是通过这样的一个函数做到的

//从资源加载图片 第一个是资源的名称,第二个是资源的类型
    BOOL LoadImageFromResource(UINT nResID, LPCTSTR lpTyp)
    {

        /*Image_.Destroy();*/


        // 查找资源
        HRSRC hRsrc = ::FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE(nResID), lpTyp);
        if (hRsrc == NULL) return false;

        // 加载资源
        HGLOBAL hImgData = ::LoadResource(AfxGetResourceHandle(), hRsrc);
        if (hImgData == NULL)
        {
            ::FreeResource(hImgData);
            return false;
        }

        // 锁定内存中的指定资源
        LPVOID lpVoid = ::LockResource(hImgData);

        LPSTREAM pStream = NULL;
        DWORD dwSize = ::SizeofResource(AfxGetResourceHandle(), hRsrc);
        HGLOBAL hNew = ::GlobalAlloc(GHND, dwSize);
        LPBYTE lpByte = (LPBYTE)::GlobalLock(hNew);
        ::memcpy(lpByte, lpVoid, dwSize);

        // 解除内存中的指定资源
        ::GlobalUnlock(hNew);

        // 从指定内存创建流对象
        HRESULT ht = ::CreateStreamOnHGlobal(hNew, TRUE, &pStream);
        if (ht != S_OK)
        {
            GlobalFree(hNew);
        }
        else
        {
            // 加载图片
            Image_.Load(pStream);

            GlobalFree(hNew);
        }
        // 释放资源
        ::FreeResource(hImgData);
        return true;
    }

如果是PNG图片的话,在通过这个函数把图片转为透明的(原本透明的部分)

//转换PNG图片为透明
    static void TransparentPNG(CImage *png)
    {
        if (png->GetBPP() == 32)
        {
            for (int i = 0; i < png->GetWidth(); i++)
            {
                for (int j = 0; j < png->GetHeight(); j++)
                {
                    unsigned char* pucColor = reinterpret_cast<unsigned char*>(png->GetPixelAddress(i, j));
                    pucColor[0] = pucColor[0] * pucColor[3] / 255;
                    pucColor[1] = pucColor[1] * pucColor[3] / 255;
                    pucColor[2] = pucColor[2] * pucColor[3] / 255;
                }
            }
        }
    }

最后 我们封装一个接口方便使用

    //从资源中加载图片
    void LoadImage(UINT nResID, LPCTSTR lpTyp = _T("PNG"))
    {
        LoadImageFromResource(nResID, lpTyp);
        if (lpTyp == _T("PNG"))
            TransparentPNG(&Image_);
    }
    //对象的绘制方法
    void Draw(CDC *cDC)
    {
        Image_.Draw(*cDC, Postion_.x, Postion_.y, Width_, Height_);
    }

好啦,现在生成一个实例对象去试试吧~


    //————————————————————开始绘制——————————————————————  

    //把要绘制的内容都贴到m_cacheDC当中就可以了

    plane.Draw(&m_cacheDC);
    //plane是Plane类的实例对象,记得先给他加载一张图片哦~
    //最后将缓冲DC内容输出到窗口DC中  
    cDC->BitBlt(0, 0, m_client.Width(), m_client.Height(), &m_cacheDC, 0, 0, SRCCOPY);

完成后运行一下看看吧,是不是小飞机出现在屏幕当中了呢

最后附上一个完整的飞机类

#pragma once
#include 
#include 

class Plane
{
private:
    CImage Image_;   //物体的贴图
    int Height_;     //物体的高度
    int Width_;      //物体的宽度
    CPoint Postion_; //物体的位置坐标

public:
    Plane()
    {
        LoadImage(IMG_Plane); // 让对象初始化的时候自动加载图片
        Height_ = 30;
        Width_ = 30;
        Postion_ = CPoint(Game_Width / 2, Game_Height);
    };
    ~Plane(){};

    //转换PNG图片为透明
    static void TransparentPNG(CImage *png)
    {
        if (png->GetBPP() == 32)
        {
            for (int i = 0; i < png->GetWidth(); i++)
            {
                for (int j = 0; j < png->GetHeight(); j++)
                {
                    unsigned char* pucColor = reinterpret_cast<unsigned char*>(png->GetPixelAddress(i, j));
                    pucColor[0] = pucColor[0] * pucColor[3] / 255;
                    pucColor[1] = pucColor[1] * pucColor[3] / 255;
                    pucColor[2] = pucColor[2] * pucColor[3] / 255;
                }
            }
        }
    }

    //从资源加载图片 第一个是资源的名称,第二个是资源的类型
    BOOL LoadImageFromResource(UINT nResID, LPCTSTR lpTyp)
    {

        /*Image_.Destroy();*/


        // 查找资源
        HRSRC hRsrc = ::FindResource(AfxGetResourceHandle(), MAKEINTRESOURCE(nResID), lpTyp);
        if (hRsrc == NULL) return false;

        // 加载资源
        HGLOBAL hImgData = ::LoadResource(AfxGetResourceHandle(), hRsrc);
        if (hImgData == NULL)
        {
            ::FreeResource(hImgData);
            return false;
        }

        // 锁定内存中的指定资源
        LPVOID lpVoid = ::LockResource(hImgData);

        LPSTREAM pStream = NULL;
        DWORD dwSize = ::SizeofResource(AfxGetResourceHandle(), hRsrc);
        HGLOBAL hNew = ::GlobalAlloc(GHND, dwSize);
        LPBYTE lpByte = (LPBYTE)::GlobalLock(hNew);
        ::memcpy(lpByte, lpVoid, dwSize);

        // 解除内存中的指定资源
        ::GlobalUnlock(hNew);

        // 从指定内存创建流对象
        HRESULT ht = ::CreateStreamOnHGlobal(hNew, TRUE, &pStream);
        if (ht != S_OK)
        {
            GlobalFree(hNew);
        }
        else
        {
            // 加载图片
            Image_.Load(pStream);

            GlobalFree(hNew);
        }
        // 释放资源
        ::FreeResource(hImgData);
        return true;
    }

    //从资源中加载图片
    void LoadImage(UINT nResID, LPCTSTR lpTyp = _T("PNG"))
    {
        LoadImageFromResource(nResID, lpTyp);
        if (lpTyp == _T("PNG"))
            TransparentPNG(&Image_);
    }
    //对象的绘制方法
    void Draw(CDC *cDC)
    {
        Image_.Draw(*cDC, Postion_.x, Postion_.y, Width_, Height_);
    }

};

你可能感兴趣的:(MFC飞机大战开发之绘制图像)