程序设计创新试验doc.docx
《程序设计创新试验doc.docx》由会员分享,可在线阅读,更多相关《程序设计创新试验doc.docx(16页珍藏版)》请在冰豆网上搜索。
程序设计创新试验doc
西安交通大学创新实验报告
课程名称
VC++案例开发
系别
电信学院
实验报告日期
2004年3月28日
专业班号
计算机63
姓名
刘侯刚
学号
06055066
一.项目名称
一个简单的小游戏:
贪食蛇
二.试验运行环境
采用的软件及开发平台
MicosoftVisual++6.0
三.游戏的简述
贪食蛇的玩法很简单,用键盘上的方向键控制贪食蛇的运动方向,游戏的目的是控制贪食蛇吃掉更多的食品,但是不能碰到四周的墙壁,也不能让贪食蛇的首和尾相接,否则游戏失败
四.游戏的结构程序设计流程及部分代码
找规律,建立对象模型:
首先从物理的角度来分析蛇得运动规律。
它是蠕动的,像水一样流动。
他有个特性就是身体的每一个点都经过他头部所在的点,他每一个时刻骨节所要运动的目的地是他前一个骨节所在的位置。
不难看出,其中的对象模型为
蛇,骨骼,骨节,头
抽象的接口为
蛇,骨骼,骨节,头,图元
结构图:
接口结构图
骨骼骨骼
对象结构图
给接口添加行为:
图元(GraphicObject)
绘画:
进行图元自身的绘画操作
当前矩形:
得到图元当前所在的矩形,也可考虑使用region,这样使得对图元范围得描述更加具体。
骨节(Condyle)
前一个骨节:
提供前序访问
后一个骨节:
提供后序访问
流动:
进行骨节运动诡计得计算。
骨骼(Cadre)(管理骨节链)
得到头:
得到蛇头
得到尾:
得到蛇尾
新增骨节:
在骨节链中新增骨骼
删除骨节:
在骨节链中删掉指定骨节
头(head)
方向:
能够设定和访问当前运动方向。
蛇(Snake)
增长:
蛇进行自身得增长
完善模型,引入访问者模式
存在的问题:
当前是由具体的骨骼对象来负责图元的绘画以及处理流动的,而骨骼只是管理骨节,没有提供任何访问骨节的方法,我们应该怎样访问骨节呢?
比如我们要每个骨节画出自己的形态,我们可能会这样做。
在骨骼上提供一个绘画方法,然后通过遍历骨节链,调用每个骨节的绘画方法。
这样似乎可以解决问题。
那么,我们要进行骨节流动计算呢?
我们再在骨骼上提供流动方法,然后同样的遍历骨节,调用每个的流动方法。
我们要是加入统计骨节个数功能呢?
我们要是加入其他的需要遍历骨骼的方法呢?
……
这样做的缺点是什么?
1、相似的代码有多份
2、如果骨节的访问结构发生变化,后果会怎样?
3、有新功能需要遍历骨节链该怎么办?
这样会迫使我们拷贝代码.
解决办法,引入访问者模式:
新增加接口如下:
加入行为:
访问者:
(空,无方法,属性)
被访问者:
接受访问者
骨节访问者:
继承自访问者
访问骨节
完整的结构图:
接口结构图
对象结构图
程序的部分代码:
voidCSnakeDlg:
:
OnTimer(UINTnIDEvent)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
m_snake.KeepMoving();//保持原方向移动一步
if(!
m_snake.IsTouch(MAX_X,MAX_Y))
{
//撞墙处理
KillTimer
(1);
MessageBox("刘侯刚为你打气,再来一次!
");
InitGame();
}
else
{
//不撞墙处理
//吃到食物处理
if(m_snake.IsEat(m_food))
{
//蛇变长
m_snake.Grow();
//放置新食物
m_food=SetFood();
}
//显示蛇
ShowSnake();
}
//如果达到最大长度,即过关
if(m_snake.GetBodyLen()==MAX_SNAKE_LEN)
{
KillTimer
(1);
m_time=m_time-30;//下一关,时间间隔减少30MS
//结局
if(m_time<=0)
{
MessageBox("刘侯刚对你的敬仰之情有如滔滔江水连绵不绝,黄河泛滥一发不可收拾!
---THEEND");
return;
}
//挑战下一关
MessageBox("鄙人刘侯刚建议您向下一级挑战!
");
InitGame();
}
}
voidCSnakeDlg:
:
OnKeyDown(UINTnChar,UINTnRepCnt,UINTnFlags)
{
//TODO:
Addyourmessagehandlercodehereand/orcalldefault
//游戏的控制
switch(nChar)
{
case'W':
caseVK_UP:
m_snake.MoveUp();
break;
case'S':
caseVK_DOWN:
m_snake.MoveDown();
break;
case'A':
caseVK_LEFT:
m_snake.MoveLeft();
break;
case'D':
caseVK_RIGHT:
m_snake.MoveRight();
break;
caseVK_ESCAPE:
EndDialog
(1);//退出程序
break;
default:
;
}
CDialog:
:
OnKeyDown(nChar,nRepCnt,nFlags);
}
voidCSnakeDlg:
:
ShowSnake()
{
//蛇身包含着坐标,把坐标对应的方块显示出来即可
intlen=m_snake.GetBodyLen();
for(inti=0;i{
m_map[m_snake.m_body[i].x][m_snake.m_body[i].y].ShowWindow(SW_SHOW);
}
//隐藏上一步的尾巴
m_map[m_snake.m_preEnd.x][m_snake.m_preEnd.y].ShowWindow(SW_HIDE);
}
voidCSnakeDlg:
:
BreakGame()
{
if(m_isRun)
{
KillTimer
(1);
m_isRun=false;
}
else
{
SetTimer(1,m_time,NULL);
m_isRun=true;
}
}
voidCSnakeDlg:
:
InitGame()
{
m_isRun=false;
//全部方块隐藏
for(inti=0;i{
for(intj=0;j{
m_map[i][j].ShowWindow(SW_HIDE);
}
}
//初始化蛇
m_snake.InitSnake(MAX_X,MAX_Y);
//显示蛇
ShowSnake();
//放置食物
m_food=SetFood();
}
CPointCSnakeDlg:
:
SetFood()
{
intx=0;
inty=0;
CPointfood;
boolflag=true;
//随机产生,合适的坐标
while(flag)
{
srand(time(NULL));
x=rand()%MAX_X;
y=rand()%MAX_Y;
food.x=x;
food.y=y;
for(inti=0;i{
if(food!
=m_snake.m_body[i])//坐标不能与蛇身重合
flag=false;
}
}
//把该坐标对应的方块显示,作为食物
m_map[x][y].ShowWindow(SW_SHOW);
returnfood;
}
BOOLCSnakeDlg:
:
PreTranslateMessage(MSG*lpmsg)
{
//对话框默认屏蔽了方向键的消息响应,在这里要处理一下
switch(lpmsg->message)
{
caseWM_KEYDOWN:
switch(lpmsg->wParam)
{
caseVK_UP:
caseVK_DOWN:
caseVK_LEFT:
caseVK_RIGHT:
OnKeyDown(lpmsg->wParam,LOWORD(lpmsg->lParam),HIWORD(lpmsg->lParam));
break;
default:
;
}
default:
;
}
returnCDialog:
:
PreTranslateMessage(lpmsg);
}
voidCSnakeDlg:
:
OnOK()//回车键消息响应
{
BreakGame();//暂停||开始游戏
//CDialog:
:
OnOK();
}
五.试验结果
六.个人小结
(1)此次实验中我感受最深的是,CWnd类库的无可替代性,并在一定的努力下,对此类库有了一定的了解:
软为了WINDOWS程序设计的方面,提供了各种各样的类库,CWnd类库和其他派生的给大类库是最常见的类库了。
仔细研究这个类的使用大大减少了程序设计的复杂程度,CWnd派生出来的类主要有对话框、视类和控制类。
其中对话框就有各种各样的形式,对于文件操作和字体设定带来方便。
例如通过使用CFileDialog类来获得文件的名字:
CFileDialogfileDlg;
if(fileDlg.DoModal()==IDOK)
{
CStringstr;
str=fileDlg.GetPathName()+fileDlg.GetFileName()+fileDlg.GetFileExt();
return;
}
这样每次执行完成这段代码,str中保存的就是放回的文件的路径和文件名。
如果str定义的是一个全局变量,那么其他地方都可以使用文件对话框操作的结果了。
(2)在此次实验中遇到了一些问题,没能解决,只好绕道而行,现在选其二如下
第一个问题:
BOOL CMyWinApp:
:
InitInstance()
{
cout << "CMyWinApp:
:
InitInstance \n";
m_pMainWnd = new CMyFrameWnd; // 引发CMyFrameWnd:
:
CMyFrameWnd 构造函数
return TRUE;
}
这个地方引发CMyFrameWnd:
:
CMyFrameWnd构造函数那么其父类构造函数为什么没有被授衔调用呢?
而在Frame1中确实自下而上引发的啊。
第二个问题:
在Frame2中还有
BOOL CWnd:
:
CreateEx()
{
cout << "CWnd:
:
CreateEx \n";// 这是一個虚拟函式,CWnd 中有定义,CFrameWnd 也改写了
PreCreateWindow(); 这里到底是呼叫 CWnd:
:
PreCreateWindow 还是
return TRUE; // CFrameWnd:
:
PreCreateWindow 呢?
}
这点我有迷茫了,既然是由CFrameWnd的构造函数调用引起的,在CMyFrameWnd中又没有改写PreCreateWindow
那么肯定是调用CFrameWnd中定义的函数啊!
怎么会牵扯到Object slicing呢?