坦克大战游戏详细设计说明.docx
《坦克大战游戏详细设计说明.docx》由会员分享,可在线阅读,更多相关《坦克大战游戏详细设计说明.docx(29页珍藏版)》请在冰豆网上搜索。
坦克大战游戏详细设计说明
单机版坦克大战游戏系统
—详细设计
1.引言………………………………………………………………………………………2
1.1编写目的…………………………………………………………………………2
1.2项目背景…………………………………………………………………………2
1.3定义………………………………………………………………………………2
1.4参考资料…………………………………………………………………………2
2.总体设计…………………………………………………………………………………3
2.1需求概述…………………………………………………………………………3
3.程序描述…………………………………………………………………………………4
3.1程序逻辑…………………………………………………………………………4
3.1.1主类模块…………………………………………………………………4
3.1.2坦克类模块………………………………………………………………4
3.1.3子弹类模块………………………………………………………………12
3.1.4爆炸类模块………………………………………………………………14
3.1.5墙类模块…………………………………………………………………16
3.1.6血块类模块………………………………………………………………16
3.3存储分配………………………………………………………………………17
3.4限制条件………………………………………………………………………18
3.5测试要点………………………………………………………………………18
1.引言
随着社会的进步,现在人们工作的压力越来越大,休息的时候很少,为了让疲惫的心灵得以休息,开发了坦克大战的小游戏来让玩家轻松一会,该游戏操作十分简单,只要操作键盘上的方向键按住攻击键就可以玩该游戏,轻松享受玩游戏的喜悦,可操作性好,而且不会使玩家沉溺于该游戏,对玩家的学习和工作都起积极的作用。
1.1编写目的
为明确软件需求、安排项目规划与进度、组织软件开发与测试,撰写本文档。
本文档供项目经理、设计人员、开发人员参考。
1.2项目背景
a.项目名称:
坦克大战游戏开发
b.产品用户:
对该游戏感兴趣的玩家
c.项目和系统的关系:
本项目旨在编写出一个操作简单但可玩性良好的小游戏来让玩家放松一下心情,对系统硬件配置要求低。
实现简单。
1.3定义
持有对方的引用:
在一个类里面有一个成员变量是另一个类的对象,这个对象相对于这个类来说就是持有了另一个类的引用,通过这个对象作成员变量可以引用对象这个类的成员变量和成员方法。
内部类:
写在一个类内部的类是内部类,内部类封装在类的内部,不能被其他的外部类直接访问,起到保护作用,但要写的简短。
1.4参考资料
《Java基础知识详解》
《Java游戏开发》等。
2.总体设计
2.1需求概述
游戏包括的范围:
本游戏包括主类、坦克类、子弹类、爆炸类、墙类、血块类。
要求游戏能有效、快速、安全、可靠和无误的完成上述操作。
并要求客户端的界面要简单明了、易于操作,服务器程序利于维护。
需求
模块
产生游戏的屏幕
主类模块
将坦克大战的过程呈现在屏幕上
画出坦克
坦克类模块
控制坦克的移动并让坦克可以攻击
让坦克不可以穿越墙或别的坦克
添加坦克的图片
画出子弹
子弹类模块
控制子弹的移动
详细查看员工信息
让子弹不可以穿墙
添加子弹的图片
添加培训信息
爆炸类模块
画出爆炸的过程
控制坦克的爆炸
添加爆炸的图片
画出墙
墙类模块
实现墙的功能
画出血块
血块类模块
让血块围绕着固定的轨迹移动
实现血块的功能
3.程序描述
3.1程序逻辑
3.1.1主类模块
主类是要画出屏幕,并且要将坦克之间打斗的全过程都呈现出来的,所以要先产生一个窗口,窗口产生之后应该要不停的刷新,否则屏幕会定格成画面,用多线程来控制刷新的频率,如果此时屏幕出现闪屏现象,应该用双缓冲机制来画出屏幕。
双缓冲机制是通过虚拟出一张图片,将屏幕画在这张虚拟的图片上,再将这张虚拟的图片呈现在屏幕上。
调用的方法如下:
publicvoidupdate(Graphicsg){
if(offScreenImage==null){
offScreenImage=this.createImage(GAME_WIDTH,GAME_HEIGHT);
}
GraphicsgOffScreen=offScreenImage.getGraphics();
Colorc=gOffScreen.getColor();
gOffScreen.setColor(Color.GREEN);
gOffScreen.fillRect(0,0,GAME_WIDTH,GAME_HEIGHT);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage,0,0,null);
}
用多线程来控制屏幕刷新的频率的方法如下:
privateclassPaintThreadimplementsRunnable{
publicvoidrun(){
while(true){
try{
repaint();
Thread.sleep((int)Math.random()*200);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
}
控制其他模块的操作都是在主类中调用其他类的方法即可。
3.1.2坦克类模块
该类实现在屏幕上画出坦克,并控制坦克的移动,让坦克可以发射炮弹打对方等功能。
画出坦克的方法如下:
publicvoiddraw(Graphicsg){
Colorc=g.getColor();
if(good)g.setColor(Color.RED);
elseg.setColor(Color.BLUE);
g.fillOval(x,y,WIDTH,HEIGHT);
g.setColor(c);
move();
switch(ptDir){
caseL:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x,y+Tank.HEIGHT/2);
break;
caseLU:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x,y);
break;
caseLD:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x,y+Tank.HEIGHT);
break;
caseR:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x+Tank.WIDTH,y+Tank.HEIGHT/2);
break;
caseRU:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x+Tank.WIDTH,y);
break;
caseRD:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x+Tank.WIDTH,y+Tank.HEIGHT);
break;
caseU:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x+Tank.WIDTH/2,y);
break;
caseD:
g.drawLine(x+Tank.WIDTH/2,y+Tank.HEIGHT/2,x+Tank.WIDTH/2,y+Tank.HEIGHT);
break;
}
}
该方法里实现了一个move方法,可以将坦克的移动都画出来,坦克的move方法如下:
publicvoidmove(){
switch(dir){
caseL:
x-=XSPEED;
break;
caseLU:
x-=XSPEED;
y-=YSPEED;
break;
caseLD:
x-=XSPEED;
y+=XSPEED;
break;
caseR:
x+=XSPEED;
break;
caseRU:
x+=XSPEED;
y-=YSPEED;
break;
caseRD:
x+=XSPEED;
y+=YSPEED;
break;
caseU:
y-=YSPEED;
break;
caseD:
y+=YSPEED;
break;
caseSTOP:
break;
}
if(dir!
=Direction.STOP){
this.ptDir=dir;
}
if(x<0)x=0;
if(y<30)y=30;
if(x+Tank.WIDTH>tc.GAME_WIDTH)x=tc.GAME_WIDTH-Tank.WIDTH;
if(y+Tank.HEIGHT>tc.GAME_HEIGHT)y=tc.GAME_HEIGHT-Tank.HEIGHT;
if(!
good){
Direction[]dirs=Direction.values();
if(step==0){
step=r.nextInt(12)+3;
intrn=r.nextInt(dirs.length);
dir=dirs[rn];
}
if(r.nextInt(40)>38)fire();
step--;
}
}
这里就涉及了随机数的概念了,用随机数来控制敌军坦克在屏幕上移动的频率,让敌军坦克显得更加的智能一些,让我军坦克无法预料敌军运动的轨迹,从而增添可玩性。
对按键的处理也是很讲究的,应该是按下某个键或抬起某个键的时候激发事件,而不应该是按下某个键就一直激发事件,让坦克不停的移动或者开火。
实现激发按键事件的代码如下:
publicvoidkeyPressed(KeyEvente){
intkey=e.getKeyCode();
switch(key){
caseKeyEvent.VK_CONTROL:
fire();
break;
caseKeyEvent.VK_LEFT:
BL=true;
break;
caseKeyEvent.VK_RIGHT:
BR=true;
break;
caseKeyEvent.VK_UP:
BU=true;
break;
caseKeyEvent.VK_DOWN:
BD=true;
break;
}
locateDirection();
}
publicvoidkeyReleased(KeyEvente){
intkey=e.getKeyCode();
switch(key){
caseKeyEvent.VK_LEFT:
BL=false;
break;
caseKeyEvent.VK_RIGHT:
BR=false;
break;
caseKeyEvent.VK_UP:
BU=false;
break;
caseKeyEvent.VK_DOWN:
BD=false;
break;
}
locateDirection();
}
publicvoidlocateDirection(){
if(BL&&!
BU&&!
BR&&!
BD)dir=Direction.L;
elseif(BL&&!
BU&&!
BR&&BD)dir=Direction.LD;
elseif(BL&&BU&&!
BR&&!
BD)dir=Direction.LU;
elseif(!
BL&&!
BU&&BR&&!
BD)dir=Direction.R;
elseif(!
BL&&BU&&BR&&!
BD)dir=Direction.RU;
elseif(!
BL&&!
BU&&BR&&BD)dir=Direction.RD;
elseif(!
BL&&BU&&!
BR&&!
BD)dir=Direction.U;
elseif(!
BL&&!
BU&&!
BR&&BD)dir=Direction.D;
elseif(!
BL&&!
BU&&!
BR&&!
BD)dir=Direction.STOP;
}
以上代码实现了按下键的时候激发事件,抬起键的时候不激发事件,这样就可以控制坦克的移动方向和发射炮弹要不停的按键才能转换了。
坦克发射炮弹应该在坦克的中心位置开始发射的,不应该是在坦克的左边或是右边还是在后边,这些地方都不合适,实现坦克的发射的代码如下:
publicMissilefire(){
if(!
live)
returnnull;
intx=this.x+Tank.WIDTH/2-Missile.WIDTH/2;
inty=this.y+Tank.HEIGHT/2-Missile.HEIGHT/2;
Missilem=newMissile(x,y,good,ptDir,this.tc);
tc.missiles.add(m);
returnm;
}
当然为了增添游戏的好玩性也可以让我军坦克实现向八个方向同时开火,这个方法的代码如下:
publicMissilefire(Directiondir){
if(!
live)
returnnull;
intx=this.x+Tank.WIDTH/2-Missile.WIDTH/2;
inty=this.y+Tank.HEIGHT/2-Missile.HEIGHT/2;
Missilem=newMissile(x,y,good,dir,this.tc);
tc.missiles.add(m);
returnm;
}
这个方法实现的很简单,只是重载了以上的单方向开火方法,这个方法传递了一个方向,通过这个方向实现向八个方向同时开火。
代码如下:
privatevoidsuperFire(){
Direction[]dirs=Direction.values();
for(inti=0;i<8;i++){
fire(dirs[i]);
}
}
坦克不可以从墙上穿过去,也不可以从别的坦克身上轧过去,所以就涉及到了碰撞检测的问题,那怎样可以进行碰撞检测呢?
可以用一个矩形框将两个要相撞的物体包住,如果两个矩形相撞则判定为相撞了。
实现方法如下:
publicRectanglegetRect(){
returnnewRectangle(x,y,WIDTH,HEIGHT);
}
两个物体都被矩形包住之后,如何相撞呢?
下面是坦克与墙相撞的代码:
publicbooleancollidesWithWall(Wallw){
if(this.live&&this.getRect().intersects(w.getRect())){
this.stay();
returntrue;
}
returnfalse;
}
Stay方法是让坦克一旦与墙壁发生碰撞的时候,让坦克返回碰撞前的地方,重新移动一次,方法如下:
privatevoidstay(){
x=oldX;
y=oldY;
}
当坦克与坦克之间发生碰撞时,分为我军坦克与敌军坦克相撞和敌军坦克之间的相撞。
代码如下:
publicbooleancollidesWithTank(Tankt){
if(this!
=t){
if(this.live&&t.isLive()
&&this.getRect().intersects(t.getRect())){
this.stay();
t.stay();
returntrue;
}
}
returnfalse;
}
publicbooleancollidesWithTanks(Listtanks){
for(inti=0;iif(collidesWithTank(tanks.get(i))){
returntrue;
}
}
returnfalse;
}
坦克类换上了图片的代码是要通过工具包来加载图片,将图片加载到内存上。
代码如下:
privatestaticToolkittk=Toolkit.getDefaultToolkit();
privatestaticImage[]tankImages=null;
privatestaticMapimgs=newHashMap();
static{
tankImages=newImage[]{
tk.getImage(Tank.class.getClassLoader().getResource("images/tankL.gif")),
tk.getImage(Tank.class.getClassLoader().getResource("images/tankLU.gif")),
tk.getImage(Tank.class.getClassLoader().getResource("images/tankLD.gif")),
tk.getImage(Tank.class.getClassLoader().getResource("images/tankR.gif")),
tk.getImage(Tank.class.getClassLoader().getResource("images/tankRU.gif")),
tk.getImage(Tank.class.getClassLoader().getResource("images/tankRD.gif")),
tk.getImage(Tank.class.getClassLoader().getResource("images/tankU.gif")),
tk.getImage(Tank.class.getClassLoader().getResource("images/tankD.gif"))
};
imgs.put("L",tankImages[0]);
imgs.put("LU",tankImages[1]);
imgs.put("LD",tankImages[2]);
imgs.put("R",tankImages[3]);
imgs.put("RU",tankImages[4]);
imgs.put("RD",tankImages[5]);
imgs.put("U",tankImages[6]);
imgs.put("D",tankImages[7]);
}
之后draw方法也需要重写,代码如下:
publicvoiddraw(Graphicsg)
{
if(!
live){
if(!
good){
tc.tanks.remove(this);
}
return;
}
if(good)bb.draw(g);
switch(ptDir){
caseL:
g.drawImage(imgs.get("L"),x,y,null);
break;
caseLU:
g.drawImage(imgs.get("LU"),x,y,null);
break;
caseLD:
g.drawImage(imgs.get("LD"),x,y,null);
break;
caseR:
g.drawImage(imgs.get("R"),x,y,null);
break;
caseRU:
g.drawImage(imgs.get("RU"),x,y,null);
break;
caseRD:
g.drawImage(imgs.get("RD"),x,y,null);
break;
caseU:
g.drawImage(imgs.get("U"),x,y,null);
break;
caseD:
g.drawImage(imgs.get("D"),x,y,null);
break;
}
move();
}
3.1.3子弹类模块
该类实现在屏幕上画出子弹和子弹的移动轨迹以及实现了坦克攻击墙壁与攻击敌方坦克的功能。
在屏幕上画出坦克的代码如下:
publicvoiddraw(Graphicsg){
if(!
live){
tc.missiles.remove(this);
return;
}
Colorc=g.getColor();
g.setColor(Color.BLACK);
g.fillOval(x,y,WIDTH,HEIGHT);
g.setColor(c);
move();
}
里面有一个move方法,与前面的坦克类相似,都是画出物体移动的轨迹,这里是画出子弹在屏幕上移动的轨迹。
代码如下:
privatevoidmove(){
switch(dir){
caseL:
x-=XSPEED;
break