我在做一个小游戏,界面被划分成许多格子(一个二维数组 ),人从一格走到旁边一格,有五张图片显示,也就是五桢动画,来模拟人的走动。通过按下键盘OnKeyDown,调用Invalidate(FALSE);,从而调用OnPaint函数进行界面重绘。如何让五张图片按顺序显示出来,不知道怎么弄?有人建议用Sleep()函数,也不知道具体怎么用,请求大家帮忙解答一下。
void CGameWnd::OnPaint()  
{
CPaintDC dc(this); // device context for painting
CDC dcMemory; // 内存设备CBitmap bitmap;
CRect m_rcClient;
  GetWindowRect(&m_rcClient);
// 与dc设备兼容
dcMemory.CreateCompatibleDC(&dc);// 使得bitmap与实际显示的设备兼容
bitmap.CreateCompatibleBitmap(&dc, m_rcClient.Width(),m_rcClient.Height());
  // 内存设备选择物件-位图
dcMemory.SelectObject(&bitmap);for (int i = 0; i<M_NUM_HEIGHT; i++)  
{
for (int j = 0; j<M_NUM_WIDTH; j++)  
{
switch(m_cMap[i][j])
{
case MAP_MAN:
DrawMan((CPaintDC&) dcMemory, i, j);break;

}
}其中的绘图函数是
void CGameWnd::DrawMan(CPaintDC &dc,int x, int y)
{
    CDC dcMemory; //用作内存设备
dcMemory.CreateCompatibleDC(&dc); //使得这个设备与dc兼容
if(walk == 0 )                 //静止状态
{
dcMemory.SelectObject(m_bmp); //将内存设备与位图资源关联
dc.StretchBlt(MAINFRAME_X+M_WID_HEIGHT*y, MAINFRAME_Y+M_WID_WIDTH*x, M_WID_HEIGHT, M_WID_WIDTH, 
&dcMemory, 0, M_WID_HEIGHT*2, M_WID_HEIGHT, M_WID_WIDTH, SRCCOPY);
}
else                    //需要走到,播放五张图片
{
dcMemory.SelectObject(m_walk);
switch(walk)
{
case 1:  //向右走
y = y - 36;
for(int i=0;i<5;i++)
{
                         dc.StretchBlt(MAINFRAME_X+M_WID_HEIGHT*y+i*9, MAINFRAME_Y+M_WID_WIDTH*x,          
                 M_WID_HEIGHT, M_WID_WIDTH, &dcMemory, M_WID_WIDTH*i, 0, M_WID_HEIGHT, M_WID_WIDTH, SRCCOPY);
//Sleep(200);
}

}

}
}

解决方案 »

  1.   

    #include "stdafx.h"
    #include "test.h"
    #include "testDlg.h"
    #include ".\testdlg.h"#ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    // 用于应用程序“关于”菜单项的 CAboutDlg 对话框
    //////////////////////////////////////////////////////////////////////////
    HBITMAP bit;
    HDC menDC;
    int w,h;
    CString dir;
    CString cc;
    int p=0;
    int x=0,y=0;
    int mx=0,my=0;
    //int q[4]={0,1,2,3};
    //////////////////////////////////////////////////////////////////////////
    BOOL Loadbmp(CString ss)
    {
    DeleteObject(bit);
    bit=(HBITMAP) LoadImage(
    AfxGetInstanceHandle(),
    ss,
    IMAGE_BITMAP,
    0,
    0,
    LR_LOADFROMFILE | LR_CREATEDIBSECTION
    );
    if (bit==NULL)
    {
    return NULL;
    }
    DIBSECTION ds;
    BITMAPINFOHEADER &dm=ds.dsBmih;
    GetObject(bit,sizeof(ds),&ds); w=dm.biWidth;
    h=dm.biHeight;
    return TRUE;
    }
    //////////////////////////////////////////////////////////////////////////
    void TransparentBlt2(HDC hdcDes,
     int nXDes,int nYDes,
     int nWDse,int nHDse,
     HDC hdcSrc,
     int nXSrc, int nYsrc,
     int nWDSrc,int nHSrc,
     UINT Tcol)
    {
    HBITMAP hBmp = CreateCompatibleBitmap(hdcDes,nWDse,nHDse);
    HBITMAP mBmp = CreateBitmap(nWDse,nHDse,1,1,NULL);
    HDC hDC = CreateCompatibleDC(hdcDes);
    HDC mDC = CreateCompatibleDC(hdcDes); HBITMAP oldBMP = (HBITMAP)SelectObject(hDC,hBmp);
    HBITMAP oldmBMP = (HBITMAP)SelectObject(mDC,mBmp); if (nWDse == nWDSrc && nHDse == nHSrc)
    {
    BitBlt(hDC,0,0,nWDse,nHDse,hdcSrc,nXSrc,nYsrc,SRCCOPY);

    else
    {
    StretchBlt(hDC,0,0,nWDse,nHDse,hdcSrc,nXSrc,nYsrc,nWDSrc,nHSrc,SRCCOPY);
    }
    SetBkColor(hDC,Tcol);
    BitBlt(mDC,0,0,nWDse,nHDse,hDC,0,0,SRCCOPY);
    //BitBlt(hdcDes,nXDes,nYDes,nWDse,nHDse,hDC,0,0,SRCCOPY); SetBkColor(hDC,RGB(0,0,0));
    SetTextColor(hDC,RGB(255,255,255)); BitBlt(hDC,0,0,nWDse,nHDse,mDC,0,0,SRCAND); SetBkColor(hDC,RGB(255,255,255));
    SetTextColor(hDC,RGB(0,0,0)); BitBlt(hdcDes,nXDes,nYDes,nWDse,nHDse,mDC,0,0,SRCAND);
    BitBlt(hdcDes,nXDes,nYDes,nWDse,nHDse,hDC,0,0,SRCPAINT); SelectObject(hDC,oldBMP);
    DeleteDC(hDC);
    SelectObject(mDC,oldmBMP);
    DeleteDC(mDC);
    DeleteObject(hBmp);
    DeleteObject(mBmp);
    }
    //////////////////////////////////////////////////////////////////////////class CAboutDlg : public CDialog
    {
    public:
    CAboutDlg();// 对话框数据
    enum { IDD = IDD_ABOUTBOX }; protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持// 实现
    protected:
    DECLARE_MESSAGE_MAP()
    };CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
    {
    }void CAboutDlg::DoDataExchange(CDataExchange* pDX)
    {
    CDialog::DoDataExchange(pDX);
    }BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
    END_MESSAGE_MAP()
    // CtestDlg 对话框CtestDlg::CtestDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CtestDlg::IDD, pParent)
    {
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }void CtestDlg::DoDataExchange(CDataExchange* pDX)
    {
    CDialog::DoDataExchange(pDX);
    }BEGIN_MESSAGE_MAP(CtestDlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_BN_CLICKED(IDOK, OnBnClickedOk)
    ON_WM_TIMER()
    END_MESSAGE_MAP()
    // CtestDlg 消息处理程序BOOL CtestDlg::OnInitDialog()
    {
    CDialog::OnInitDialog(); // 将\“关于...\”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
    CString strAboutMenu;
    strAboutMenu.LoadString(IDS_ABOUTBOX);
    if (!strAboutMenu.IsEmpty())
    {
    pSysMenu->AppendMenu(MF_SEPARATOR);
    pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
    }
    } // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE); // 设置大图标
    SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码
    menDC=CreateCompatibleDC(0);
    return TRUE;  // 除非设置了控件的焦点,否则返回 TRUE
    }void CtestDlg::OnSysCommand(UINT nID, LPARAM lParam)
    {
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
    CAboutDlg dlgAbout;
    dlgAbout.DoModal();
    }
    else
    {
    CDialog::OnSysCommand(nID, lParam);
    }
    }// 如果向对话框添加最小化按钮,则需要下面的代码
    //  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
    //  这将由框架自动完成。void CtestDlg::OnPaint() 
    {
    CPaintDC dc(this);
    if (IsIconic())
    {
     // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0); // 使图标在工作矩形中居中
    int cxIcon = GetSystemMetrics(SM_CXICON);
    int cyIcon = GetSystemMetrics(SM_CYICON);
    CRect rect;
    GetClientRect(&rect);
    int x = (rect.Width() - cxIcon + 1) / 2;
    int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标
    dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
    if (Loadbmp("02.bmp")==FALSE)
    {
    AfxMessageBox("没有");
    }
    SelectObject(menDC,bit);
    TransparentBlt2(dc.m_hDC,mx+150,my+150,w/4,h/4,menDC,x,y,w/4,h/4,RGB(255,255,255));
    CDialog::OnPaint();
    }
    }//当用户拖动最小化窗口时系统调用此函数取得光标显示。
    HCURSOR CtestDlg::OnQueryDragIcon()
    {
    return static_cast<HCURSOR>(m_hIcon);
    }void CtestDlg::OnBnClickedOk()
    {
    // TODO: 在此添加控件通知处理程序代码
    /*CClientDC d(this);
    menDC=CreateCompatibleDC(0);
    if(Loadbmp("02.bmp")==FALSE)
    {
    AfxMessageBox("没有");
    }
    SelectObject(menDC,bit);
    TransparentBlt2(d.m_hDC,0,0,w/4,h/4,menDC,0,0,w/4,h/4,RGB(255,255,255));
    CDialog::OnPaint();*/ SetTimer(1,100,NULL);
    OnTimer(NULL);// OnOK();
    }void CtestDlg::OnTimer(UINT nIDEvent)
    {
    // TODO: 在此添加消息处理程序代码和/或调用默认值
    Invalidate();
    /*x+=w/4;
    if (x==w)
    {
    x=0;
    }
    my++;*/
    switch (y)
    {
    case 0:
    x+=w/4;
    if (x==w)
    {
    x=0;
    }
    my++;
    if (my==100)
    {
    y+=h/4;
    }
    break;
    case 48:
    x+=w/4;
    if (x==w)
    {
    x=0;
    }
    mx--;
    if (mx==-100)
    {
    y+=h/2;
    }
    break;
    case 96:
    x+=w/4;
    if (x==w)
    {
    x=0;
    }
    mx++;
    if (mx==0)
    {
    y-=h/2;
    }
    break;
    case 144:
    x+=w/4;
    if (x==w)
    {
    x=0;
    }
    my--;
    if (my==0)
    {
    y-=h/4;
    }
    break;
    }
    CDialog::OnTimer(nIDEvent);
    }
      

  2.   

    一看帖子还以为是以前的,楼主你又发了啊= =||
    sleep函数百度一下很明了的算了我等会晚上把完整的代码写给你。
      

  3.   

    没办法,问题解决不了。就像我上面运用了Sleep(200)函数,图片也显示不了。而且播放完一张图片后显示另外一张图片时清楚前一张图片,Sleep函数也无法做到。
      

  4.   

    用Timer来做。用Sleep的话UI线程会阻塞住,看起来像死掉了
      

  5.   

    用Timer怎么做,我试了,也不行。我在设置了
    m_uTimer1 = SetTimer(ID_TIMER_EVENT1, 200, NULL);在OnTime函数中
    void CGameWnd::OnTimer(UINT nIDEvent) 
    {if (walk != 0 && nIDEvent == ID_TIMER_EVENT1)
    {

    m_ptPosition = GetPosition();
    CRect rcNum(m_ptPosition.x-36, m_ptPosition.y-36, 108, 108);
        InvalidateRect(rcNum);
    //Invalidate();//

    }
    else/**/
    CWnd::OnTimer(nIDEvent);
    }DrawMan函数改为
    void CGameWnd::DrawMan(CPaintDC &dc,int x, int y)
    {
        CDC dcMemory; //用作内存设备
    dcMemory.CreateCompatibleDC(&dc); //使得这个设备与dc兼容
    if(walk == 0 )
    {
    dcMemory.SelectObject(m_bmp); //将内存设备与位图资源关联
    dc.StretchBlt(MAINFRAME_X+M_WID_HEIGHT*y, MAINFRAME_Y+M_WID_WIDTH*x, M_WID_HEIGHT, M_WID_WIDTH, 
    &dcMemory, 0, M_WID_HEIGHT*2, M_WID_HEIGHT, M_WID_WIDTH, SRCCOPY);
    }
    else
    {
    dcMemory.SelectObject(m_walk);
    switch(walk)
    {
    case 1:
    y = y - 36;
    for(int i=num;i<num +1;i++)
    {
    dc.StretchBlt(MAINFRAME_X+M_WID_HEIGHT*y+i*9, MAINFRAME_Y+M_WID_WIDTH*x, M_WID_HEIGHT, M_WID_WIDTH, 
    &dcMemory, M_WID_WIDTH*i, 0, M_WID_HEIGHT, M_WID_WIDTH, SRCCOPY);
    if(num == 4)
    {
    num = 1;
    walk = 0;
    }
    }


    }

    }
    }
    运行结果还是一样啊!求解答
      

  6.   

    给楼主一个建议,所有画图的动作都在OnPaint里完成,所有逻辑处理在OnTimer中完成,例如SetTimer()设置一个比较短的时间,30毫秒,然后OnTimer中就每30毫秒处理一下逻辑,修改按时间跨度更新各种逻辑值,像人物的位置等等数据,然后OnPaint中就根据这些值画图。当然必须要双缓冲。
      

  7.   

    还有别忘了在OnTimer的最后写上Invalidate(FALSE);
      

  8.   


    200肯定是不够的,视觉停留需要的是24帧,也就是1/24秒,换算成sleep的参数是1000/24,所以起码要小于50才有效果,至于楼上说UI线程会死掉,我本来是建议多开一个线程专门绘图,但是看你的学习程序我觉得应该还没学线程,所以就没说。
      

  9.   

    下面是我刚写的已经通过VS2010测试的代码,是纯API,你直接复制都可以用的。void Inition(HWND hWnd)//初始化函数
    {
    hBmpMan[0]=(HBITMAP)LoadImage(NULL,_T("res\\Man0.bmp"),IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);//载入各种图片
    hBmpMan[1]=(HBITMAP)LoadImage(NULL,_T("res\\Man1.bmp"),IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);//一次载入5张人物图片
    hBmpMan[2]=(HBITMAP)LoadImage(NULL,_T("res\\Man2.bmp"),IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);
    hBmpMan[3]=(HBITMAP)LoadImage(NULL,_T("res\\Man3.bmp"),IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);
    hBmpMan[4]=(HBITMAP)LoadImage(NULL,_T("res\\Man4.bmp"),IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);
    hBmpBack=(HBITMAP)LoadImage(NULL,_T("res\\Back.bmp"),IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE);载入背景图片
    HDC hdcMem;//进行第一次绘图
    HDC hdcScr=GetDC(hWnd);
    hdcMem=CreateCompatibleDC(hdcScr);
    ::SelectObject(hdcMem,hBmpMan[0]);
    BitBlt(hdcScr,200,200,50,50,hdcMem,0,0,SRCCOPY);
    DeleteDC(hdcMem);
    DeleteDC(hdcScr);
    }void OnPaint(HWND hWnd,WPARAM wParam)//绘图函数
    {
    switch(wParam)
    {
    case VK_UP://如果是按的上这个按键
    {
    for(int i=0;i<5;i++)
    {
    HDC hdcMem;//创建内存DC
    HDC hdcScr=GetDC(hWnd);//创建兼容当前窗口的屏幕DC
    hdcMem=CreateCompatibleDC(hdcScr);//使内存DC兼容屏幕DC
    ::SelectObject(hdcMem,hBmpBack);//给内存DC一张白色背景(其他背景自己设定即可)
    BitBlt(hdcScr,0,0,500,500,hdcMem,0,0,SRCCOPY);//清屏
    HDC hdcMan;//创建人物DC
    hdcMan=CreateCompatibleDC(hdcMem);//让人物DC兼容内存DC
    ::SelectObject(hdcMan,hBmpMan[i]);//把人物的张图片随循环绑定到人物DC里面
    BitBlt(hdcMem,200,200-i*10,50,50,hdcMan,0,0,SRCCOPY);//绘制人物图片到内存DC里
    BitBlt(hdcScr,0,0,500,500,hdcMem,0,0,SRCCOPY);//讲内存DC的图片一次性画到屏幕上
    DeleteDC(hdcMan);//清理各种DC
    DeleteDC(hdcMem);
    DeleteDC(hdcScr);
    Sleep(50);//挂起50毫秒再继续,这样循环的速度就接近一秒24帧,有了动画的效果
    }
    }
    break;
    case VK_DOWN:
    break;
    case VK_LEFT:
    break;
    case VK_RIGHT:
    break;
    }}
    以上代码就可以实现了,但是为什么刷屏没有起作用,原来的图片留有尾巴,你自己看看吧,我之前做贪吃蛇都刷的很正常的,有点奇怪,MFC刷屏还是很方便的。至于你觉得这个程度还是不成动画,那只能说你的图片给的太少了,如果有24个图片(如果美工做的够好的话)一次用小于50毫秒的速度播放肯定是标准动画了。