前一段时间完成了蜘蛛纸牌的仿写,现将过程和思路记录下来

  首先,为了符合复用性,在win32的基本框架中,把可变的部分用c++封装起来成为一系列虚函数,这样如果再继续写游戏的话,只需要继承这个类就可以了

CGameApp.h

 #pragma once
class CGameApp //接口类
{
public:
virtual void OnCreatGame(){}
virtual void OnGameDraw(){}
virtual void OnGameRun(){}
virtual void OnKeyDown(){}
virtual void OnKeyUp(){}
virtual void OnLButtonDown(){}
virtual void OnLButtonUp(){}
virtual void OnMouseMove(){}
};
 #include<windows.h>
#include"CGameApp.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPreInstance,LPSTR pCmdLine,int nCmdShow)
{
// 1. 设计
WNDCLASSEX wndclass;
wndclass.cbClsExtra = ;
wndclass.cbWndExtra = ;
wndclass.cbSize = sizeof(wndclass);
wndclass.hbrBackground = (HBRUSH)COLOR_WINDOW;
wndclass.hCursor = ;
wndclass.hIcon = ;
wndclass.hIconSm = ; // 窗口左上的小图标
wndclass.hInstance = hInstance;
wndclass.lpfnWndProc = WndProc; // 窗口的消息处理函数
wndclass.lpszClassName = "cyc"; // 注册窗口类的名字
wndclass.lpszMenuName = ;
wndclass.style = CS_HREDRAW|CS_VREDRAW; // 2. 注册
if( ::RegisterClassEx(&wndclass) == FALSE)
{
::MessageBox(,"注册失败","提示",MB_OK);
return ;
}
// 3. 创建
HWND hwnd = ::CreateWindow("cyc","游戏壳",WS_OVERLAPPEDWINDOW,,,,,,,hInstance,);
if(hwnd == )
{
::MessageBox(,"创建失败","提示",MB_OK);
return ;
} // 4. 显示窗口
::ShowWindow(hwnd,SW_SHOW); // 5. 消息循环
MSG msg;
while(::GetMessage(&msg,,,))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg); // 分发, 调用消息的处理函数WndProc
} return ;
} CGameApp *p = ;
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
{
if(p == NULL)
p->OnCreatGame(); }
break;
case WM_PAINT:
{
if(p == NULL)
p->OnGameDraw(); }
break;
case WM_TIMER:
{
if(p == NULL)
p->OnGameRun(); }
break;
case WM_KEYDOWN:
{
if(p == NULL)
p->OnKeyDown(); }
break;
case WM_KEYUP:
{
if(p == NULL)
p->OnKeyUp(); }
break;
case WM_LBUTTONDOWN:
{
if(p == NULL)
p->OnLButtonDown(); }
break;
case WM_LBUTTONUP:
{
if(p == NULL)
p->OnLButtonUp(); }
break;
case WM_MOUSEMOVE:
{
if(p == NULL)
p->OnMouseMove(); }
break;
case WM_CLOSE: // 关闭
::PostQuitMessage(); // 发送一个退出的消息
break;
}
return DefWindowProc( hwnd, uMsg, wParam, lParam);
}

接下来就是 蜘蛛纸牌建设的过程了,先来分析一下纸牌的功能,因为蜘蛛纸牌里抛去大小王,所以1--K每副牌里有13张牌,由于我想搭建类似与纸牌类游戏框架的东西,所以分为可重写,和不可重写两个部分,不可重写的,所以类就设置为单张牌,一副牌,牌的排列,规则这些类,由于哪种游戏用几副牌,鼠标点击是否取牌,鼠标点击是否拿牌,这些有开发人员自行定义,UML如下,

在接下来分模块记录的时候也分为框架内,和框架外来进行记录

框架内:

在CCards和CPocker两个类中,均是简单的参数赋值,此处也是第一次使用STL中vector组件,在CCardsRank类中,每一张牌的属性都用结构体记录了下来,如图

并且在贴图的过程中,我多设置了一个判断来加载位图是否进入内存,为开发人员省去了加载位图的过程

 void CCardsRank::ShowRank(HDC hdc, HINSTANCE hIns)
{
//1==============================显示窗口的背景图============================
if(m_hBmpWndBack == )
m_hBmpWndBack = ::LoadBitmap(hIns,MAKEINTRESOURCE(IDB_WND_BACK)); HDC hMemDC = ::CreateCompatibleDC(hdc);
::SelectObject(hMemDC,m_hBmpWndBack);
::BitBlt(hdc,,,,,hMemDC,,,SRCCOPY);
::DeleteDC(hMemDC);
//1==============================显示窗口的背景图============================ //2==============================显示牌=====================================
//-------------有没有牌的背景图----------
if(m_hBmpCardsBack == )
m_hBmpCardsBack = ::LoadBitmap(hIns,MAKEINTRESOURCE(IDB_CARDS_BACK));
//-------------有没有牌的背景图----------
for(size_t i=;i<m_vecRank.size();i++)
{
list<Node*>::iterator ite = m_vecRank[i].begin();
while(ite != m_vecRank[i].end())
{
//----------贴图-------------------
HDC hMemDC = ::CreateCompatibleDC(hdc); if((*ite)->bflag == false)
::SelectObject(hMemDC,m_hBmpCardsBack);
else
::SelectObject(hMemDC,(*ite)->pCards->m_hBmpCards); ::BitBlt(hdc,(*ite)->x,(*ite)->y,,,hMemDC,,,SRCCOPY); ::DeleteDC(hMemDC);
//----------贴图------------------
++ite;
}
}
//2==============================显示牌=====================================
}

在CardsApp中,由于创建多少副牌是不确定的,那么就没法创建对象,在这里就使用了博客内记录的动态创建对象,只需要在CardsApp中贴上两个宏,开发人员就可以随意的创建多少副牌,在CardsApp中可以自动的去创建对象,而不用修改代码,并且重点标注的是,由于蜘蛛纸牌有松开鼠标归位的功能,所以在显示移动牌的时候,都是以牌的上一个位置为标准进行移动牌坐标的计算

 void CCardsApp::ShowCursorCards(HDC hdc)
{
int X = pointMouseMove.x - pointMouseDown.x;
int Y = pointMouseMove.y - pointMouseDown.y; // 在 移动的距离的位置显示牌
list<Node*>::iterator ite = m_lstCursorCards.begin();
while(ite != m_lstCursorCards.end())
{
HDC hMemDC = ::CreateCompatibleDC(hdc);
::SelectObject(hMemDC,(*ite)->pCards->m_hBmpCards);
::BitBlt(hdc,(*ite)->x+X,(*ite)->y+Y,,,hMemDC,,,SRCCOPY);
::DeleteDC(hMemDC);
++ite;
}
}

在CRule中,进行三个判断,第一个接收牌后的操作,利用vector自身的计数函数,以及遍历链表,通过是否接收牌这个规则之后,与链表结合,更新位置,翻牌,第二个是获得鼠标点击牌的坐标,在获得之前也需要进行一系列的判断,是否光标点击在牌上,牌是否是正面,是不是最后一张能否拿起来,这些都为真之后,将牌放入光标移动的链表中,在这一步值得一提的是,运用了反向迭代器,正向迭代器比反向迭代器指向少一个元素,所以在删迭代器指向元素前,反向迭代器++,或者转为正向迭代器后--  ,第三个,如果接收失败的话,将光标链表中的牌放回原先列表的尾部

 bool CRule::ReceiveCards(POINT point, CCardsRank* pCardsRank, list<Node*>& lstCursor)
{
// 遍历 所有的链表
for(size_t i=;i<pCardsRank->m_vecRank.size();i++)
{
// 判断坐标的 交给子类
if(this->IsReceiveCardsRule(point,i,pCardsRank,lstCursor) == true)
{
// 和 i这个链表结合
pCardsRank->m_vecRank[i].splice(pCardsRank->m_vecRank[i].end(),lstCursor);
// 更新位置(对齐)
this->UpDatePos(pCardsRank,i);
// 翻牌
if(pCardsRank->m_vecRank[m_nGetCardsListID].empty() == false)
pCardsRank->m_vecRank[m_nGetCardsListID].back()->bflag = true;
m_nGetCardsListID = -;
return true;
}
}
return false;
}
void CRule::GetCards(POINT point, CCardsRank* pCardsRank, list<Node*>& lstCursor)
{
// 遍历 所有的链表
for(size_t i=;i<pCardsRank->m_vecRank.size();i++)
{
// 遍历 i 个链表的所有节点
list<Node*>::reverse_iterator rev_ite = pCardsRank->m_vecRank[i].rbegin();
while(rev_ite != pCardsRank->m_vecRank[i].rend())
{
// 判断光标是否点击到这个牌上
if(point.x >= (*rev_ite)->x && point.x <= (*rev_ite)->x+
&& point.y >= (*rev_ite)->y && point.y <= (*rev_ite)->y+)
{
// 判断是不是正面
if((*rev_ite)->bflag == true)
{
// 判断能不能拿起来
list<Node*>::iterator ite = --(rev_ite.base());
if( this->IsGetCardsRule(pCardsRank,i,ite) == true)
{
// 记录下标
m_nGetCardsListID = i;
// 放到光标的链表上
lstCursor.splice(lstCursor.end(),pCardsRank->m_vecRank[i],ite,pCardsRank->m_vecRank[i].end());
}
}
return;
}
++rev_ite;
}
}
}
void CRule::RevertCards(CCardsRank* pCardsRank, list<Node*>& lstCursor)
{
if(m_nGetCardsListID != -)
{
// 把光标的链表 放回到 m_nGetCardsListID 这个链表尾部
pCardsRank->m_vecRank[m_nGetCardsListID].splice(pCardsRank->m_vecRank[m_nGetCardsListID].end(),lstCursor);
m_nGetCardsListID = -;
}
}

框架外:

针对于蜘蛛纸牌而言,难点在于规则的制定上,在CMyCardsRank中需要注意的点就是,这个类的构造应该使用初始化列表来写,初始化列表的作用1.初始化成员属性 2.先完成指定类的构造,也就是说没有CCardsRank这个类,哪来的CMyCardsRank呢?

CMyCardsRank::CMyCardsRank(void):CCardsRank()

并且在CCardsApp中的显示应该用双缓冲来完成,因为只要连续贴的图超过一张,就有可能多张图出现在两个显卡刷新周期之内,这样的话就会出现闪屏的问题,所以利用双缓冲再为一个兼容性DC在创建一个兼容性DC,多次贴图在第一个兼容性DC中,最后一次性显示到窗口HDC中,这就是解决窗口闪烁的双缓冲技术

 void CCardsApp::OnGameDraw()                 //  WM_PAINT
{
HDC dc = ::GetDC(m_hMainWnd);
HDC hdc = ::CreateCompatibleDC(dc);
HBITMAP hbitmap = ::CreateCompatibleBitmap(dc,,);
::SelectObject(hdc,hbitmap);
//-------------------------------------------------------------
// 显示排列
if(m_pRank != )
m_pRank->ShowRank(hdc,m_hIns);
this->ShowCursorCards(hdc);
//-------------------------------------------------------------
::BitBlt(dc,,,,,hdc,,,SRCCOPY);
::DeleteObject(hbitmap);
::DeleteDC(hdc);
::ReleaseDC(m_hMainWnd,dc);
}

大部分的重写都在CRule中,第一个拿牌的规则,利用迭代器的移动和首个牌的数字--,来判断参数的一串是否连续,一旦连续就可以拿牌

 bool CMyRule::IsGetCardsRule(CCardsRank* pCardsRank, int nlstID, list<Node*>::iterator iteCursorPos)
{
int num = (*iteCursorPos)->pCards->m_nCardsNum; while(iteCursorPos != pCardsRank->m_vecRank[nlstID].end())
{
if(num != (*iteCursorPos)->pCards->m_nCardsNum)
return false;
--num;
iteCursorPos++;
}
return true;
}

第二个,发牌的规则,首先判断在发牌的序列中,也就是最后一个链表中是否有牌了,通过了,再判断点到的是不是发牌序列中的最后一张牌,也就是说是否触发发牌的指令,最后一个判断前十个链表中是否有空的链表

 bool CMyRule::IsOpenCards(POINT point, CCardsRank* pCardsRank)
{
//判断最后一个链表里是否有东西
if(pCardsRank->m_vecRank[].empty() == false)
{
//判断是不是点到最后一张牌
if(point.x >= pCardsRank->m_vecRank[].back()->x && point.x <= pCardsRank->m_vecRank[].back()->x+
&& point.y >= pCardsRank->m_vecRank[].back()->y && point.y <= pCardsRank->m_vecRank[].back()->y+)
{
//前十个有没有空链表
for(int i = ;i < ;i++)
{
if(pCardsRank->m_vecRank[i].empty() == true)
return false;
}
return true;
}
}
return false;
}

第三个,接收牌的规则,这个就只有两点,是否鼠标坐标在上个牌的坐标范围之内,是否鼠标选中这张牌的数字比该链表的尾结点减一

 bool CMyRule::IsReceiveCardsRule(POINT point, int nlstID, CCardsRank* pCardsRank, list<Node*>& lstCursorCards)
{
if(pCardsRank->m_vecRank[nlstID].empty() == true)
{
if(point.x >= +nlstID* && point.x <= +nlstID*+ && point.y >= && point.y <= +)
{
return true;
}
}
else
{
if(point.x >= pCardsRank->m_vecRank[nlstID].back()->x && point.x <= pCardsRank->m_vecRank[nlstID].back()->x+
&& point.y >= pCardsRank->m_vecRank[nlstID].back()->y && point.y <= pCardsRank->m_vecRank[nlstID].back()->y+)
{
if(lstCursorCards.front()->pCards->m_nCardsNum == pCardsRank->m_vecRank[nlstID].back()->pCards->m_nCardsNum - )
{
return true;
}
}
}
}

第四个,更新坐标,这里需要注意的就是不更新松手后复位的坐标

 void CMyRule::UpDatePos(CCardsRank* pCardsRank, int nlstID)
{
int j = ;
list<Node*>::iterator ite = pCardsRank->m_vecRank[nlstID].begin();
while(ite != pCardsRank->m_vecRank[nlstID].end())
{
(*ite)->x = +nlstID*;
(*ite)->y = +j*;
++j;
++ite;
}
this->DeleteNode(pCardsRank,nlstID);
}

第五个,当结成连续的13张牌时,进行消除,这个判断要在接收牌时,以及发牌时进行

1.链表内至少有13个结点,最后一张牌应该是A

2.反向遍历判断有没有13张连续的正面

3.不连续或者为背面时结束

4.反向迭代器删除时要转为正向

5.删除后,尾结点翻牌

 void CMyRule::DeleteNode(CCardsRank* pCardsRank, int nlstID)
{
if(pCardsRank->m_vecRank[nlstID].size() >= && pCardsRank->m_vecRank[nlstID].back()->pCards->m_nCardsNum == )
{
int num = ;
list<Node*>::reverse_iterator rite = pCardsRank->m_vecRank[nlstID].rbegin();
for(int i = ;i < ;i++)
{
if((*rite)->bflag == false)
return;
if((*rite)->pCards->m_nCardsNum != num)
return;
++rite;
++num;
}
list<Node*>::iterator ite = rite.base();
while(ite != pCardsRank->m_vecRank[nlstID].end())
{
delete (*ite);
ite = pCardsRank->m_vecRank[nlstID].erase(ite);
}
}
if(pCardsRank->m_vecRank[nlstID].empty() == false)
pCardsRank->m_vecRank[nlstID].back()->bflag = true; }

从宏观上看,蜘蛛纸牌就是vector-List的应用,这也是我第一次尝试去写一个框架,代码我放在文件里了,希望各位能够指正一下

2019-07-08 11:47:46 编程小菜鸟自我总结,各位大佬可以提出自己的建议和意见,谢谢!!!

Win32小游戏--蜘蛛纸牌的更多相关文章

  1. Win32小游戏--贪吃蛇

    近日里学习了关于win32编程的相关知识,利用这些知识制作了一款贪吃蛇小游戏,具体细节还是分模块来叙述 前期准备:在网上找到一些贪吃蛇的游戏素材图片,以及具体的逻辑框图 在正式写功能之前,先把一系列环 ...

  2. C语言-纸牌计算24点小游戏

    C语言实现纸牌计算24点小游戏 利用系统时间设定随机种子生成4个随机数,并对4个数字之间的运算次序以及运算符号进行枚举,从而计算判断是否能得出24,以达到程序目的.程序主要功能已完成,目前还有部分细节 ...

  3. WinForm-简单21点纸牌小游戏

    纸牌游戏有很多种玩法,C#代码写的纸牌游戏,网上也能找到不少,从中也能学习到不少知识,自己动手也写一个,算是记录下学习过程吧. 纸牌21点的玩法也比较简单,就是看谁手中的所有牌相加是21点,或是离21 ...

  4. 一起来做webgame,《Javascript蜘蛛纸牌》

    不得不说,做游戏是会上瘾的,这次带来的是win系统上的经典游戏<蜘蛛纸牌>,不能完美,但求一玩 移牌 0 次 Javascript game_蜘蛛纸牌 正在努力加载... // " ...

  5. DFS 蜘蛛纸牌(深度解析)

    蜘蛛纸牌 Problem Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的 ...

  6. jQuery实践-网页版2048小游戏

    ▓▓▓▓▓▓ 大致介绍 看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了, ...

  7. 拼图小游戏之计算后样式与CSS动画的冲突

    先说结论: 前几天写了几个非常简单的移动端小游戏,其中一个拼图游戏让我郁闷了一段时间.因为要获取每张图片的位置,用`<style>`标签写的样式,直接获取计算后样式再用来交换位置,结果就悲 ...

  8. 推荐10款超级有趣的HTML5小游戏

    HTML5的发展速度比任何人的都想像都要更快.更加强大有效的和专业的解决方案已经被开发......甚至在游戏世界中!这里跟大家分享有10款超级趣味的HTML5游戏,希望大家能够喜欢! Kern Typ ...

  9. 如何开发一个简单的HTML5 Canvas 小游戏

    原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...

随机推荐

  1. C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享

    原文:C# 桌面软件开发-深入学习[2]- AY-C#人爱学不学-aaronyang技术分享 1 : C# Assembly.GetEntryAssembly().GetName().Version. ...

  2. 为什么腾讯总能做出好产品?(在互联网行业,往往仅凭一个关键产品就足以改变整个公司的格局)MSN失败在不以用户体验为中心

    投递人 itwriter 发布于 2017-07-10 11:16 评论(36) 有3401人阅读 原文链接 [收藏] « » 本文来自微信公众号“郑志昊 Peter”,作者李翔.郑志昊:博客园经授权 ...

  3. Qt 事件处理 快捷键(重写eventFilter的函数,使用Qt::ControlModifier判断)

    CTRL+Enter发送信息的实现 在现在的即时聊天程序中,一般都设置有快捷键来实现一些常用的功能,类似QQ可以用CTRL+Enter来实现信息的发送. 在QT4中,所有的事件都继承与QEvent这个 ...

  4. Win8 Metro(C#)数字图像处理--2.52图像K均值聚类

    原文:Win8 Metro(C#)数字图像处理--2.52图像K均值聚类  [函数名称]   图像KMeans聚类      KMeansCluster(WriteableBitmap src,i ...

  5. Win8Metro(C#)数字图像处理--2.30直方图均衡化

    原文:Win8Metro(C#)数字图像处理--2.30直方图均衡化 [函数名称] 直方图均衡化函数HistogramEqualProcess(WriteableBitmap src) [算法说明] ...

  6. python selenium chrome 测试

    #coding=utf-8 from selenium import webdriver from selenium.webdriver.common.keys import Keys from se ...

  7. 16.Oct Working Note

    01 writing algorithm by assembly,but the bug... now,it runs normaly,but how to print the answer? suc ...

  8. win10 uwp 获得Slider拖动结束的值

    原文:win10 uwp 获得Slider拖动结束的值 本文讲的是如何获得Slider移动结束的值,也就是触发移动后的值.如果我们监听ValueChanged,在我们鼠标放开之前,只要拖动不放,那么就 ...

  9. 项目中NHibernate问题及解决方法

     1.用户代码未处理 NHibernate.QueryException   Message=Type mismatch in NHibernate.Criterion.SimpleExpressio ...

  10. LCID

    Language Location (or type) Language ID Language tag Supported version Afar   0x1000 aa Release 9 Af ...