|
| C++课程设计报告 ——贪吃蛇和迷宫的设计及实现
学院:信息科学与工程学院 专业:电气信息类 班级:1230班 姓名:保密 学号:保密 2013/7/7 Sunday
|
目录
卷一:贪吃蛇的设计及实现
卷二:迷宫的设计及实现
卷一:贪吃蛇的设计及实现
一、 系统描述
游戏规则:
贪吃蛇游戏采用传统的游戏规则,实现蛇的移动、进食、增长以及死亡过程,并统计所得分数。游戏面板划分为许多小格,蛇移动时每次只移动一格。食物随机放置,但不能放到蛇身。每进食一次,蛇身增长一格,得分相应增加。蛇碰到边界时转到对面边界出来。当蛇头碰到自身时,蛇死亡,游戏结束。
游戏需求:
要有良好的可视化操作界面,有良好的声音效过。通过键盘鼠标控制,并且实现用户菜单及提示信息。游戏采用实时控制和动画刷新,保证系统稳定流畅运行。能够根据游戏规则自动判断游戏状态,并将信息实时反馈给用户。
概要设计:
游戏启动时显示游戏LOGO或加载信息,随后进入菜单界面,可选菜单要有:新的游戏、继续游戏、选择关卡、帮助及退出。游戏时,左边及中心显示游戏面板,右边显示信息栏。通过上下左右四个方向键控制蛇的移动,通过鼠标进行按钮的点击。游戏能够在任何时候暂停和退出游戏。通过关卡或者失败时,显示全屏的提示信息,并提供按钮供用户选择。游戏加入音乐功能模块,实现音效和背景音乐的播放。
环境要求:
Windows系统,Visual C++编译环境。
二、 功能模块结构
模块:
程序的主要模块有:游戏控制模块、资源管理模块、交互元素模块、游戏规则模块、绘图模块、音效模块、错误处理模块等。
| 模块 | 包含的类 | 功能 |
| 游戏控制模块 | Game | 主要负责游戏流程的控制,游戏元素的存储与组织,是整个游戏的控制中心。
|
| 资源管理模块 | Manager | 主要负责图片与音乐资源的统一管理、分配与调度。能够有效地集中资源节省空间、降低重复工作。 |
| 交互元素模块 | Button、Grid、Rect、SelBox、Text | 负责用户交互界面的处理与绘制。 |
| 游戏规则模块 | Food、Node、Panel、Snake | 控制游戏的规则。 |
| 绘图模块 | Painter、Elebase、Picture、Tile、Animation、FullScrAni | 用来绘图的类,包括绘图的操作和图元。 |
| 音效模块 | Mid | 对背景音乐进行加载与播放 |
| 错误处理模块 | Error、Init_Error、Outer_Error | 用来处理错误 |
类视图(详情请参见附件):
三、 主要模块的算法说明:
游戏控制模块:
游戏控制类的成员包含有窗口的基本信息,游戏资源管理类对象,面板类对象。在窗口建立好后接着构造游戏控制类,实现对其他部分类的初始化工作,并调用资源管理类进行资源的加载以及绘图设备的初始化。
游戏控制类采用状态进行驱动,通过改变记录状态的变量实现流程的跳转。
在游戏开始进行时,游戏控制模块负责面板的创建,从而建立起游戏规则类对象。游戏控制类接管了窗口的定时器消息,定时对游戏进行更新。许多页面的显示,例如菜单页面、成功页面、失败页面、帮助信息页面都由游戏控制类负责。
此外,游戏控制类实现了游戏存档的读取与保存。
资源管理模块:
资源管理模块用Manager类实现,Manager类依赖Picture类、Tile类、Animation类和Mid类,用四个顺序容器分别储存四个类的对象,实现资源的统一管理。此外Manager类实现了对背景音乐的控制。
交互元素模块:
以Elebase为虚基类,只要能在屏幕上画出来的都看做交互元素,每个交互元素必须与一个动画对象相关联,且都从Elebase依次多层继承下去。Elebase类储存有动画指针,提供的绘图的虚函数。
游戏规则模块:
游戏规则模块是游戏的核心,它构成了一个独立的系统,控制着游戏的运行。能读取键盘按键状态,并将游戏跳转信息发给游戏控制模块。游戏规则模块包括Food类、Node类、Panel类、Snake类,Food类保存食物信息并随机产生食物,Node类为蛇的节点类,Panel类是游戏的控制类,实现游戏的更新与画面的刷新,并检测游戏的状态。此外Panel有一Snake类的子对象。Panel类通过检测按键,向Snake类发送改变方向的消息,并让其移动。在每一帧中,Panel会检测蛇的长度、蛇是否已撞到自身,从而做出相应的响应。蛇的身体由一个Node顺序容器构成,Node类包含的成员主要有坐标、方向。蛇移动时,从尾开始,每个节点的位置都移动到下一个节点位置,最后一个节点(即蛇头)则根据蛇的方向移动。
绘图模块:
本游戏通过Windows GDI实现图形的绘制。其基本绘图过程为:加载位图、创建兼容设备,位块传输。由于考虑到游戏的刷新率较高,直接绘图会产生闪动的现象,所以采用了双缓冲技术。双缓冲的基本原理是:把图画全部画到缓冲设备上,再在最后一次性位块传输到原设备上。
音效模块:
游戏中采用wav声音作为普通的短暂的音效,这种音效的播放分散在程序的各个地方。具体的播放方法是调用PlaySound函数,即时加载即时播放。对于背景音乐,则采用mid格式文件。mid文件具有格式小,播放简单的特点,其实现方法是调用mciSendCommand函数。
错误处理模块:
错误处理类用来供程序抛出与捕捉程序,主要的类有Error基类、Init_Error类、Outer_Error类。通过错误处理模块,程序能够有序地处理错误。
四、 游戏运行效果:
五、 总结:
这是迄今为止,我用C++做过的最复杂的程序。程序总共大约一千五百行,拥有类21个。在程序设计的过程中,我遇到了许许多多的问题,但可喜的是,通过请教老师同学以及网上寻求帮助,这些问题最终都被解决了。主要遇到的问题有以下这些:如何组织资源、如何播放MID、怎样解决重载的二义性问题、怎样实现双缓冲等等。对于组织资源问题,我询问了许多网友,他们都一致认为资源应该集中加载与管理,这样有利于提高效率和节约空间。播放MID 的问题,我通过查阅Windows编程的书籍,发现其实MID的播放并不难,已经有现成的API可以调用。重载的二义性问题我也曾经遇到过,也曾经和同学探讨过,当时的解决方案是用强制转换类型避免二义性。但是这一次我遇到的问题不能通过强制转换来解决,幸运的是,在The C++ ProgrammingLanguage上面我成功地找到了解决办法,只要在类的构造函数前加一个explicit关键字,禁止默认的类型转换就能避免二义性。双缓冲技术曾经听说过,也写过DEMO,但由于双缓冲技术较为复杂,一时之间竟然不能很好地实现,所幸的是网上有许许多多关于双缓冲的介绍,通过阅读技术博客,我在游戏中顺利地实现了双缓冲。
在这个程序的编写过程中,我学到了许许多多知识,特别是学会了STL和Windows API。我认识到了软件的开发是一个庞大的复杂的工程,要实现这个工程,不仅需要很好的技术,更需要良好的组织与管理。我与许多同学交流过这方面的经验,发现一个项目的成功与失败,取决的往往不是技术的高低,而是工程的策划管理以及模块的划分是不是很到位。由此,我认识到了软件工程的重要性,并决心以后要学好软件工程。
六、 参考文献:
The C++ Programming Language——(美)Bjarne Stroustrup著
Windows程序设计——CharlesPetzold著
MSDN
七、 附录:
程序清单(详情请查看工程文件):
//****************global.h 全局类、枚举、常量的声明*********************************
#pragmaonce
#pragmawarning(disable:4786)
#include<windows.h>
#include<string>
usingstd::string;
//--------------------错误处理类------------------------------
classError
{
string info;
public:
Error(conststring& sinf):info(sinf){}
voidMsgBox(){MessageBox(NULL,info.c_str(),"错误",MB_OK);}
};
classInit_Error:publicError
{
public:
Init_Error(conststring& sinf):Error(sinf){}
};
classOuter_Error:publicError
{
public:
Outer_Error(conststring& sinf):Error(sinf){}
};
//-------------------Enums--------------------------------
enumANITYPE{ANI_LOGO=0,ANI_MENUBK,ANI_SNAKE_HEAD,ANI_SNAKE_BODY,ANI_SNAKE_TAIL,ANI_FOOD,ANI_BACKGROUND,
ANI_WIN,ANI_FAIL,ANI_BUTTON,ANI_OUTLINE,ANI_SELBKGROUND,ANI_HELP};
enumANIBACKGROUND{ANI_BACKGROUND_GRASS,ANI_BACKGROUND_WOOD,ANI_BACKGROUND_ROCK,ANI_BACKGROUND_TILE};
enumANIBUTTON{ANI_BUTTON_NEW,ANI_BUTTON_OLD,ANI_BUTTON_SEL,ANI_BUTTON_HELP,ANI_BUTTON_QUIT,
ANI_BUTTON_CONTINUE,ANI_BUTTON_BACK,ANI_BUTTON_LEFT,ANI_BUTTON_RIGHT,ANI_BUTTON_OK,ANI_BUTTON_CANCEL,
ANI_BUTTON_PAUSE,ANI_BUTTON_RECOVER,ANI_BUTTON_RETURN};
enumMID{MID_MENUBKM,MID_MAP0};
enumDIRECTION{RIGHT=0,DOWN=1,LEFT=2,UP=3};
//------------------const------------------------------
constintFPS=30;
//*********************贪吃蛇.cpp ******************************
//窗体的创建、消息的处理
#include<windows.h>
#include<stdlib.h>
#include<malloc.h>
#include<memory.h>
#include<tchar.h>
#include"resource.h"
#include"manager.h"
//全局变量
HINSTANCE hInst;
Game* pGame;
//函数声明
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULTCALLBACK WndProc(HWND,UINT, WPARAM, LPARAM);
//主函数
intAPIENTRY WinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
MyRegisterClass(hInstance);
// 程序初始化
if(!InitInstance (hInstance, nCmdShow))returnFALSE;
//消息转发
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}
//注册窗口类
ATOM MyRegisterClass(HINSTANCEhInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style =CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra =0;
wcex.cbWndExtra =0;
wcex.hInstance =hInstance;
wcex.hIcon =LoadIcon(hInstance, (LPCTSTR)IDI_MY);
wcex.hCursor =LoadCursor(hInstance, (LPCTSTR)IDC_CUR);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;(LPCSTR)IDC_MY;
wcex.lpszClassName = "SnakeWindow";
wcex.hIconSm =LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
returnRegisterClassEx(&wcex);
}
//初始化程序
BOOL InitInstance(HINSTANCEhInstance,intnCmdShow)
{
HWNDhWnd;
hInst = hInstance;
hWnd = CreateWindowEx((unsigned)NULL,"SnakeWindow","Snake", WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
CW_USEDEFAULT,0, 800+GetSystemMetrics(SM_CXFIXEDFRAME)*2,
600+GetSystemMetrics(SM_CXFIXEDFRAME)*2+GetSystemMetrics(SM_CYCAPTION), NULL, NULL, hInstance, NULL);
if (!hWnd)returnFALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//创建游戏
pGame=newGame(hInst,hWnd,800,600);
//设置定时器
SetTimer(hWnd,0,1000/FPS,NULL);
returnTRUE;
}
//消息处理
LRESULTCALLBACKWndProc(HWNDhWnd,UINTmessage, WPARAMwParam, LPARAMlParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
caseWM_PAINT:
hdc = BeginPaint(hWnd,&ps);
EndPaint(hWnd,&ps);
break;
caseWM_TIMER:
pGame->Loop();
break;
caseWM_DESTROY:
deletepGame;
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hWnd, message,wParam,lParam);
}
return 0;
}
//*********************element.h**************************
//包括了用户交互元素的类、绘图的类以及游戏规则类
#pragmaonce
#include"global.h"
#include"manager.h"
#include"paint.h"
#include<sstream>
#include<fstream>
#include<vector>
#include<string>
usingnamespacestd;
classPainter;
classManager;
classAnimation;
classNode;
classPanel;
classSnake;
//运算符重载声明
ofstream& operator<<(ofstream&ofs,constPanel&panel);
ifstream& operator>>(ifstream&ifs,Panel& panel);
ofstream& operator<<(ofstream&ofs,constSnake&snake);
ifstream& operator>>(ifstream&ifs,Snake& snake);
ofstream& operator<<(ofstream&ofs,constNode&node);
ifstream& operator>>(ifstream&ifs,Node& node);
classElebase//元素虚基类
{
protected:
staticPainter *pPainter;
staticManager *pManager;
Animation *pAni;
public:
void SetAni(Animation *pani){pAni=pani;}
virtualvoidSetAni(int id1,intid2=-1,int id3=-1);
staticvoidPreInit(Painter *pP,Manager *pM){pPainter=pP;pManager=pM;}
Elebase():pAni(NULL){if(!pPainter|| !pManager)throwInit_Error("元素类未初始化!");}
virtualvoidDraw()=0;
Animation* Ani(){returnpAni;}
};
classRect:publicElebase//矩形类
{
protected:
intx,y,w,h;
public:
virtualvoidDraw();
Rect(intrx,intry,intrw,intrh):x(rx),y(ry),w(rw),h(rh){}
};
classFullScrAni:publicRect//全屏动画类
{
staticintflscrw,flscrh;
public:
staticvoidPreInit(int w,inth);
FullScrAni():Rect(0,0,flscrw,flscrh){}
};
classText:publicRect//文字输出类
{
friendclassPainter;
string str;
HFONT font;
DWORD color;
public:
Text(int rx,intry,int rw,intrh,constchar*face="宋体");
~Text(){if(font)DeleteObject(font);}
voidSetText(conststring&tex,DWORDtexcolor=RGB(0,0,0)){str=tex;color=texcolor;};
voidSetText(intnum,DWORDtexcolor=RGB(0,0,0)){ostringstreamss;ss<<num;str=ss.str();color=texcolor;}
void Draw();
};
classButton:publicRect//按钮类
{
staticHWND hwnd;
bool focus;
boolkeydown;
boolpressed;
intorgx,orgy,orgw,orgh;
voidEnLarge(intpx=6){x-=px;w+=2*px;y-=px;h+=2*px;}
void Shift(intpx=2){x+=px;y+=px;}
voidEnNormal(){x=orgx;y=orgy;w=orgw;h=orgh;}
public:
staticvoidPreInit(HWNDhWnd){hwnd=hWnd;}
Button(intbx,int by,intbw=200,int bh=50);
void Deal();
boolMousePressed(){return pressed;}
voidRecover(){focus=false;keydown=false;pressed=false;EnNormal();}
};
classSelBox:publicRect//选择框类
{
Buttonleftbutton;
Buttonrightbutton;
Text tex;
vector<string> strlist;
int curstr;
public:
SelBox(intsx,int sy,intsw,int sh,floatscale=1);//scale为文本和按钮的大小比
void Deal();
voidSelBox::SetAni(int psb1,intplb1,int prb1,intpsb2=-1,int plb2=-1,intprb2=-1,int psb3=-1,intplb3=-1,int prb3=-1);
SelBox&Add(conststring&str){strlist.push_back(str);return*this;}
SelBox&Add(intnum){ostringstream ss;ss<<num;strlist.push_back(ss.str());return*this;}
string&str(){return strlist[curstr];}
void Draw();
};
classSnake//蛇类
{
friendclassPanel;
int rate; //移动一次所占帧数
intcurratepos; //移动之后过去的帧数
intdirection; //方向
intnewdirect; //新方向
bool added; //遇到食物是否已添加
bool live; //活的
vector<Node*> nodes;
public:
Snake(int lenth,intv,int startx=10,intstarty=10,int direc=RIGHT);
~Snake();
bool Move();//移动返回真,未移动返回假
boolIsBumpFood(int fx,intfy);
boolIsBumpSelf();
boolIsLive(){return live;}
voidRight(){if(direction!=LEFT)newdirect=RIGHT;}
void Down(){if(direction!=UP)newdirect=DOWN;}
void Left(){if(direction!=RIGHT)newdirect=LEFT;}
void Up(){if(direction!=DOWN)newdirect=UP;}
voidAddNode();
void Draw();
intlenth(){return nodes.size();}
Node*Head(){return nodes[0];}
//判断指定坐标是否在蛇上
inlineboolOnHead(int x,inty);
inlineboolOnBelly(int x,inty);
inlineboolOnBody(int x,inty);
inlineboolOnTail(int x,inty);
boolOnSnake(intx,inty){returnOnBody(x,y)||OnHead(x,y);}
inlineintHeadX();
inlineintHeadY();
//重载 用于存档
friendofstream& operator<<(ofstream&ofs,constSnake&snake);
friendifstream& operator>>(ifstream&ifs,Snake& snake);
};
classGrid:publicElebase//格子类
{
friendclassPanel;
friendclassSnake;
protected:
staticintpx,py,gw,gh;
int gx,gy;
public:
staticvoidPreInit(intpanelx,intpanely,intgridw,intgridh)
{px=panelx;py=panely;gw=gridw;gh=gridh;}
Grid(intgridx,intgridy):gx(gridx),gy(gridy){}
void Draw();
};
classFood:publicGrid//食物类
{
friendclassPanel;
intmaxx,maxy;
public:
Food(intxnum,intynum):Grid(0,0),maxx(xnum),maxy(ynum){}
voidGenerate()
{
gx=rand()%maxx;
gy=rand()%maxy;
}
friendofstream& operator<<(ofstream&ofs,constPanel&panel);
friendifstream& operator>>(ifstream&ifs,Panel& panel);
};
classNode:publicGrid//节点类 也即蛇的节点
{
friendclassSnake;
intdirection;
public:
Node(intgridx,intgridy,intdirect=RIGHT):Grid(gridx,gridy),direction(direct){}
int X(){returngx;}
int Y(){returngy;}
intDirection(){return direction;}
friendofstream& operator<<(ofstream&ofs,constNode&node);
friendifstream& operator>>(ifstream&ifs,Node& node);
};
classPanel:publicRect //游戏面板类
{
intxnum,ynum;
Snake *snake;
Food food;
bool overed;
bool won;
int level;
int score;
bool pause;
voidChkBump();
public:
explicit Panel(intLevel,int rectx=100,intrecty=100,int rectw=450,intrecth=450,int gridxnum=20,intgridynum=20);
~Panel(){if(snake){deletesnake;snake=NULL;}}
voidDoFrame();
boolOvered(){return overed;}
bool Won(){returnwon;}
intScore(){return score;}
intRestFood();
voidSetPause(boolp){pause=p;}
boolIsPause(){return pause;}
friendofstream& operator<<(ofstream&ofs,constPanel&panel);
friendifstream& operator>>(ifstream&ifs,Panel& panel);
};
//*******************element.cpp**********************
//
#include"element.h"
usingnamespacestd;
#pragmacomment(lib,"Winmm.lib")
Painter* Elebase::pPainter=NULL;
Manager *Elebase::pManager=NULL;
intFullScrAni::flscrw=0;
intFullScrAni::flscrh=0;
HWNDButton::hwnd=NULL;
voidElebase::SetAni(intid1,intid2,intid3)
{
pAni=pManager->PickAni(id1,id2,id3);
}
voidRect::Draw()
{
if(!pAni)return;
if(!pAni->Stoped())pPainter->Draw(*pAni,x,y,w,h);
pAni->Refresh();
}
intGrid::px=0,Grid::py=0,Grid::gw=0,Grid::gh=0;
voidGrid::Draw()
{
if(!pAni) throwInit_Error("格子类动画未初始化!");
if(!pAni->Stoped())pPainter->Draw(*pAni,px+gx*gw,py+gy*gh,gw,gh);
pAni->Refresh();
}
Text::Text(intrx,intry,intrw,intrh,constchar*face):Rect(rx,ry,rw,rh),font(NULL)
{
if(font)DeleteObject(font);
//创建字体画笔
font=CreateFont(h,w,0,0,FW_NORMAL,false,false,false,
CHINESEBIG5_CHARSET,OUT_CHARACTER_PRECIS,
CLIP_CHARACTER_PRECIS,DEFAULT_QUALITY,
FF_MODERN,"face");
}
voidText::Draw()
{
if(pAni)Rect::Draw();
pPainter->Draw(*this,x,y);
}
//--------------------------------------Button------------------------
Button::Button(intbx,intby,intbw,intbh)
:Rect(bx,by,bw,bh),focus(false),keydown(false),
pressed(false),orgx(bx),orgy(by),orgw(bw),orgh(bh)
{
}
voidButton::Deal()//处理按钮的响应事件
{
if(!hwnd)throwInit_Error("按钮类未获得窗口句柄!");
POINT point;
//获取鼠标指针坐标
GetCursorPos(&point);
ScreenToClient(hwnd,&point);
EnNormal();
if(point.x>x&&point.x<x+w&&point.y>y&&point.y<y+h)
{ //判断鼠标指针是否在按钮区域内
if(!focus)PlaySound("data\\mouseon.wav",NULL,SND_FILENAME|SND_ASYNC);
focus=true;
EnLarge();
if(GetAsyncKeyState(VK_LBUTTON))
{ //鼠标左键是否按下
keydown=true;
Shift();
}
else
if(keydown)
{
pressed=true;
keydown=false;
PlaySound("data\\click.wav",NULL,SND_FILENAME|SND_ASYNC);
}
}
else focus=false;
GetAsyncKeyState(VK_LBUTTON);
}
//-------------------SelBox
SelBox::SelBox(intsx,intsy,intsw,intsh,floatscale)
:Rect(sx,sy,sw,sh),leftbutton(sx,sy,int(sw/(scale+2)),sh),rightbutton(int(sx+sw-sw/(scale+2)),sy,int(sw/(scale+2)),sh),
tex(int(sx+sw/(scale+2)),sy,int(sw-2*sw/(scale+2)),sh),curstr(0)
{
}
voidSelBox::SetAni(intpsb1,intplb1,intprb1,intpsb2,intplb2,intprb2,intpsb3,intplb3,intprb3)
{
Elebase::SetAni(psb1,psb2,psb3);
leftbutton.SetAni(plb1,plb2,plb3);
rightbutton.SetAni(prb1,prb2,prb3);
}
voidSelBox::Deal()
{
leftbutton.Deal();
rightbutton.Deal();
if(leftbutton.MousePressed()&&curstr>0)
{ //针对左右按钮的按键做出响应
curstr--;
leftbutton.Recover();
}
if(rightbutton.MousePressed()&&curstr<int(strlist.size()-1))
{
curstr++;
rightbutton.Recover();
}
tex.SetText(strlist[curstr]);
}
voidSelBox::Draw()
{
if(pAni)Rect::Draw();
tex.Draw();
leftbutton.Draw();
rightbutton.Draw();
}
Snake::Snake(intlenth,intv,intstartx,intstarty,intdirec)
:rate(v),direction(direc),curratepos(0),added(false),live(true),
newdirect(RIGHT)
{
//蛇的构建
Node* head=newNode(startx--,starty);
head->SetAni(ANI_SNAKE_HEAD);
nodes.push_back(head);
for(inti=1;i<lenth-1;i++)
{
Node*body=newNode(startx--,starty);
body->SetAni(ANI_SNAKE_BODY);
nodes.push_back(body);
}
Node* tail=newNode(startx--,starty);
tail->SetAni(ANI_SNAKE_TAIL);
nodes.push_back(tail);
}
Snake::~Snake()
{
//蛇的清除
for(inti=0;i<(int)nodes.size();i++)
deletenodes[i];
nodes.clear();
}
intSnake::HeadX()
{
returnHead()->X();
}
intSnake::HeadY()
{
returnHead()->Y();
}
boolSnake::Move()//蛇的移动
{
if(!live) returnfalse;
curratepos+=1000/FPS;
if(curratepos>=rate)
{
curratepos=0;
inthx=Head()->gx;
inthy=Head()->gy;
direction=newdirect;
//根据蛇的方向改变蛇头坐标
switch(direction)
{
caseRIGHT:
hx++;
break;
caseDOWN:
hy++;
break;
caseLEFT:
hx--;
break;
caseUP:
hy--;
break;
}
//移动
for(vector<Node*>::iterator it=nodes.end()-1;it>nodes.begin();it--)
{
if(it==nodes.end()-1&& added)
{
added=false;
continue;
}
(*it)->gx=(*(it-1))->gx;
(*it)->gy=(*(it-1))->gy;
(*it)->direction=(*(it-1))->direction;
}
nodes[0]->gx=hx;
nodes[0]->gy=hy;
nodes[0]->direction=direction;
returntrue;
}
returnfalse;
}
boolSnake::IsBumpFood(intfx,intfy)//自己增长
{
if(HeadX()==fx&& HeadY()==fy)//撞到食物
{
AddNode();
returntrue;
}
returnfalse;
}
boolSnake::IsBumpSelf()
{
if(OnBody(HeadX(),HeadY()))//撞到自己
{
live=false;
}
return live;
}
voidSnake::AddNode()//蛇增加节点
{
Node*tail=nodes.back();
Node* body=newNode(tail->X(),tail->Y());
body->SetAni(ANI_SNAKE_BODY);
nodes.insert(nodes.end()-1,body);
added=true;
}
boolSnake::OnHead(intx,inty)
{
returnx==nodes[0]->gx&& y==nodes[0]->gy;
}
boolSnake::OnBody(intx,inty)//包括头和尾
{
return OnTail(x,y)||OnBelly(x,y);
}
boolSnake::OnTail(intx,inty)
{
returnx==nodes.back()->gx&& y==nodes.back()->gy;
}
boolSnake::OnBelly(intx,inty)//蛇的中间部分
{
for(vector<Node*>::iterator it=nodes.begin()+1;it<nodes.end()-1;it++)
if(x==(*it)->gx&& y==(*it)->gy) returntrue;
returnfalse;
}
voidSnake::Draw()
{
//循环把蛇身画出来
for(vector<Node*>::iterator it=nodes.begin();it<nodes.end();it++)
(*it)->Draw();
}
Panel::Panel(intLevel,intrectx,intrecty,intrectw,intrecth,intgridxnum,intgridynum)
:level(Level),Rect(rectx,recty,rectw,recth),xnum(gridxnum),ynum(gridynum),
snake(NULL),food(xnum,ynum),overed(false),won(false),score(0),pause(false)
{
food.SetAni(ANI_FOOD);
food.Generate();
Grid::PreInit(x,y,w/xnum,h/ynum);
snake=newSnake(int(level*2),int(400/level));//创建蛇
SetAni(ANI_BACKGROUND,int(level/2+0.5)%9);//设置动画
}
voidPanel::DoFrame()
{
Draw();
food.Draw();
if(!pause)
{
//检测按键
if(GetAsyncKeyState(VK_RIGHT)) snake->Right();
if(GetAsyncKeyState(VK_DOWN)) snake->Down();
if(GetAsyncKeyState(VK_LEFT)) snake->Left();
if(GetAsyncKeyState(VK_UP)) snake->Up();
if(snake->Move())
{
if(snake->HeadX()<0)snake->Head()->gx=xnum-1;
if(snake->HeadY()<0)snake->Head()->gy=ynum-1;
if(snake->HeadX()>=xnum)snake->Head()->gx=0;
if(snake->HeadY()>=ynum)snake->Head()->gy=0;
ChkBump();
}
}
snake->Draw();
if(RestFood()<=0)//判断食物是否全部吃完
{
overed=true;
won=true;
PlaySound("data\\win.wav",NULL,SND_FILENAME|SND_ASYNC);
}
}
intPanel::RestFood()
{
returnlevel*3+3-snake->lenth();
}
voidPanel::ChkBump()//碰撞检测
{
snake->IsBumpSelf();
if(snake->IsBumpFood(food.gx,food.gy))//蛇是否撞到食物
{
score+=level*10;
PlaySound("data\\eat.wav",NULL,SND_FILENAME|SND_ASYNC);
do
{
boolonsnake=false;
food.Generate();
}while(snake->OnSnake(food.gx,food.gy));
}
if(!snake->IsLive())//蛇是否是活的
{
overed=true;
won=false;
PlaySound("data\\fail.wav",NULL,SND_FILENAME|SND_ASYNC);
}
}
//以下是运算符重载,用于存档的读取与储存
ofstream& operator<<(ofstream&ofs,constNode& node)
{
ofs<<node.gx<<endl<<node.gy<<endl<<node.direction<<endl;
returnofs;
}
ifstream& operator>>(ifstream&ifs,Node& node)
{
ifs>>node.gx>>node.gy>>node.direction;
returnifs;
}
ofstream& operator<<(ofstream&ofs,constSnake& snake)
{
ofs<<snake.rate<<endl<<snake.direction<<endl<<snake.newdirect<<endl;
for(vector<Node*>::const_iterator it=snake.nodes.begin();it<snake.nodes.end();it++)
ofs<<(**it);
returnofs;
}
ifstream& operator>>(ifstream&ifs,Snake& snake)
{
ifs>>snake.rate>>snake.direction>>snake.newdirect;
for(vector<Node*>::const_iterator it=snake.nodes.begin();it<snake.nodes.end();it++)
{
ifs>>**it;
}
returnifs;
}
ofstream& operator<<(ofstream&ofs,constPanel& panel)
{
ofs<<panel.xnum<<endl<<panel.ynum<<endl
<<panel.food.gx<<endl<<panel.food.gy<<endl
<<panel.level<<endl<<panel.score<<endl;
ofs<<*panel.snake;
returnofs;
}
ifstream& operator>>(ifstream&ifs,Panel& panel)
{
ifs>>panel.xnum>>panel.ynum
>>panel.food.gx>>panel.food.gy
>>panel.level>>panel.score;
ifs>>*panel.snake;
returnifs;
}
//*******************paint.h**************************
//主要包含绘图的模块
#pragmaonce
#include"global.h"
#include"manager.h"
#include"element.h"
#include<windows.h>
#include<vector>
usingstd::vector;
classText;
classPicture//图片类
{
friendclassPainter;
HDC hdc;
HBITMAP hbmp;
int w,h;
staticHINSTANCE hInst;
staticHDC horgdc;
public:
staticvoidPreInit(HINSTANCE hInstance,HDChoriginaldc);
Picture(constchar*picfile);
~Picture();
};
classTile//贴片类,即图片上的一块矩形区域
{
friendclassPainter;
Picture *pPic;
intpicx,picy,tilew,tileh;
public:
Tile(Picture*ppic,intx,inty,intw,inth)
:pPic(ppic),picx(x),picy(y),tilew(w),tileh(h)
{if(ppic<0||x<0||y<0||w<=0||h<=0)throwInit_Error("贴片类初始化失败!");}
virtualconstTile& GetTile()const{return*this;}
Picture* Pic(){returnpPic;}
};
classAnimation//动画类,由许多贴片构成动画
{
friendclassPainter;
vector<Tile*> tilepv;
//
int cycl;
intinterval;
int pos;
intintervalpos;
intcyctimes;
bool stoped;
public:
Animation(intcyc,intitvl):cycl(cyc),interval(itvl),pos(0),cyctimes(0),intervalpos(0),stoped(false)
{if(cyc<0||itvl<0)throwInit_Error("动画类初始化失败!");}
voidAddTile(Tile* tile){tilepv.push_back(tile);}
voidRefresh();
voidRestart(){pos=0;intervalpos=0;cyctimes=0;stoped=false;}
constTile& GetTile()const {return*tilepv[pos];}
boolStoped()const{returnstoped;}
};
classPainter//负责画图的类
{
HDChcompdc;
HDC hdc;
HBITMAPhcompbmp;
intx,y,w,h;
DWORDbkcolor;
public:
Painter(HDChDC,int cx,intcy,int cw,intch);
~Painter();
void Draw(constTile& tile,int x,inty);
void Draw(constAnimation& ani,int x,inty);
void Draw(constAnimation& ani,int x,inty,int w,inth);
void Draw(constText& tex,int x,inty);
voidErase();
voidApply();
};
//*********************paint.cpp**************************
#include"paint.h"
#pragmacomment(lib,"Msimg32.lib")
//---------------Picture-----------------------
HINSTANCEPicture::hInst=NULL;
HDCPicture::horgdc=NULL;
//预先初始化函数,由于该类需要在构造时知道某些必要参数,并且对于所有对象,这些参数
//都相同,所以采用静态成员预先初始化的方法
voidPicture::PreInit(HINSTANCEhInstance,HDChoriginaldc)
{
hInst=hInstance;
horgdc=horiginaldc;
}
Picture::Picture(constchar*picfile):hdc(NULL),hbmp(NULL)
{
//图片的加载
if(!hInst|| !horgdc) throwInit_Error("图片类未预初始化!");
hbmp=(HBITMAP)LoadImage(hInst,picfile,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_LOADFROMFILE);
if(!hbmp)throwOuter_Error("加载图片失败!");
hdc=CreateCompatibleDC(horgdc);
if(!hdc)throwOuter_Error("创建兼容设备失败!");
SelectObject(hdc,hbmp);
BITMAP bmp;
if(!GetObject(hbmp,sizeof(BITMAP),&bmp))throwOuter_Error("获取图片信息失败!");
w=bmp.bmWidth;
h=bmp.bmHeight;
}
Picture::~Picture()
{
if(hdc)DeleteObject(hdc);
if(hbmp)DeleteObject(hbmp);
}
//---------------Animation-------------------
voidAnimation::Refresh()
{
//动画的控制
if(intervalpos<interval)
{
intervalpos++;
return;
}
else
{
intervalpos=0;
}
if(pos<(int)tilepv.size()-1&& !stoped)
{
pos++;
}
else
{
pos=0;
if(cycl!=0)
{
cyctimes++;
if(cyctimes>=cycl)
stoped=true;
}
}
}
//--------------------Painter----------------------------
Painter::Painter(HDChDC,intcx,intcy,intcw,intch):hdc(hDC),hcompdc(NULL),hcompbmp(NULL),x(cx),y(cy),w(cw),h(ch)
{
//采用双缓冲,建立缓冲区
hcompdc=CreateCompatibleDC(hdc);
hcompbmp=CreateCompatibleBitmap(hdc,w,h);
SelectObject(hcompdc,hcompbmp);
}
Painter::~Painter()
{
if(hcompdc)
{
DeleteDC(hcompdc);
hcompdc=NULL;
}
if(hcompbmp)
{
DeleteObject(hcompbmp);
hcompbmp=NULL;
}
}
voidPainter::Draw(constTile& tile,intx,inty)
{
//位块传输,用来贴图
BitBlt(hcompdc,x,y,tile.tilew,tile.tileh,tile.pPic->horgdc,tile.picx,tile.picy,SRCCOPY);
}
voidPainter::Draw(constAnimation& ani,intx,inty)
{
if(ani.Stoped())return;
constTile& tile=ani.GetTile();
BitBlt(hcompdc,x,y,tile.tilew,tile.tileh,tile.pPic->hdc,tile.picx,tile.picy,SRCCOPY);
}
voidPainter::Draw(constAnimation& ani,intx,inty,intw,inth)
{
if(ani.Stoped())return;
constTile& tile=ani.GetTile();
//过滤透明色的位块传输
TransparentBlt(hcompdc,x,y,w,h,tile.pPic->hdc,tile.picx,tile.picy,tile.tilew,tile.tileh,RGB(0,0,0));
}
voidPainter::Draw(constText& tex,intx,inty)
{
//绘制文字
SetBkMode(hcompdc,TRANSPARENT);
SetTextColor(hcompdc,tex.color);
SelectObject(hcompdc,tex.font);
TextOut(hcompdc,x,y,tex.str.c_str(),tex.str.length());
}
voidPainter::Erase()
{
//对整个绘图区域进行擦除
HBRUSHbrush=CreateSolidBrush(bkcolor);
SelectObject(hcompdc,brush);
RECTrect={x,y,x+w,y+h};
FillRect(hcompdc,&rect,brush);
DeleteObject(brush);
}
voidPainter::Apply()
{
//把缓冲页面的内容贴到屏幕设备上
BitBlt(hdc,x,y,w,h,hcompdc,0,0,SRCCOPY);
}
//**********************manager.h*******************
//管理模块
#pragmaonce
#include"global.h"
#include"paint.h"
#include"element.h"
#include<fstream>
#include<string>
#include<vector>
usingstd::string;
usingstd::fstream;
usingstd::vector;
classPicture;
classTile;
classAnimation;
classPainter;
classPanel;
classMid;
classManager
{
typedefvector<Picture*> PICPV;
typedefvector<Tile*> TILEPV;
typedefvector<Animation*> ANIPV;
typedefvector<Mid*> MIDPV;
//用顺序容器储存所有的图片与背景音乐资源
PICPV pics;
TILEPV tiles;
ANIPV anis;
MIDPV mids;
int curmid;
public:
Manager():curmid(-1){}
~Manager();
void Add(conststring& picfile);//添加图片
void Add(Picture *ppic,int x,inty,int w,inth);//添加贴片
void Add(intpicid,int x,inty,int w,inth);//添加贴片
void Add(constvector<int>&tilevct,int cycl,intinterval);//添加动画
void AddMid(conststring& midfile);
void Add(intaniIndex);//添加单次循环动画
Animation&Ani(intid){return*anis.at(id);}
voidLoadRes();
voidLoadMid();
Animation*PickAni(int p1,intp2=-1,int p3=-1);
Mid*PicMid(int id);
void Play(intid);
void Stop();
};
classGame
{
//游戏状态
enumGAMESTATE{GAME_SHOWLOGO,GAME_SHOWMENU,GAME_SHOWHELP,GAME_CREATE,GAME_RESTART,GAME_RUN,GAME_DESTROY,GAME_WIN,GAME_FAIL,GAME_OVER};
GAMESTATE state;
HDChclientdc;
HWNDhWindow;
Painter*pPainter;
intclientw,clienth;
Managermanager;
int level;
introundtime;//每一局流逝的时间
voidShowLogo();
voidShowMenu();
voidShowWin();
voidShowFail();
voidShowOver();
voidShowHelp();
voidCreate();
voidReStart();
void Run();
voidDestroy();
voidReadArchive();
voidSaveArchive();
///////////////////Game Element///////////////////
Panel*pPanel;
public:
Game(HINSTANCEhinst,HWND hwnd,intclw,int clh);
~Game();
void Loop();
};
classMid//mid背景音乐处理的类
{
stringfilename;
UINTwDeviceID;
public:
Mid(conststring& fname);
~Mid();
void Play();
void Stop();
};
//*********************manager.cpp*******************
#include"manager.h"
#include"element.h"
#include<time.h>
#include<iterator>
usingnamespacestd;
#include<mmsystem.h>
#include<Digitalv.h>
#pragmacomment(lib,"Msimg32.lib")
#pragmacomment(lib,"winmm.lib")
voidFullScrAni::PreInit(intw,inth)
{
flscrw=w;
flscrh=h;
}
//-------------------Game-----------------------------
Game::Game(HINSTANCEhinst,HWNDhwnd,intclw,intclh)
:clientw(clw),clienth(clh),pPanel(NULL),level(0),pPainter(NULL),hclientdc(NULL),hWindow(hwnd),
roundtime(0)
{
srand((unsignedint)time(0));
hclientdc=GetDC(hWindow);
pPainter=newPainter(hclientdc,0,0,clientw,clienth);
//PreInit
Picture::PreInit(hinst,hclientdc);
Elebase::PreInit(pPainter,&manager);
FullScrAni::PreInit(clientw,clienth);
Button::PreInit(hwnd);
//加载资源
try
{
manager.LoadRes();
ShowLogo();
pPainter->Apply();
manager.LoadMid();
}
catch(Error err)
{
err.MsgBox();
}
state=GAME_SHOWMENU;
}
Game::~Game()
{
if(hclientdc)ReleaseDC(hWindow,hclientdc);
if(pPanel){deletepPanel;pPanel=NULL;}
if(pPainter){deletepPainter;pPainter=NULL;}
}
voidGame::Create()
{
if(pPanel)deletepPanel;
//创建游戏面板
pPanel=newPanel(level);
manager.Play(MID_MAP0);
state=GAME_RUN;
roundtime=0;
}
voidGame::ShowWin()//显示胜利信息
{
staticFullScrAni ani;
staticButton butcont(clientw/2-100,clienth*4/5);
ani.SetAni(ANI_WIN);
ani.Draw();
butcont.SetAni(ANI_BUTTON,ANI_BUTTON_CONTINUE);
butcont.Deal();
butcont.Draw();
if(butcont.MousePressed())
{
butcont.Recover();
state=GAME_RESTART;
level++;
ani.Ani()->Restart();
}
}
voidGame::ShowFail()//显示失败信息
{
staticFullScrAni ani;
staticButton butcont(clientw*4/5-200,clienth*4/5);
staticButton butback(clientw/5,clienth*4/5);
ani.SetAni(ANI_FAIL);
ani.Draw();
butcont.SetAni(ANI_BUTTON,ANI_BUTTON_CONTINUE);
butcont.Deal();
butcont.Draw();
butback.SetAni(ANI_BUTTON,ANI_BUTTON_BACK);
butback.Deal();
butback.Draw();
if(butcont.MousePressed())
{
butcont.Recover();
state=GAME_RESTART;
ani.Ani()->Restart();
}
elseif(butback.MousePressed())
{
butback.Recover();
level=1;
Destroy();
state=GAME_SHOWMENU;
}
}
voidGame::ShowOver()//显示游戏结束
{
Destroy();
state=GAME_SHOWMENU;
}
voidGame::Destroy()//销毁游戏成员对象
{
if(pPanel)
{
deletepPanel;
pPanel=NULL;
}
}
voidGame::Loop()//游戏的循环,由窗口的WM_TIMER消息引发
{
pPainter->Erase();
try
{
//根据游戏状态控制游戏流程
switch(state)
{
caseGAME_SHOWMENU:
ShowMenu();
break;
caseGAME_SHOWHELP:
ShowHelp();
break;
caseGAME_CREATE:
Create();
break;
caseGAME_RUN:
Run();
break;
caseGAME_DESTROY:
Destroy();
break;
caseGAME_RESTART:
ReStart();
break;
caseGAME_WIN:
ShowWin();
break;
caseGAME_FAIL:
ShowFail();
break;
caseGAME_OVER:
break;
}
}catch(Error err)
{
err.MsgBox();
}
pPainter->Apply();
}
voidGame::ReStart()
{
Destroy();
Create();
}
voidGame::Run()//游戏运行
{
//界面元素的创建
staticFullScrAni outline;
staticText texlevel(675,217,20,20);
staticText texfood(675,300,20,20);
staticText texscore(675,387,20,20);
staticButton butpause(628,500,100,50);
staticButton butret(740,40,50,50);
//界面元素动画的设置
butpause.SetAni(ANI_BUTTON,pPanel->IsPause()?ANI_BUTTON_RECOVER:ANI_BUTTON_PAUSE);
butret.SetAni(ANI_BUTTON,ANI_BUTTON_RETURN);
outline.SetAni(ANI_OUTLINE);
//响应
butpause.Deal();
butret.Deal();
if(butpause.MousePressed())
{//是否暂停
butpause.Recover();
pPanel->SetPause(!pPanel->IsPause());
}
outline.Draw();
pPanel->DoFrame();
//绘制
butpause.Draw();
butret.Draw();
texlevel.SetText(level,RGB(255,255,255));
texscore.SetText(pPanel->Score(),RGB(255,255,255));
texfood.SetText(pPanel->RestFood(),RGB(255,255,255));
texlevel.Draw();
texscore.Draw();
texfood.Draw();
if(pPanel->Overed())
{
if(pPanel->Won())state=GAME_WIN;
elsestate=GAME_FAIL;
manager.Stop();
}
if(butret.MousePressed())
{
butret.Recover();
Destroy();
state=GAME_SHOWMENU;
}
roundtime+=1000/FPS;
if(roundtime%1000<1000/FPS)//每秒储存一次
SaveArchive();
}
voidGame::ShowHelp()//显示帮助信息
{
staticFullScrAni helppage;
staticButton helpback(clientw*3/4-30,clienth*7/8,200,50);
helppage.SetAni(ANI_HELP);
helpback.SetAni(ANI_BUTTON,ANI_BUTTON_BACK);
helpback.Deal();
helppage.Draw();
helpback.Draw();
if(helpback.MousePressed())
{
helpback.Recover();
state=GAME_SHOWMENU;
}
}
voidGame::ShowMenu()//显示菜单
{
staticboolfirstrun=true;
//创建界面元素
staticFullScrAni menubk;
staticButton butnew(clientw/2,clienth/3+20);
staticButton butold(clientw/2,clienth/3+80);
staticButton butsel(clientw/2,clienth/3+140);
staticButton buthelp(clientw/2,clienth/3+200);
staticButton butquit(clientw/2,clienth/3+260);
staticSelBox sel(clientw/8+110,clienth*2/5+45,150,50);
staticRect selbkground(clientw/8,clienth*2/5,clientw/3,clienth/3);
staticButton butok(clientw/8+20,clienth*2/5+120,100,50);
staticButton butcl(clientw/8+145,clienth*2/5+120,100,50);
manager.Play(MID_MENUBKM);
if(firstrun)
{
//设置动画
menubk.SetAni(ANI_MENUBK);
butnew.SetAni(ANI_BUTTON,ANI_BUTTON_NEW);
butold.SetAni(ANI_BUTTON,ANI_BUTTON_OLD);
butsel.SetAni(ANI_BUTTON,ANI_BUTTON_SEL);
buthelp.SetAni(ANI_BUTTON,ANI_BUTTON_HELP);
butquit.SetAni(ANI_BUTTON,ANI_BUTTON_QUIT);
butok.SetAni(ANI_BUTTON,ANI_BUTTON_OK);
butcl.SetAni(ANI_BUTTON,ANI_BUTTON_CANCEL);
sel.Add(1).Add(2).Add(3).Add(4).Add(5).Add(6).Add(7).Add(8).Add(9);
sel.SetAni(-1,ANI_BUTTON,ANI_BUTTON,-1,ANI_BUTTON_LEFT,ANI_BUTTON_RIGHT);
selbkground.SetAni(ANI_SELBKGROUND);
}
//界面元素的响应
butnew.Deal();
butold.Deal();
butsel.Deal();
buthelp.Deal();
butquit.Deal();
//界面元素的绘制
menubk.Draw();
butnew.Draw();
butold.Draw();
butsel.Draw();
buthelp.Draw();
butquit.Draw();
//检测界面元素的状态
if(butnew.MousePressed())//新的开始
{
level=1;
butnew.Recover();
state=GAME_CREATE;
}
if(butold.MousePressed())//旧的回忆
{
ReadArchive();
butold.Recover();
state=GAME_RUN;
}
if(butsel.MousePressed())//选择关卡
{
selbkground.Draw();
sel.Deal();
sel.Draw();
butok.Deal();
butcl.Deal();
butok.Draw();
butcl.Draw();
if(butok.MousePressed())
{
butsel.Recover();
butok.Recover();
level=atoi(sel.str().c_str());
state=GAME_CREATE;
}
if(butcl.MousePressed())
{
butcl.Recover();
butsel.Recover();
}
}
if(buthelp.MousePressed())//帮助
{
buthelp.Recover();
state=GAME_SHOWHELP;
}
if(butquit.MousePressed())
{
::PostQuitMessage(0);
}
firstrun=false;
}
voidGame::ShowLogo()
{
Rectanilog(400,450,350,100);
anilog.SetAni(ANI_LOGO);
anilog.Draw();
}
voidGame::ReadArchive()//读取存档
{
ifstream ifs("data\\archive.txt");
ifs>>level;
if(pPanel)deletepPanel;
if(level<=0)
{
level=1;
pPanel=newPanel(level);
}
else
{
pPanel=newPanel(level);
ifs>>*pPanel;
}
ifs.close();
manager.Play(MID_MAP0);
state=GAME_RUN;
roundtime=0;
}
voidGame::SaveArchive()//保存存档
{
ofstream ofs("data\\archive.txt");
if(!ofs)throwOuter_Error("打开存档文件失败!");
ofs<<level<<endl;
ofs<<*pPanel;
ofs.close();
}
//----------------------Manager----------------------------
Manager::~Manager()
{
for(PICPV::iteratorpit=pics.begin();pit<pics.end();pit++)
delete*pit;
for(TILEPV::iteratortit=tiles.begin();tit<tiles.end();tit++)
delete*tit;
for(ANIPV::iteratorait=anis.begin();ait<anis.end();ait++)
delete*ait;
for(MIDPV::iteratormit=mids.begin();mit<mids.end();mit++)
delete*mit;
}
voidManager::LoadRes()//加载资源
{
Add("data\\logo.bmp");
Add("data\\background.bmp");
Add("data\\fail.bmp");
Add("data\\win.bmp");//3
Add("data\\game.bmp");//4
Add("data\\ui.bmp");//5
Add("data\\outline.bmp");//6
Add("data\\help.bmp");//7
Add(0,0,0,800,600);//0 logo
Add(1,0,0,450,450);//1ground
Add(1,450,0,450,450);//2ground
Add(1,0,450,450,450);//3ground
Add(1,450,450,450,450);//4ground
Add(2,0,0,800,600);//5 failpic
Add(3,0,0,800,600);//6 winpic
Add(4,0,0,30,30);//7snake head
Add(4,30,0,30,30);//8snake body
Add(4,60,0,30,30);//9snake tail
Add(4,90,0,30,30);//10food
//ui
Add(5,0,0,200,50);//11new
Add(5,0,50,200,50);//12 old
Add(5,0,100,200,50);//13 sel
Add(5,0,150,200,50);//14help
Add(5,0,200,200,50);//15quit
Add(5,0,250,200,50);//16 继续
Add(5,0,300,200,50);//17 返回
Add(6,0,0,800,600);//18outline
Add(5,200,0,50,50);//19selbox<
Add(5,250,0,50,50);//20selbox>
Add(5,300,0,350,250);//21selectlevel background
Add(5,0,350,100,50);//22 确定
Add(5,100,350,100,50);//23 取消
Add(7,0,0,800,600);//24 帮助
Add(5,0,400,100,50);//25 确定
Add(5,100,400,100,50);//26 取消
Add(5,200,50,50,50);//27 x按钮
Add(5,300,250,350,100);//logo28
vector<int>iv;
Add(0);//menubackground
Add(7);//snake1
Add(8);
Add(9);
Add(10);//food 4
//5 ground
Add(1);Add(2);Add(3);Add(4);
Add(5);//fail 9
Add(6);//win 10
//ui botton 11-15
Add(11);Add(12);Add(13);Add(14);Add(15);Add(16);Add(17);
//outline
Add(18);
Add(19);Add(20);//selbox<>
Add(21);//selectlevel background
Add(22);Add(23);//确定取消2223
Add(24);//帮助页面24
Add(25);Add(26);Add(27);//暂停返回X 25 26 27
Add(28);//logo28
}
voidManager::LoadMid()
{
AddMid("data\\menubkm.mid");
AddMid("data\\map0.mid");
}
Animation* Manager::PickAni(intp1,intp2,intp3)
{ //这个函数提供从标示符到动画指针的转换
switch(p1)
{
caseANI_LOGO: returnanis[28];break;
caseANI_MENUBK: returnanis[0]; break;
caseANI_SNAKE_HEAD:returnanis[1]; break;
caseANI_SNAKE_BODY:returnanis[2]; break;
caseANI_SNAKE_TAIL:returnanis[3]; break;
caseANI_FOOD: returnanis[4]; break;
caseANI_BACKGROUND:
switch(p2)
{
caseANI_BACKGROUND_GRASS: returnanis[5]; break;
caseANI_BACKGROUND_WOOD: returnanis[6]; break;
caseANI_BACKGROUND_ROCK: returnanis[7]; break;
caseANI_BACKGROUND_TILE: returnanis[8]; break;
}
returnanis[5];
break;
caseANI_FAIL: returnanis[9]; break;
caseANI_WIN: returnanis[10];break;
caseANI_BUTTON:
switch(p2)
{
caseANI_BUTTON_NEW:returnanis[11];break;
caseANI_BUTTON_OLD:returnanis[12];break;
caseANI_BUTTON_SEL:returnanis[13];break;
caseANI_BUTTON_HELP:returnanis[14];break;
caseANI_BUTTON_QUIT:returnanis[15];break;
caseANI_BUTTON_CONTINUE:returnanis[16];break;
caseANI_BUTTON_BACK:returnanis[17];break;
caseANI_BUTTON_LEFT:returnanis[19];break;
caseANI_BUTTON_RIGHT:returnanis[20];break;
caseANI_BUTTON_OK:returnanis[22];break;
caseANI_BUTTON_CANCEL:returnanis[23];break;
caseANI_BUTTON_PAUSE:returnanis[25];break;
caseANI_BUTTON_RECOVER:returnanis[26];break;
caseANI_BUTTON_RETURN:returnanis[27];break;
}
break;
caseANI_OUTLINE:returnanis[18];break;
caseANI_SELBKGROUND:returnanis[21];break;
caseANI_HELP:returnanis[24];break;
}
returnNULL;
}
//以下是添加图元的函数
voidManager::Add(conststring& picfile)
{
pics.push_back(newPicture(picfile.c_str()));
}
voidManager::Add(Picture *ppic,intx,inty,intw,inth)
{
tiles.push_back(newTile(ppic,x,y,w,h));
}
voidManager::Add(intpicid,intx,inty,intw,inth)
{
tiles.push_back(newTile(pics.at(picid),x,y,w,h));
}
voidManager::Add(constvector<int>&tilevct,intcycl,intinterval)
{
Animation* pani=newAnimation(cycl,interval);
anis.push_back(pani);
for(inti=0;i<(int)tilevct.size();i++)
pani->AddTile(tiles.at(tilevct[i]));
}
voidManager::Add(intaniIndex)
{
Animation* pani=newAnimation(0,10);
anis.push_back(pani);
pani->AddTile(tiles.at(aniIndex));
}
//添加MID 背景音乐
voidManager::AddMid(conststring& midfile)
{
Mid* pmid=newMid(midfile);
mids.push_back(pmid);
}
Mid* Manager::PicMid(intid)
{
returnmids.at(id);
}
//播放背景音乐
voidManager::Play(intid)
{
if(curmid==id)return;
if(curmid!=-1)mids.at(curmid)->Stop();
mids.at(id)->Play();
curmid=id;
}
voidManager::Stop()
{
mids.at(curmid)->Stop();
curmid=-1;
}
Mid::Mid(conststring& fname):filename(fname)
{//mid的初始化
MCI_DGV_OPEN_PARMSmciOpen;
MCIERRORmciError;
mciOpen.lpstrDeviceType = "sequencer"; //设备名
mciOpen.lpstrElementName = (char*)filename.c_str();//设备元素
mciError=mciSendCommand(0,MCI_OPEN,MCI_OPEN_TYPE|MCI_OPEN_ELEMENT,(DWORD)&mciOpen);
if(mciError)
{
chars[80];
mciGetErrorString(mciError,s,80);
throwOuter_Error(s);
}
wDeviceID=mciOpen.wDeviceID; //保存设备ID
}
Mid::~Mid()
{
mciSendCommand(wDeviceID,MCI_CLOSE,0,NULL);
}
voidMid::Play()
{
MCI_DGV_PLAY_PARMSmciPlay;
mciSendCommand(wDeviceID, MCI_SEEK, MCI_SEEK_TO_START,NULL);
MCIERRORmciError=mciSendCommand(wDeviceID, MCI_PLAY, MCI_DGV_PLAY_REPEAT,(DWORD)&mciPlay);
if(mciError)
{
chars[80];
mciGetErrorString(mciError,s,80);
throwOuter_Error(s);
}
}
voidMid::Stop()
{
mciSendCommand(wDeviceID,MCI_STOP,0,NULL);
}
卷二:迷宫的设计及实现
一、 系统描述
规则:
迷宫为矩形区域,有一个入口一个出口,至少要有一条路径,并且要有多条死路。
需求分析:
程序实现迷宫的自动生成与自动寻路。迷宫的大小可以设定。程序能够将迷宫和路径画出
二、 功能模块结构
主要模块可以划分为:显示模块、迷宫生成模块、迷宫寻路模块。
| 模块 | 涉及的类或函数 | 功能 |
| 显示模块 | InitInstance函数MyRegisterClass函数 WndProc函数 About函数 SetupDlg函数 | 实现界面的显示 |
| 迷宫生成模块 | Panel类、Block类、NODE类 | 迷宫的自动生成 |
| 迷宫寻路模块 | Panel类、Block类、NODE类 | 迷宫的自动寻路 |
显示模块:
先用MyRegisterClass函数注册窗口类,再用InitInstance创建并显示窗口。窗口完成后必须对其消息进行处理,WndProc即消息处理中心。
当用户单击菜单时,系统会调用WndProc函数,发送WM_COMMAND消息,由此,可以通过接受WM_COMMAND消息实现菜单响应。当窗口需要重画时,窗口会受到WM_PAINT消息,程序在此消息中响应的框架的绘制。
迷宫数据结构:
迷宫用一个Block类对象的矩阵表示,每个Block记录了右方和下方是否有墙,无墙代表连通,有墙代表阻隔。
迷宫生成思路:
首先令所有方块的墙全部关上,在面板中随机选取一个Block,随机选取一个移动方向,每移动一步就将响应的墙打开,并记录走过的位置。行走时随机选取方向,但不能走原来走过的位置,也不能超出面板。用栈来保存路径,每走一步就入栈,每退一步就出栈,遇到死路就出栈,直到最后栈空,所有的方块全部走完。
迷宫寻路思路:
迷宫的寻路方法与生成方法类似,也是用栈保存路径,走一步就入栈,退一步就出栈,遇到死路就退。从迷宫入口开始,遇到迷宫出口结束。
类视图:
三、 迷宫运行效果:
四、 总结:
在迷宫的设计编写过程中,我接触到了许多数据结构和算法方面的知识,例如栈、图等。起初的迷宫我采用的是递归算法,但是我发现递归算法有严重的弊端,当迷宫比较大的时候,递归会很快消耗完资源,从而终止运行。一开始我还以为是算法的问题,调试了很久才发现是资源耗尽了。否定的递归的方案之后,我试着用栈来实现,由于以前很少接触栈,我感到有些吃力,但是最后成功后再回头看栈,发现用栈来实现这类问题是多么简洁和高效。
除此之外,我认识到其实学习程序不应该过于关注语言细节,而应该注重数据结构和算法,注重解决问题的思路与方法。语言细节对于每一门语言都不相同,但是解决问题的方法却是通用的。我们应该学会怎样去提出问题、分析问题、解决问题,而不是将程序的语言作为清规戒律。在制作迷宫的过程中,我提出过很多方案,许多都是中途发现不能再继续下去,但在不懈的努力下,终于实现的预期要求。我想说的是,我们既要勇于探索不怕困难,又要充分考虑方案的可行性。
五、 参考文献:
TheC++ Programming Language——(美)Bjarne Stroustrup著
Windows程序设计——CharlesPetzold著
六、 附录:
程序清单:
//迷宫.cpp
//
#include<windows.h>
#include<stdlib.h>
#include<malloc.h>
#include<memory.h>
#include<tchar.h>
#include"resource.h"
#include"maze.h"
#defineMAX_LOADSTRING 100
//Global Variables:
HINSTANCE hInst; //current instance
TCHAR szTitle[MAX_LOADSTRING]; //The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; //The title bar text
intNUM_X=50;
intNUM_Y=50;
Panel *panel;
//Foward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULTCALLBACK WndProc(HWND,UINT, WPARAM, LPARAM);
LRESULTCALLBACK About(HWND,UINT, WPARAM, LPARAM);
LRESULTCALLBACK SetupDlg(HWND,UINT, WPARAM, LPARAM);
intAPIENTRY WinMain(HINSTANCEhInstance,
HINSTANCEhPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//TODO: Place code here.
MSG msg;
HACCELhAccelTable;
// Initialize global strings
LoadString(hInstance,IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance,IDC_MY, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
panel=newPanel(NUM_X,NUM_Y);
panel->Generate();
// Perform applicationinitialization:
if(!InitInstance (hInstance, nCmdShow))
{
returnFALSE;
}
hAccelTable = LoadAccelerators(hInstance,(LPCTSTR)IDC_MY);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if(!TranslateAccelerator(msg.hwnd,hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
delete panel;
returnmsg.wParam;
}
//注册窗口类
ATOM MyRegisterClass(HINSTANCEhInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style =CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra =0;
wcex.cbWndExtra =0;
wcex.hInstance =hInstance;
wcex.hIcon =LoadIcon(hInstance, (LPCTSTR)IDI_MY);
wcex.hCursor =LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = (LPCSTR)IDC_MY;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm =LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
returnRegisterClassEx(&wcex);
}
BOOL InitInstance(HINSTANCEhInstance,intnCmdShow)
{//创建显示窗口
HWNDhWnd;
hInst = hInstance;//Store instance handle in our global variable
hWnd = CreateWindowEx(NULL,szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT, 800, 600, NULL,NULL, hInstance, NULL);
if (!hWnd)
{
returnFALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
returnTRUE;
}
//处理消息
LRESULTCALLBACKWndProc(HWNDhWnd,UINTmessage, WPARAMwParam, LPARAMlParam)
{
int wmId,wmEvent;
PAINTSTRUCT ps;
HDC hdc;
charinfostr[100];
int width;
int height;
switch (message)
{
caseWM_COMMAND://菜单消息
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parsethe menu selections:
switch(wmId)
{
caseIDM_ABOUT: //关于菜单
DialogBox(hInst,(LPCTSTR)IDD_ABOUTBOX, hWnd,(DLGPROC)About);
break;
caseIDM_SETUP: //设置菜单
DialogBox(hInst,(LPCTSTR)IDD_SETUP,hWnd,(DLGPROC)SetupDlg);
InvalidateRect(hWnd,NULL,true);
break;
caseIDM_EXIT: //退出菜单
DestroyWindow(hWnd);
break;
caseID_MAZE_GENERATE: //生成迷宫菜单
panel->Generate();
InvalidateRect(hWnd,NULL,true);
break;
caseIDM_FINDPATH: //迷宫寻路菜单
panel->FindPath();
InvalidateRect(hWnd,NULL,true);
break;
default:
returnDefWindowProc(hWnd, message,wParam,lParam);
}
break;
caseWM_PAINT: //绘图消息
hdc = BeginPaint(hWnd,&ps);
// TODO:Add any drawing code here...
RECTrt;
GetClientRect(hWnd,&rt);
height=rt.bottom-rt.top-30;
width=height;
//绘制信息
sprintf(infostr,"宽度:%d",NUM_X);
TextOut(hdc,rt.left+height+40,rt.top+10,infostr,strlen(infostr));
sprintf(infostr,"高度:%d",NUM_Y);
TextOut(hdc,rt.left+height+40,rt.top+40,infostr,strlen(infostr));
panel->SetDC(hdc);
panel->Draw(rt.left+10,rt.top+10,width,height);
EndPaint(hWnd,&ps);
break;
caseWM_DESTROY:
PostQuitMessage(0);
break;
default:
returnDefWindowProc(hWnd, message,wParam,lParam);
}
return 0;
}
//关于对话框消息处理
LRESULTCALLBACK About(HWNDhDlg, UINTmessage, WPARAMwParam, LPARAMlParam)
{
switch (message)
{
caseWM_INITDIALOG:
returnTRUE;
caseWM_COMMAND:
if(LOWORD(wParam) == IDOK || LOWORD(wParam)== IDCANCEL)
{
EndDialog(hDlg,LOWORD(wParam));
returnTRUE;
}
break;
}
returnFALSE;
}
//设置对话框消息处理
LRESULTCALLBACKSetupDlg(HWNDhDlg,UINTmessage, WPARAMwParam, LPARAMlParam)
{
int ex,ey;
int wmId,wmEvent;
switch (message)
{
caseWM_INITDIALOG:
SetDlgItemInt(hDlg,IDC_EDIT_X,NUM_X,TRUE);
SetDlgItemInt(hDlg,IDC_EDIT_Y,NUM_Y,TRUE);
returnTRUE;
caseWM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch(wmId)
{
caseIDOK:
ex=GetDlgItemInt(hDlg,IDC_EDIT_X,NULL,TRUE);
ey=GetDlgItemInt(hDlg,IDC_EDIT_Y,NULL,TRUE);
if(ex<2||ex>500||ey<2||ey>500)
{
MessageBox(hDlg,"输入数据有误!","提示",MB_OK);
break;
}
NUM_X=ex;
NUM_Y=ey;
deletepanel;
panel=newPanel(NUM_X,NUM_Y);
panel->Generate();
caseIDCANCEL:
EndDialog(hDlg,wmId);
returnTRUE;
}
break;
}
returnFALSE;
}
//*********************maze.h***********************
#include<windows.h>
#include<stack>
#include<vector>
usingstd::stack;
usingstd::vector;
enumDIRECTION{RIGHT=0,BOTTOM=1,LEFT=2,TOP=3};
classBlock //方块类,把迷宫面板分为许多小方块后,每个方块的类
{
boolrightwall;
boolbottomwall;
Block*neighbour[4];
long flag;
public:
staticHDC pDC;
Block():rightwall(true),bottomwall(true)
{};
void Init(Block* rightneb,Block* bottomneb,Block* leftneb,Block* topneb)
{
neighbour[RIGHT]=rightneb; neighbour[BOTTOM]=bottomneb;
neighbour[LEFT] =leftneb; neighbour[TOP] =topneb;
}
//设置墙
voidSetWall(boolrwall=true,boolbwall=true)
{rightwall=rwall;bottomwall=bwall;}
voidSetWall(int direction,boolwall=true);
voidSetRWall(boolrwall=true){rightwall=rwall;}
voidSetBWall(boolbwall=true){bottomwall=bwall;}
//是否有墙
bool rw()const{returnrightwall;}
bool bw()const{returnbottomwall;}
bool lw()const{returnneighbour[LEFT]?neighbour[LEFT]->rw():true;}
bool tw()const{returnneighbour[TOP]?neighbour[TOP]->bw():true;}
//获取下一个方块
Block* Neb(intdirection){returnneighbour[direction];};
boolBlock::wall(intdirection);
bool OnWall(intdirection) const{returnneighbour[direction]==NULL?true:false;}
voidRandSet(){rightwall=rand()%2==0?false:true;bottomwall=rand()%2==0?false:true;}
//设置标志
bool Flag(intbit=0)const{returnflag&(1<<bit)?true:false;}
voidSetFlag(intbit=0,boolflg=true)
{
if(flg)flag|=(1<<bit);
elseflag&= ~(1<<bit);
}
void ClearFlag(){flag=0;}
int GoNext(intnoflg=0);//走到下一个方块
intFindNext(int noflg=1);//寻下一个路
void Draw(intx,int y,intw,int h);
};
classNODE//用来表示方块的x、y坐标
{
int x,y;
public:
NODE(){}
NODE(intvx,intvy):x(vx),y(vy){}
int GetX()const{returnx;}
int GetY()const{returny;}
void Set(intvx,intvy){x=vx;y=vy;}
};
classPanel//面板类,对整个迷宫操作
{
intxnum,ynum;
Block**blocks;
HDC pDC;
voidClearFlag(int bit);
voidClearFlag();
vector<NODE> path;
public:
voidFindPath(); //寻路
Panel(int nx,intny);
~Panel();
voidGenerate(); //生成迷宫
void Draw(intx,int y,intw,int h);
void SetDC(HDChdc){pDC=hdc;}
};
//****************maze.cpp**************************
#include"maze.h"
#include<stack>
#include<vector>
usingstd::stack;
usingstd::vector;
HDCBlock::pDC=NULL;
voidBlock::Draw(intx,inty,intw,inth)
{
if(!OnWall(RIGHT)&&rw())
{
MoveToEx(pDC,x+w,y,NULL);
LineTo(pDC,x+w,y+h);
}
if(!OnWall(BOTTOM)&&bw())
{
MoveToEx(pDC,x,y+h,NULL);
LineTo(pDC,x+w,y+h);
}
}
voidBlock::SetWall(intdirection,boolwall)
{//通过方向形参设置墙的有无
switch(direction)
{
caseRIGHT:
rightwall=wall;
break;
caseBOTTOM:
bottomwall=wall;
break;
caseLEFT:
if(neighbour[LEFT])neighbour[LEFT]->SetRWall(wall);
break;
caseTOP:
if(neighbour[TOP])neighbour[TOP]->SetBWall(wall);
break;
}
}
boolBlock::wall(intdirection)
{//通过方向形参判断该方向是否有墙
switch(direction)
{
caseRIGHT:return rw();break;
caseBOTTOM:return bw();break;
caseLEFT:return lw();break;
caseTOP:return tw();break;
}
returntrue;
}
intBlock::GoNext(intnoflg)
{//生成迷宫时走到下一步
vector<int>nxtblk;
for(inti=0;i<4;i++)
if(neighbour[i]&&!neighbour[i]->Flag(noflg))
nxtblk.push_back(i);
if(nxtblk.size()<=0)return-1;
intsel=nxtblk[rand()%nxtblk.size()];
return sel;
}
intBlock::FindNext(intnoflg)
{//寻路时走到下一步
SetFlag(noflg);
for(inti=0;i<4;i++)
if(!wall(i)&&!neighbour[i]->Flag(noflg))
returni;
return -1;
}
Panel::Panel(intnx,intny)
:xnum(nx),ynum(ny)
{//生成方块二维数组
blocks=newBlock*[ynum];
for(inti=0;i<ynum;i++)
blocks[i]=newBlock[xnum];
for(intiy=0;iy<ynum;iy++)
for(intix=0;ix<xnum;ix++)
blocks[iy][ix].Init(ix<xnum-1?&blocks[iy][ix+1]:NULL,
iy<ynum-1?&blocks[iy+1][ix]:NULL,
ix>0?&blocks[iy][ix-1]:NULL,
iy>0?&blocks[iy-1][ix]:NULL);
ClearFlag();
}
Panel::~Panel()
{
for(inti=0;i<ynum;i++)
delete[]blocks[i];
}
voidPanel::Generate()//生成迷宫
{
stack<Block*> bs;
for(intiy=0;iy<ynum;iy++)
for(intix=0;ix<xnum;ix++)
blocks[iy][ix].SetWall();
ClearFlag(0);
intfstx=rand()%xnum;
intfsty=rand()%ynum;
blocks[fsty][fstx].SetFlag();
bs.push(&blocks[fsty][fstx]);
while(!bs.empty())
{
Block*curblk=bs.top();
intnxtdrt=curblk->GoNext();
if(nxtdrt!=-1)
{
curblk->SetWall(nxtdrt,false);
curblk->Neb(nxtdrt)->SetFlag(0);
curblk=curblk->Neb(nxtdrt);
bs.push(curblk);//走一步就入栈
}
else
{//死路出栈
bs.pop();
}
}
while(!path.empty())
path.pop_back();
ClearFlag(1);
}
voidPanel::Draw(intx,inty,intw,inth)
{
//绘制迷宫和路径
HPENhp=CreatePen(PS_SOLID,1,RGB(0,0,0));
SelectObject(pDC,hp);
int blkw=w/xnum;
int blkh=h/ynum;
//draw frame
Rectangle(pDC,x,y,x+blkw*xnum,y+blkh*ynum);
HBRUSHbrush=CreateSolidBrush(RGB(255,0,0));
SelectObject(pDC,brush);
Ellipse(pDC,x,y,x+blkw,y+blkh);
Ellipse(pDC,x+blkw*(xnum-1),y+blkh*(ynum-1),x+blkw*xnum,y+blkh*ynum);
//draw block
Block::pDC=pDC;
for(intiy=0;iy<ynum;iy++)
for(intix=0;ix<xnum;ix++)
blocks[iy][ix].Draw(x+blkw*ix,y+blkh*iy,blkw,blkh);
HPENpathpen=CreatePen(PS_SOLID,xnum>=100?1:2,RGB(255,0,0));
SelectObject(pDC,pathpen);
//draw path
for(inti=0;i<path.size();i++)
{
intcx=x+blkw*(path[i].GetX()+0.5);
intcy=y+blkh*(path[i].GetY()+0.5);
if(i==0)MoveToEx(pDC,cx,cy,NULL);
elseLineTo(pDC,cx,cy);
}
DeleteObject(brush);
DeleteObject(pathpen);
DeleteObject(hp);
}
voidPanel::ClearFlag(intbit)
{
for(intiy=0;iy<ynum;iy++)
for(intix=0;ix<xnum;ix++)
blocks[iy][ix].SetFlag(bit,false);
}
voidPanel::ClearFlag()
{
for(intiy=0;iy<ynum;iy++)
for(intix=0;ix<xnum;ix++)
blocks[iy][ix].ClearFlag();
}
voidPanel::FindPath()
{//寻路
int x,y;
x=0;y=0;
while(!path.empty())
path.pop_back();
ClearFlag(1);
path.push_back(NODE(0,0));
while(!path.empty())
{
NODEnode=path.back();
x=node.GetX();
y=node.GetY();
intdrt=blocks[y][x].FindNext();
switch(drt)
{
caseRIGHT:x++;break;
caseBOTTOM:y++;break;
caseLEFT:x--;break;
caseTOP:y--;break;
}
if(drt==-1)path.pop_back();
elsepath.push_back(NODE(x,y));
if(x==xnum-1&&y==ynum-1)
break;
}
}
本文详细介绍了使用C++进行贪吃蛇和迷宫游戏的设计与实现,涵盖了游戏规则、需求分析、功能模块设计、算法说明、游戏运行效果以及总结。在贪吃蛇游戏部分,涉及游戏控制、资源管理、交互元素等多个模块;迷宫部分则讨论了迷宫数据结构、生成与寻路算法。整个项目展示了C++在游戏开发中的应用,强调了软件工程中的模块化设计和错误处理的重要性。

424

被折叠的 条评论
为什么被折叠?



