65.9K
CodeProject 正在变化。 阅读更多。
Home

扑克游戏的 ActiveX 控件

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2002年7月13日

5分钟阅读

viewsIcon

68849

downloadIcon

3118

使用此控件,您可以轻松构建自己的扑克游戏。

Sample Image - screen.jpg

引言

这是一个用于扑克游戏的ActiveX控件。我为它添加了几个功能,使其能够满足扑克游戏中一张牌的大部分职责。由于它是一个ActiveX控件,实际上,它可以用于任何支持ActiveX的语言编写的程序中,例如VC++、VB……甚至网页。此外,通过我提供的代码,您可以轻松地对其进行修改以满足您的特定需求,当然,前提是您了解ActiveX控件。

关于该控件

控件类的每个实例代表一张牌。它具有以下用户可以操作的函数和属性。

函数

  • BOOL SetValue(short nNewValue):设置牌的值。值与牌的关系如下:
    spade 2, 3,..., 10, J, Q, K, A --> 1 ~ 13
    heart 2, 3,..., 10, J, Q, K, A --> 14 ~ 26
    club 2, 3,..., 10, J, Q, K, A --> 27 ~ 39
    diamond 2, 3,..., 10, J, Q, K, A --> 40 ~ 52
    small joker --> 53
    big joker --> 54
    (注意:如果我在扑克游戏中使用术语不当,请原谅我的英语,因为我不是母语人士。)
  • short GetValue():获取牌的值。
  • BOOL SetBackID(short nNewID):设置牌的背面图片。有两种图片可供选择,ID分别为1和2。
  • short GetBackID():获取当前背面图片的ID(1或2)。

属性

以上是添加到控件的属性。但是,您不能直接访问它们,除非在可视化设计时。您需要借助以下函数来操作。

  • BOOL GetBackSide():查看牌当前是否显示背面。TRUE表示显示背面,反之亦然。
  • void SetBackSide(BOOL):使牌显示背面或正面。
  • BOOL GetIsMovable():查看牌是否可以通过鼠标拖动。TRUE表示可以,反之亦然。
  • void SetIsMovable(BOOL):使牌可以或不可以被拖动。
  • BOOL GetIsReturn():查看牌拖动后是否可以返回到其原始位置。TRUE表示可以,反之亦然。
  • void SetIsReturn(BOOL):使牌可以或不可以返回到其原始位置。
  • BOOL GetIsSelected():查看牌当前是否被选中。被选中的牌周围会有一个红色边框。
  • void SetIsSelected(BOOL):使牌被选中或不被选中。

对于函数,您可以在程序中调用它们。对于属性,当然您也可以在程序中调用它们。此外,您可以在设计时设置它们的初始值,例如在资源编辑器中。当选择ActiveX控件的属性时,您会看到一些用于上述属性的复选框。您可以在那里设置初始值,最可能的是,稍后在代码中通过调用相应的GetXXX()SetXXX()方法来更改它们。

注意:我为控件添加了一个额外的功能。每当您用鼠标左键单击它时,它就会被选中,并以红色边框表示。

标准ActiveX事件

最后,我为控件添加了五个标准的ActiveX事件。它们是ClickDblClickMouseDownMouseMoveMouseUp。在您的程序中,您可以使用事件接收映射来处理任意数量的这些事件。例如,在我的测试程序中,我处理了MouseDown事件:当您用鼠标右键单击牌时,它会翻转。

一些编码细节

我根据牌的当前值绘制了牌。我在程序中保存了54张位图,并按如下方式以编程方式加载它们。

void CPokerCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, 
                                          const CRect& rcInvalid)
{
       // TODO: Replace the following code with your own drawing code.
    CBitmap* pOldBmp = NULL;
    CBitmap bmp;
    CDC dcMem;
    dcMem.CreateCompatibleDC(pdc);

    if(m_bBackSide)
    {
        if(GetBackID() == 1)
            bmp.LoadBitmap(IDB_BACKSIDE1);
        else
            bmp.LoadBitmap(IDB_BACKSIDE2);
    }
    else
    {
        bmp.LoadBitmap(IDB_BITMAP1 + GetValue() - 1);
    }
    pOldBmp = dcMem.SelectObject(&bmp);

    pdc->StretchBlt(0, 0, rcBounds.Width(), rcBounds.Height(),
                    &dcMem, 0, 0, 71, 96, SRCCOPY);
    dcMem.SelectObject(pOldBmp);

    if(m_bIsSelected)
    {
        CBrush* pOldBrush = (CBrush*)pdc->SelectStockObject(NULL_BRUSH);
        CPen pen(PS_SOLID, 4, RGB(192, 0, 0));
        CPen* pOldPen = pdc->SelectObject(&pen);

        CRect rect;
        GetClientRect(&rect);
        pdc->Rectangle(&rect);

        pdc->SelectObject(pOldBrush);
        pdc->SelectObject(pOldPen);
    }
}

为了移动牌,我处理了MouseDownMouseMoveMouseUp事件。请不要将它们与同名的标准ActiveX事件混淆。它们是不同的概念。

void CPokerCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    m_bIsSelected = TRUE;

    if(m_bIsMovable)
    {
        m_bIsMoving = TRUE;
        SetCapture();
        // get the mouse position relative to the ActiveX 
        //control's lefttop corner
        m_ptMouseInitPos = point;

        GetClientRect(&m_rtHomeRect);
        ::ClientToScreen(m_hWnd, &m_rtHomeRect.TopLeft());
        // get the screen coords of the ActiveX control's lefttop corner
        m_ptOldLeftTop = m_rtHomeRect.TopLeft();

        CRect rect;
        GetClientRect(&rect);
        MoveWindow(0, 0, rect.Width(), rect.Height(), TRUE);
        GetClientRect(&m_rtHomeRect);
        // get the screen coords of the container's lefttop corner
        ::ClientToScreen(m_hWnd, &m_rtHomeRect.TopLeft());
        ::ClientToScreen(m_hWnd, &m_rtHomeRect.BottomRight());

    }
    COleControl::OnLButtonDown(nFlags, point);
}

void CPokerCtrl::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    
    if(m_bIsMoving)
    {
        CPoint myPoint  = point;
        ClientToScreen(&myPoint);
        MoveWindow(myPoint.x - m_ptMouseInitPos.x - m_rtHomeRect.left, 
                   myPoint.y - m_ptMouseInitPos.y - m_rtHomeRect.top,
                   m_rtHomeRect.Width(), m_rtHomeRect.Height(), TRUE);
    }
    COleControl::OnMouseMove(nFlags, point);
}

void CPokerCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    if(m_bIsMoving)
    {
        ReleaseCapture();
        m_bIsMoving = FALSE;
        if(m_bIsReturn)
        {
            MoveWindow(m_ptOldLeftTop.x - m_rtHomeRect.left, 
                       m_ptOldLeftTop.y - m_rtHomeRect.top,
                       m_rtHomeRect.Width(), m_rtHomeRect.Height(), TRUE);
        }
    }
                       
    COleControl::OnLButtonUp(nFlags, point);
}

如何使用该控件

注册控件

与任何COM对象一样,ActiveX控件必须在宿主系统上注册后才能使用。如果您自己使用Visual C++构建控件,则在构建过程中会自动注册控件。如果您想直接使用该控件而不进行编译,则需要在系统上注册它,然后才能使用。以下是两种在系统上注册控件的方法。

第一种方法是编程注册控件。由于OCX是自注册的进程内COM服务器,程序可以像加载普通DLL一样加载OCX,找到其DllRegisterServer函数的地址,然后调用该函数。DllRegisterServer会注册OCX中的所有控件。以下代码演示了如果OCX名为Poker.ocx,如何完成此操作。

HINSTANCE hOcx = ::LoadLibrary (_T ("Poker.ocx"));
if (hOcx != NULL) 
{
    FARPROC lpfn = ::GetProcAddress (hOcx, _T ("DllRegisterServer"));
    if (lpfn != NULL)
        (*lpfn) ();    // Register the control(s).
    ::FreeLibrary (hOcx);
}

第二种方法是使用Visual C++附带的Regsvr32实用程序。如果Poker.ocx在当前目录中,在命令提示符窗口中键入以下命令将注册OCX的控件。

Regsvr32 Poker.ocx

同样,向Regsvr32传递/U开关可以注销OCX中的控件。

Regsvr32 /U Poker.ocx

使用它

您可以像添加普通控件一样将其添加到程序中,例如,将其添加到对话框中。您可以按任意大小调整它。您可以在属性菜单项中设置属性。稍后在代码中,您可以通过调用相应的函数来更改它们。

在我的测试程序中,我在对话框中添加了三个控件,并添加了三个成员变量,每个控件一个。

// in PokerTestDlg.h
CPoker    m_ctlPoker1;
CPoker    m_ctlPoker2;
CPoker    m_ctlPoker3;

// in PokerTestDlg.cpp
DDX_Control(pDX, IDC_POKER1, m_ctlPoker1);
DDX_Control(pDX, IDC_POKER2, m_ctlPoker2);
DDX_Control(pDX, IDC_POKER3, m_ctlPoker3);

我还添加了两个复选框来演示如何切换IsMovableIsReturn属性。

void CPokerTestDlg::OnCheckMovable() 
{
    // TODO: Add your control notification handler code here
    m_bMovable = !m_bMovable;
    m_ctlPoker1.SetIsMovable(m_bMovable);
    m_ctlPoker2.SetIsMovable(m_bMovable);
    m_ctlPoker3.SetIsMovable(m_bMovable);    
}

void CPokerTestDlg::OnCheckReturn() 
{
    // TODO: Add your control notification handler code here
    m_bReturn = !m_bReturn;
    m_ctlPoker1.SetIsReturn(m_bReturn);
    m_ctlPoker2.SetIsReturn(m_bReturn);
    m_ctlPoker3.SetIsReturn(m_bReturn);
}

在标准控件事件中,我只处理了MouseDown。我用它来确保一次最多只有一个牌被选中,并且右键单击它会翻转牌。当然,您可以选择处理更多事件以满足您的需求。

void CPokerTestDlg::OnMouseDownPoker1(short Button, 
                                 short Shift, long x, long y) 
{
    // TODO: Add your control notification handler code here
    m_ctlPoker1.SetIsSelected(TRUE);
    m_ctlPoker2.SetIsSelected(FALSE);
    m_ctlPoker3.SetIsSelected(FALSE);

    if(Button == MK_RBUTTON)
    {
        m_ctlPoker1.SetBackSide(!m_ctlPoker1.GetBackSide());
    }    
}

void CPokerTestDlg::OnMouseDownPoker2(short Button, 
                                 short Shift, long x, long y) 
{
    // TODO: Add your control notification handler code here
    m_ctlPoker1.SetIsSelected(FALSE);
    m_ctlPoker2.SetIsSelected(TRUE);
    m_ctlPoker3.SetIsSelected(FALSE);

    if(Button == MK_RBUTTON)
    {
        m_ctlPoker2.SetBackSide(!m_ctlPoker2.GetBackSide());
    }    
}

void CPokerTestDlg::OnMouseDownPoker3(short Button, 
                                    short Shift, long x, long y) 
{
    // TODO: Add your control notification handler code here
    m_ctlPoker1.SetIsSelected(FALSE);
    m_ctlPoker2.SetIsSelected(FALSE);
    m_ctlPoker3.SetIsSelected(TRUE);

    if(Button == MK_RBUTTON)
    {
        m_ctlPoker3.SetBackSide(!m_ctlPoker3.GetBackSide());
    }    
}

好了,到目前为止,我认为我已经涵盖了关于这个控件及其使用方法的大部分方面。您也可以在VB程序、网页等中使用它,尽管我个人尚未尝试过。

由于我假定读者对ActiveX控件有一定的了解,因此我省略了一些不必要的细节。对于那些不太熟悉ActiveX控件的读者,Jeff Prosise的《MFC编程》第二版最后一章是一个很好的学习资源。祝您使用愉快!

© . All rights reserved.