一、对于图形游戏的分析
对游戏图形进行简化,该游戏主要是有正方形的方块构成,图形主要有,我们分别用不同颜色的方块来表示这些元素
1. 背景 (白色) WHITH 15
2. 墙 (浅灰) LIGHTGRAY 7
3. 人 (蓝色) BLUE 1
4. 位置 (红色) RED 4
5. 没有在位置上的箱子(绿色) GREEN 2
6. 在位置上的箱子(黄色)YELLOW 14
7. 在位置上的人 (淡蓝) LIGHTBLUE
由于简化后的图形所以绘图程序只需要一个函数
void FillRect(int x0,int y0,int x1,int y1,int color)
{
int x,y;
for(x=x0;x<x1;x+=2)
{
for(y=y0;y<y1;y+=2)
{
putpixel(x,y,color);
}
}
}
二、对游戏逻辑进行分析
1.游戏过程的描述
Step1:玩家打开程序进入游戏
Step2:玩家按’W’,’A’,’S’,’D’键控制人物推动方块,如果按下’Q’键退出游戏
Step3:如果没有将所有的方块移动到位置上则继续Step2
Step4:游戏胜利显示胜利信息
Step5:退出游戏
2.流程图
3.游戏中的数据存放在一个二维矩阵中(8*8)
#define MAXSIZE 8
int matrix[MAXSIZE][ MAXSIZE];
由于箱子和位置有重复的时候(箱子在位置上),所以我们用int的一个二进制位来代表该位置上是否有元素。
这里我们约定:
0x0000 该位置上什么也没有(只是背景)
0x0001 该位置有是墙
0x0002 该位置有是人
0x0004 该位置有箱子
0x0008 该位置有位置
先初始化这个矩阵
void initMatrix(int level)
{
int i , j;
int map[1][MAXSIZE][MAXSIZE]={
{
{0,1,1,1,1,1},
{0,1,0,2,0,1,1,1},
{1,1,0,1,4,0,0,1},
{1,0,12,8,0,8,0,1},
{1,0,0,4,4,0,1,1},
{1,1,1,0,1,8,1},
{0,0,1,0,0,0,1},
{0,0,1,1,1,1,1},
}
};
for(i=0;i<MAXSIZE;i++)
{
for(j=0;j<MAXSIZE;j++)
{
matrix[i][j] = map[level][j][i];
}
}
}
所以这里先确定一个draw方法来绘制这个矩阵(界面)
先定义一个格子的大小
#define BOXSIZE 40
void draw()
{
int i , j;
for (i = 0 ; i < MAXSIZE ; i ++)
{
for(j = 0 ;j <MAXSIZE;j++)
{
int color = BLACK;
switch(matrix[i][j])
{
case 0 : /*背景用白色*/
color = 15;break;
case 1:/*墙用灰色*/
color = LIGHTGRAY;break;
case 2:/*人用蓝色*/
color = BLUE;break;
case 4:/*箱子用绿色*/
color = GREEN;break;
case 8:/*位置用红色*/
color = RED;break;
case 12:/*在位置上的箱子用黄色*/
color = YELLOW;break;
case 10:/*人在位置上淡蓝*/
color = LIGHTBLUE;break;
}
/*确定在屏幕上的位置并绘画出来*/
FillRect(i*BOXSIZE,j*BOXSIZE,(i+1)*BOXSIZE,(j+1)*BOXSIZE,color);
}
}
}对于接受的输入我们用一个cmd变量来存储
char cmd;
在游戏中能移动的有人和箱子我们统一一个函数来移动,移动箱子和人其实就是改变矩阵中的数据。
这里我们先看看有情况:
1. 人可以推动一个箱子(也就是所人所推动的箱子的前面不能有箱子);
2. 人不能穿墙(人的前面是墙,则人不能移动)
3. 箱子不能穿墙(箱子前面是墙,则箱子不能移动);
4. 箱子不能推动箱子
/*移动 dir 位方向 ‘w’为上 ‘a’ 为左 ‘s’为下 ‘d’为右 i,j 确定要移动的位置
返回是否移动成功
*/
int move(char dir,int i,int j)
{
int ni = i ,nj = j;
/*计算要移动到的位置*/
switch(dir)
{
case 'w': nj--;break;
case 'a': ni--;break;
case 's': nj++;break;
case 'd': ni++;break;
}
/*如果前面是墙 则人和箱子都不能移动*/
if((matrix[ni][nj] & 1) == 1)
{
return 0;
}
/*当前要移动的是人*/
if((matrix[i][j] & 2) == 2)
{
int flag = 1;
/*如果前面是箱子*/
if((matrix[ni][nj] & 4) == 4){
/*移动箱子 看是否成功*/
flag = move(dir,ni,nj);
}
if(flag)
{
/*如果成功则一个人*/
/*将前面的位置标志加上人*/
matrix[ni][nj] = matrix[ni][nj] | 2;
/*将本地的位置去除人*/
matrix[i][j] = matrix[i][j] & ~2;
return 1;/*移动成功*/
}
}
/*当前要移动的是箱子*/
if((matrix[i][j] & 4)==4)
{
/*如果前面是箱子 不能移动*/
if((matrix[ni][nj] & 4) == 4)return 0;
/*将前面的位置标志加上箱子*/
matrix[ni][nj] = matrix[ni][nj] | 4;
/*将本地的位置去除箱子*/
matrix[i][j] = matrix[i][j] & ~4;
return 1;/*移动成功*/
}
}
然后我们还要检查是否成功
如果matrix矩阵中没有一个是单独的箱子那么就成功了
int check()
{
int i ,j;
for(i=0;i<MAXSIZE;i++)
{
for(j=0;j<MAXSIZE;j++)
{
/*如果有单独的箱子 没有成功*/
if(matrix[i][j] == 4)return 0;
}
}
/*都没有 就成功了*/
return 1;
}
在写这个函数写一个移动人的函数
void moveHero(char dir)
{
int i,j;
int x,y;
/*先要找到人的位置*/
for(i=0;i<MAXSIZE;i++)
{
for(j=0;j<MAXSIZE;j++)
{
if((matrix[i][j] & 2) == 2){
x = i ;
y = j ;
break;
}
}
}
move(dir,x,y);
}
这里我们来处理游戏逻辑(主函数中的代码)
int main()
{
int graphdirver = DETECT;
int graphmode = 0;
char cmd;
initgraph(&graphdirver,&graphmode,"C:\\JMSOFT\\CYuYan\\tc3\\BGI");
initMatrix(0);
cleardevice();
while(1){
draw();
cmd = getch();
switch(cmd)
{
case 'q':exit(0);break;
case 'w':
case 'a':
case 's':
case 'd':
moveHero(cmd);
/*检查是否成功*/
if(check())
{
printf("胜利");
}
break;
}
}
closegraph();
}
三、还没有实现的功能 ,以及缺陷
1.悔步的功能,该功能可以用一个栈来存储移动步骤,然后通过这个历史栈来撤销操作。
2.关卡功能 ,在initMatrix函数中已经预留了关卡功能,只要在map中添加关卡地图,然后再修改下 游戏的逻辑便可以实现。
3.缺陷。由于屏幕的刷新产生的 描述现象
四.题目 以供大家交流学习
1)。请组装以上代码 ,在tc2012环境中运行
2)。替换元素
原方案是:
不同颜色的方块来表示这些元素
1背景 (白色) WHITH 15
2墙 (浅灰) LIGHTGRAY 7
3人 (蓝色) BLUE 1
4位置 (红色) RED 4
5没有在位置上的箱子(绿色) GREEN 2
6在位置上的箱子(黄色)YELLOW 14
7在位置上的人 (淡蓝) LIGHTBLUE
现在请你:
修改FillRect函数,分别用 矩形代表墙 圆形代表人 棱形代表位置 三角形代表箱子来表示这些元素,以降低putpixel的调用次数
3)。实现关卡功能
提示:只需在initMatrix函数中添加map地图数据
然后当游戏胜利后调用initMatrix(级别)就可以了,
不过你可以实现一个选择关卡的功能
4)。实现悔步功能
提示:用栈来存储 这涉及数据结构的知识
由于每次操作都可以使人和箱子移动则 这个栈里面的数据结构可以定义为
#define MAXHISTORY 100
typedef struct operate
{
char dir;/*方向*/
int hero;/*人是否移动过*/
int box;/*箱子是否移动过*/
}OPERATE;
typedef struct stack
{
int top;
OPERATE[MAXHISTORY] data;
}STACK;
然后游戏逻辑和栈的操作就该你写了(我的思想也不一定对,没有实现过)
附录:
以下是这个库所支持的函数列表。恩,仅仅是列表。
void initgraph(int Width, int Height); // 初始化图形环境
void initgraph(int Width, int Height, int Flag);
void closegraph(); // 关闭图形环境
void cleardevice(); // 清屏
COLORREF getcolor(); // 获取当前绘图前景色
void setcolor(COLORREF color); // 设置当前绘图前景色
COLORREF getbkcolor(); // 获取当前绘图背景色
void setbkcolor(COLORREF color); // 设置当前绘图背景色
void getviewsettings(struct viewporttype *viewport); // 获取视图信息
void setviewport(int left, int top, int right, int bottom, int clip); // 设置视图
void clearviewport(); // 清空视图
void getlinesettings(struct linesettingstype *lineinfo); // 获取当前线形
void setlinestyle(int linestyle, unsigned int upattern, int thickness); //
设置当前线形
void getfillsettings(struct fillsettingstype *fillinfo); // 获取填充类型
void setfillstyle(int pattern, int color); // 设置填充类型
void getfillpattern(char *pattern); // 获取自定义填充类型
void setfillpattern(const char *upattern, int color); // 设置自定义填充类型
void getaspectratio(int *xasp, int *yasp); // 获取当前缩放因子
void setaspectratio(int xasp, int yasp); // 设置当前缩放因子
void setwritemode(int mode); // 设置绘图位操作模式
void graphdefaults(); // 重置所有绘图设置为默认值
COLORREF getpixel(int x, int y); // 获取点的颜色
void putpixel(int x, int y, COLORREF color); // 画点
void moveto(int x, int y); // 移动当前点(绝对坐标)
void moverel(int dx, int dy); // 移动当前点(相对坐标)
void line(int x1, int y1, int x2, int y2); // 画线
void linerel(int dx, int dy); // 画线(至相对坐标)
void lineto(int x, int y); // 画线(至绝对坐标)
void rectangle(int left, int top, int right, int bottom); // 画矩形
void getarccoords(struct arccoordstype *arccoords); // 获取圆弧坐标信息
void arc(int x, int y, int stangle, int endangle, int radius); // 画圆弧
void circle(int x, int y, int radius); // 画圆
void pieslice(int x, int y, int stangle, int endangle, int radius); // 画填充圆扇形
void ellipse(int x, int y, int stangle, int endangle, int xradius, int yradius);//
画椭圆弧线
void fillellipse(int x, int y, int xradius, int yradius); // 画填充椭圆
void sector(int x, int y, int stangle, int endangle, int xradius, int yradius); //
画填充椭圆扇形
void bar(int left, int top, int right, int bottom); // 画无边框填充矩形
void bar3d(int left, int top, int right, int bottom, int depth, int topflag); //
画有边框三维填充矩形
void drawpoly(int numpoints, const int *polypoints); // 画多边形
void fillpoly(int numpoints, const int *polypoints); // 画填充的多边形
void floodfill(int x, int y, int border); // 填充区域
void outtext(LPCTSTR textstring); // 在当前位置输出文字
void outtextxy(int x, int y, LPCTSTR textstring); // 在指定位置输出文字
int textwidth(LPCTSTR textstring); // 获取字符串占用的像素宽
int textheight(LPCTSTR textstring); // 获取字符串占用的像素高
void SetFont(int nHeight,int nWidth,int nEscapement,int nOrientation,int fnWeight,BYTE fdwItalic,BYTE fdwUnderline,BYTE fdwStrikeOut,LPCTSTR lpszFace); //
设置当前字体样式
void SetFont(const LOGFONT *font); // 设置当前字体样式
void GetFont(LOGFONT *font); // 获取当前字体样式
void getimage(int left, int top, int right, int bottom, IMAGE *imgdst); //
从屏幕获取图像
void getimage(const char *imagefile, IMAGE *imgdst); // 从 BMP
文件获取图像
void getimage(const IMAGE *imgsrc, int left, int top, int right, int bottom, IMAGE *imgdst); //
从 IMAGE
对象获取图像
void putimage(int left, int top, IMAGE *img, int op); // 绘制图像
int getmaxcolor(); // 获取最大颜色值
int getmaxx(); // 获取最大 x
坐标
int getmaxy(); // 获取最大 y
坐标
int getx(); // 获取当前 x
坐标
int gety(); // 获取当前 y
坐标
int GetVer(); // 获取当前版本