关闭

一步一步实现五子棋2

标签: 五子棋gdi+双缓存
664人阅读 评论(0) 收藏 举报

上一章我们实现了棋盘的绘制,现在来实现用鼠标下棋的功能吧。

首先添加一个Engine类,然后添加若干方法和成员,代码如下:

#ifndef FIVE_ENGINE_H

#define FIVE_ENGINE_H

 

#include <vector>

 

using namespacestd;

 

// 游戏状态

enum GAME_STATUS

{

   GAME_READY = 0,   // 游戏准备

   GAME_WAITING,     // 电脑等待中

   GAME_THINKING,    // 电脑思考中

   GAME_OVER         // 游戏结束

};

 

// 棋盘交叉点属性

enum CHESS_TYPE

{

   CHESS_SPACE = 0,    // 无棋子

   CHESS_BLACK,        // 此处有黑子

   CHESS_WHITE         // 此处有白子

};

 

// 棋盘交叉点结构 

struct CHESS_POINT

{

   int x;        // 水平方向位置

   int y;        // 垂直方向位置

   int type;     // 棋子类型

};

 

class Engine

{

public:

   Engine(void);

   ~Engine(void);

 

   void Init();

   void StartGame();

 

   // 用户落子

   BOOL UserDown(int x,inty);

 

   vector<CHESS_POINT*>getChessList() {returnm_chessList;}

   

private:

   int m_gameState;    // 游戏状态

   int m_userType;     // 用户持子类型 1表示持黑子 2表示持白子

   int m_AIType;       // 电脑持子类型

   CHESS_POINT m_chessTable[15][15];   // 棋盘状态

   vector<CHESS_POINT*>m_chessList;   // 已落子列表

};

#endif

 

以上基本满足了对棋盘和棋子状态的保存。接下来就要添加鼠标事件来完成落子了。

切换到资源视图,右键对话框,选择属性,右侧面板会出现属性框,点击消息,添加WM_LBUTTONUP事件,然后添加相应的处理代码:

void CFiveDlg::OnLButtonUp(UINTnFlags,CPointpoint)

{

   // TODO: 在此添加消息处理程序代码和/或调用默认值

 

   int x= (point.x+m_blockSize / 2 -m_left)/m_blockSize;

   int y= (point.y+m_blockSize / 2 -m_top)/m_blockSize;

 

   if (m_engine->UserDown(x,y))

   {

       // 重绘

       Invalidate();

   }

 

   CDialog::OnLButtonUp(nFlags,point);

}

 

Engine::UserDown的实现如下:

BOOL Engine::UserDown(intx,inty )

{

   // 不在棋盘上,返回

   if ((x> 14) || (x < 0) || (y > 14) || (y< 0))

   {

       return FALSE;

   }

 

   // 该位置有子,返回

   if (m_chessTable[x][y].type !=CHESS_SPACE)

   {

       return FALSE;

   }

 

   m_chessTable[x][y].type =m_userType;

   m_chessList.push_back(&m_chessTable[x][y]);

 

   return TRUE;

}

 

绘制棋子的函数要做相应的修改:

void CFiveDlg::DrawChess(Graphics *g)

{

   if ((m_whiteImg==NULL) || (m_blackImg==NULL))

   {

       return;

   }

 

   int chessLeft= m_left - m_blockSize/ 2;

   int chessTop= m_top - m_blockSize/ 2;

   int chessSize= m_blockSize;

 

   // 从m_engine中获取棋子信息,然后一个一个绘制

   vector<CHESS_POINT*>chessList =m_engine->getChessList();

   vector<CHESS_POINT*>::iteratoritor =chessList.begin();

   while (itor!=chessList.end())

   {

       if ((*itor)->type ==CHESS_WHITE)

       {

            g->DrawImage(m_whiteImg,chessLeft + (*itor)->x * m_blockSize,chessTop + (*itor)->y *m_blockSize,chessSize,chessSize);

       }

       else if((*itor)->type=CHESS_BLACK)

       {

            g->DrawImage(m_blackImg,chessLeft + (*itor)->x * m_blockSize,chessTop + (*itor)->y *m_blockSize,chessSize,chessSize);

       }

 

       itor++;

   }

}


  做好上面的一切后运行程序,看看效果吧。

看,已经能够用鼠标下棋了。可能有同学要问了,怎么都是黑子啊?不急不急,电脑现在智商为零,还不会下棋呢,后面我会让它越来越聪明的,人机对弈的日子不太遥远。

有一个问题,在运行时我发现每下一颗子,画面重绘时会有闪烁现象,我是绝对不能容忍这种伤眼睛的问题存在的,明天重点解决这个问题,不知道有没有高手能够提供解决方法。


继续昨天的问题,大家应该都想到了用双缓存就可以解决闪烁的问题,下面我对自己的绘制部分代码做下修改。

// 建立一块虚拟画布
        Bitmap bmp(m_width, m_height);
        Graphics bmpGraphics(&bmp);

        // 绘制棋盘
        DrawChessBoard(&bmpGraphics);


        // 绘制棋子
        DrawChess(&bmpGraphics);

        // 下面是在对话框上绘图了
        CPaintDC dc(this);
        Graphics graphics(dc.m_hDC);

        // 建立一个CacheBitmap用于快速绘图
        CachedBitmap cachedBmp(&bmp, &graphics);

        graphics.DrawCachedBitmap(&cachedBmp,0,0);  

  试一下效果,还是有闪烁现象抓狂。为了解决这个问题,在网上找了许多资料,经大神指点知道了原来调用Invalidate()之后会发出两个消息WM_ERASEBKGND和WM_PAINT。WM_ERASEBKGND消息会擦除背景,先擦除后描画导致了闪烁现象。解决方法是截获WM_ERASEBKGND,代码如下:

BOOL CFiveDlg::OnEraseBkgnd(CDC* pDC)
{
    // TODO: 在此添加消息处理程序代码和/或调用默认值
   
//return CDialog::OnEraseBkgnd(pDC);

  return TRUE;

}

再试一下效果,真的一点都不闪了!!!除此之外,调用Invalidate(FALSE)进行重绘的效果也一样。




0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:4934次
    • 积分:117
    • 等级:
    • 排名:千里之外
    • 原创:6篇
    • 转载:0篇
    • 译文:0篇
    • 评论:2条
    文章存档
    最新评论