Win32小游戏--蜘蛛纸牌
前一段时间完成了蜘蛛纸牌的仿写,现将过程和思路记录下来
首先,为了符合复用性,在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小游戏--蜘蛛纸牌的更多相关文章
- Win32小游戏--贪吃蛇
近日里学习了关于win32编程的相关知识,利用这些知识制作了一款贪吃蛇小游戏,具体细节还是分模块来叙述 前期准备:在网上找到一些贪吃蛇的游戏素材图片,以及具体的逻辑框图 在正式写功能之前,先把一系列环 ...
- C语言-纸牌计算24点小游戏
C语言实现纸牌计算24点小游戏 利用系统时间设定随机种子生成4个随机数,并对4个数字之间的运算次序以及运算符号进行枚举,从而计算判断是否能得出24,以达到程序目的.程序主要功能已完成,目前还有部分细节 ...
- WinForm-简单21点纸牌小游戏
纸牌游戏有很多种玩法,C#代码写的纸牌游戏,网上也能找到不少,从中也能学习到不少知识,自己动手也写一个,算是记录下学习过程吧. 纸牌21点的玩法也比较简单,就是看谁手中的所有牌相加是21点,或是离21 ...
- 一起来做webgame,《Javascript蜘蛛纸牌》
不得不说,做游戏是会上瘾的,这次带来的是win系统上的经典游戏<蜘蛛纸牌>,不能完美,但求一玩 移牌 0 次 Javascript game_蜘蛛纸牌 正在努力加载... // " ...
- DFS 蜘蛛纸牌(深度解析)
蜘蛛纸牌 Problem Description 蜘蛛牌是windows xp操作系统自带的一款纸牌游戏,游戏规则是这样的:只能将牌拖到比她大一的牌上面(A最小,K最大),如果拖动的牌上有按顺序排好的 ...
- jQuery实践-网页版2048小游戏
▓▓▓▓▓▓ 大致介绍 看了一个实现网页版2048小游戏的视频,觉得能做出自己以前喜欢玩的小游戏很有意思便自己动手试了试,真正的验证了这句话-不要以为你以为的就是你以为的,看视频时觉得看懂了,会写了, ...
- 拼图小游戏之计算后样式与CSS动画的冲突
先说结论: 前几天写了几个非常简单的移动端小游戏,其中一个拼图游戏让我郁闷了一段时间.因为要获取每张图片的位置,用`<style>`标签写的样式,直接获取计算后样式再用来交换位置,结果就悲 ...
- 推荐10款超级有趣的HTML5小游戏
HTML5的发展速度比任何人的都想像都要更快.更加强大有效的和专业的解决方案已经被开发......甚至在游戏世界中!这里跟大家分享有10款超级趣味的HTML5游戏,希望大家能够喜欢! Kern Typ ...
- 如何开发一个简单的HTML5 Canvas 小游戏
原文:How to make a simple HTML5 Canvas game 想要快速上手HTML5 Canvas小游戏开发?下面通过一个例子来进行手把手教学.(如果你怀疑我的资历, A Wiz ...
随机推荐
- Hadoop源代码分析:HDFS读取和写入数据流控制(DataTransferThrottler类别)
DataTransferThrottler类别Datanode读取和写入数据时控制传输数据速率.这个类是线程安全的,它可以由多个线程共享. 用途是构建DataTransferThrottler对象,并 ...
- 写在程序猿的困惑(特别Java程序猿)入行一年,感觉我不知道接下来该怎么办才能不断进步的,寻求翼
入行了一年.感觉不知道接下来该怎么做才干继续进步了,求不吝赐教(V2EX) @kafka0102 :做技术能够学的东西太多了.仅仅是在不同的阶段做好不同的规划.要结合当前所做的事情去做更深入或广度的学 ...
- Linux 下编译并安装配置 Qt 4.53全过程
最近准备做 Nokia 的 Symbian,Maemo 下触摸屏开发.考虑到程序的跨平台可移植性,最终选择使用 Qt 开发.相对来说,国内关于 Qt 相关文档并不算很多.作者将 Linux 下编译并安 ...
- Bootstrap 反色导航条
@{ Layout = null;}<!DOCTYPE html><html><head> <meta name="viewport&q ...
- Android进程间通信-AIDL实现原理
Android进程间通信基于Proxy(代理)与Stub(桩或存根)的设计模式(如图1-1所示).其中,Proxy将特殊性接口转换成通用性接口,Stub将通用性接口转换成特殊性接口,二者之间的数据转换 ...
- 我写的一个Qt 显示二维码( QR Code)的控件(可以去掉对 libpthread 的依赖,而且编译出的库文件可以在 vc2010 的release 模式下使用)
最近一个项目需要显示二维码,所以花了点时间(只用了一个晚上,写的很不完善),写了个显示二维码的控件.当然这个控件用到了些开源的代码,比如qrencode,所以我也打算把我的代码开源. 我的代码参考了 ...
- [Erlang-0016][aque_tcp] 一个 Erlang TCP 组件
项目地址:https://github.com/liangjingyang/aque_tcp 欢迎任何形式的转载,但请务必注明出处:http://www.cnblogs.com/liangjingya ...
- 开源中国的 IT 公司开源软件整理计划介绍
直击现场 <HTML开发MacOSApp教程> http://pan.baidu.com/s/1jG1Q58M 开源中国的 IT 公司开源软件整理计划介绍 oschina 发布于: 20 ...
- Qt 5.3更新无数,更改C++控制台输出最为赞(这样就和普通C++ IDE没区别了)
转载请注明文章:Qt 5.3更新无数,更改C++控制台输出最为赞 出处:多客博图 本人觉得有了这个更新,Qt Creator可谓几乎没有缺点了,起码仅仅开发C/C++,是不用再去安装VS了. Qt 5 ...
- 为什么API多用C而不是C++,为什么C++程序大多不使用异常
读Defective C++随笔 不尽知用兵之害者,则不能尽知用兵之利也 ——<孙子兵法> 1.为什么API多用C而不是C++以前就一直很奇怪,为什么API大都用C的方式提供,即使有C++ ...