wxWeights Demo学习(二)bombs

BombsCanvas

区域

class BombsCanvas : public wxPanel
{
public:

    // Constructor and destructor

    BombsCanvas(wxFrame *parent, BombsGame *game);

    void UpdateGridSize();

    wxSize GetGridSizeInPixels() const;

    virtual ~BombsCanvas();

private:

    void OnPaint(wxPaintEvent& event);
    void DrawField(wxDC *, int xc1, int yc1, int xc2, int yc2);
    void RefreshField(int xc1, int yc1, int xc2, int yc2);
    void Uncover(int x, int y);
    void OnMouseEvent(wxMouseEvent& event);
    void OnChar(wxKeyEvent& event);

    BombsGame *m_game;

    wxBitmap *m_bmp;

    // Cell size in pixels
    int m_cellWidth;
    int m_cellHeight;

    wxDECLARE_EVENT_TABLE();
};

红框区域就是Panel了,我理解是Frame(窗口)中内容部分。我们验证一下。
在这里插入图片描述
在这里插入图片描述
OnPaint里画红色矩形。果然!

构造函数

BombsCanvas::BombsCanvas(wxFrame *parent, BombsGame *game)
    : wxPanel(parent, wxID_ANY)
{
    m_game = game;
    int sx, sy;
    wxClientDC dc(this);
    dc.SetFont(BOMBS_FONT);

    wxCoord chw, chh;
    wxString buf = wxT("M");

    dc.GetTextExtent(buf, &chw, &chh);
    dc.SetFont(wxNullFont);

    dc.SetMapMode(wxMM_METRIC);

    int xcm = dc.LogicalToDeviceX(10);
    int ycm = dc.LogicalToDeviceY(10);
    // To have a square cell, there must be :
    //    sx*ycm == sy*xcm
    if (chw*ycm < chh*xcm)
    {
        sy = chh;
        sx = chh*xcm/ycm;
    }
    else
    {
        sx = chw;
        sy = chw*ycm/xcm;
    }

    m_cellWidth = (sx+3+X_UNIT)/X_UNIT;
    m_cellHeight = (sy+3+Y_UNIT)/Y_UNIT;
    dc.SetMapMode(wxMM_TEXT);
    m_bmp = NULL;
}

有点长,实际做的事情不多。

wxPanel(parent, wxID_ANY)

设置了Panel的父窗口,实际传入的就是Frame

m_game = game;

游戏逻辑

int sx, sy;
wxClientDC dc(this);
dc.SetFont(BOMBS_FONT);

wxCoord chw, chh;
wxString buf = wxT("M");

dc.GetTextExtent(buf, &chw, &chh);
dc.SetFont(wxNullFont);

dc.SetMapMode(wxMM_METRIC);

int xcm = dc.LogicalToDeviceX(10);
int ycm = dc.LogicalToDeviceY(10);
// To have a square cell, there must be :
//    sx*ycm == sy*xcm
if (chw*ycm < chh*xcm)
{
    sy = chh;
    sx = chh*xcm/ycm;
}
else
{
    sx = chw;
    sy = chw*ycm/xcm;
}

m_cellWidth = (sx+3+X_UNIT)/X_UNIT;
m_cellHeight = (sy+3+Y_UNIT)/Y_UNIT;
dc.SetMapMode(wxMM_TEXT);

这一整段就是在计算m_cellWidth m_cellHeight,从名字看就是每个格子的长、宽。

m_bmp = NULL;

准备一个图片做为绘制缓存。

OnPaint函数

void BombsCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
{
    wxPaintDC dc(this);

    const int numHorzCells = m_game->GetWidth();
    const int numVertCells = m_game->GetHeight();
    // Insert your drawing code here.
    if (!m_bmp)
    {
        wxSize size = dc.GetSize();
        m_bmp = new wxBitmap(size.GetWidth(), size.GetHeight());
        if (m_bmp)
        {
            wxMemoryDC memDC;
            memDC.SelectObject(*m_bmp);
            DrawField(&memDC, 0, 0, numHorzCells-1, numVertCells-1);
            memDC.SelectObject(wxNullBitmap);
        }
    }

    if (m_bmp)
    {
        wxMemoryDC memDC;
        memDC.SelectObject(*m_bmp);
        wxSize size = dc.GetSize();
        dc.Blit(0, 0, size.GetWidth(), size.GetHeight(),
            &memDC, 0, 0, wxCOPY);
      memDC.SelectObject(wxNullBitmap);
    }
    else
    {
        DrawField(&dc, 0, 0, numHorzCells-1, numVertCells-1);
    }
}
wxPaintDC dc(this);

这是当前绘制DC

const int numHorzCells = m_game->GetWidth();
const int numVertCells = m_game->GetHeight();

计算横竖有几个格子

if (!m_bmp)
{
    wxSize size = dc.GetSize();
    m_bmp = new wxBitmap(size.GetWidth(), size.GetHeight());
    if (m_bmp)
    {
        wxMemoryDC memDC;
        memDC.SelectObject(*m_bmp);
        DrawField(&memDC, 0, 0, numHorzCells-1, numVertCells-1);
        memDC.SelectObject(wxNullBitmap);
    }
}

如果 m_bmp (绘制的缓存图片)不存在,
计算当前绘制设备的大小
创建一个同样大小的Bitmap(位图)
保护性判断了一下位图创建成功没,没有就结束。
创建一个内存dc做为双缓冲
把位图绑定在内存dc上
通过DrawField在内存dc(缓存图片)上画游戏画面
资源清理
简言之,如果没有缓存,创建并把游戏内容画上去

if (m_bmp)
{
    wxMemoryDC memDC;
    memDC.SelectObject(*m_bmp);
    wxSize size = dc.GetSize();
    dc.Blit(0, 0, size.GetWidth(), size.GetHeight(),
        &memDC, 0, 0, wxCOPY);
  memDC.SelectObject(wxNullBitmap);
}
else
{
    DrawField(&dc, 0, 0, numHorzCells-1, numVertCells-1);
}

如果有缓存
创建一个内存dc做为双缓冲
把位图绑定在内存dc上
把内存dc拷贝到当前屏幕dc上
清理资源
如果没有缓存,直接在屏幕上画游戏画面
简言之,如果有缓存,把缓存画到屏幕上,如果还没有缓存,直接在屏幕上画

可见,核心的绘制流程在DrawField 函数里
另外,绘制流程使用一个位图缓存,正常情况下,每次刷新界面都是把这个缓存贴在屏幕上。
我们可以参考上述过程但不需要关注DrawField 怎么绘制游戏画面。我们的Demo按照自己的方式绘制就好。

OnMouseEvent

void BombsCanvas::OnMouseEvent(wxMouseEvent& event)
{
    const int gridWidth = m_game->GetWidth();
    const int gridHeight = m_game->GetHeight();

    wxCoord fx, fy;
    event.GetPosition(&fx, &fy);
    int x = fx/(m_cellWidth*X_UNIT);
    int y = fy/(m_cellHeight*Y_UNIT);
    if (x<gridWidth && y<gridHeight)
    {
        if ( (event.RightDown() || (event.LeftDown() && event.ShiftDown()))
           && (m_game->IsHidden(x,y)
               || !m_game->GetNumRemainingCells() ) )
        {
            // store previous and current field
            int prevFocusX = m_game->m_gridFocusX;
            int prevFocusY = m_game->m_gridFocusY;
            m_game->m_gridFocusX = x;
            m_game->m_gridFocusY = y;
            RefreshField(prevFocusX, prevFocusY, prevFocusX, prevFocusY);
            m_game->Mark(x, y);
            RefreshField(x, y, x, y);
            return;
        }
        else if (event.LeftDown() && m_game->IsHidden(x,y)
            && !m_game->IsMarked(x,y))
        {
            // store previous and current field
            int prevGridFocusX = m_game->m_gridFocusX;
            int prevGridFocusY = m_game->m_gridFocusY;
            m_game->m_gridFocusX = x;
            m_game->m_gridFocusY = y;
            RefreshField(prevGridFocusX, prevGridFocusY,
                prevGridFocusX, prevGridFocusY);
            Uncover(x, y);
            return;
        }
    }
}

代码有点多,而且多是游戏逻辑,重点在这里:

  m_game->Mark(x, y);
    RefreshField(x, y, x, y);

点击后让游戏逻辑处理点击位置,给出合理响应,刷新界面。

再来个图
在这里插入图片描述
游戏逻辑负责更新bitmap
绘制逻辑负责把bitmap刷到界面上

消息映射

BombsCanvas 声明中定义了

 wxDECLARE_EVENT_TABLE();

BombsCanvas 实现中定义了消息处理函数

wxBEGIN_EVENT_TABLE(BombsCanvas, wxPanel)
    EVT_PAINT(BombsCanvas::OnPaint)
    EVT_MOUSE_EVENTS(BombsCanvas::OnMouseEvent)
    EVT_CHAR(BombsCanvas::OnChar)
wxEND_EVENT_TABLE()
于是,绘制 鼠标 键盘消息分发到了特定函数。还发现一个Bug 键盘并没有相应,有机会研究一下。
至此,除了游戏逻辑,bombs的脉络已经理清楚了。我们可以参考实现自己的Demo了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值