javaTankWar设计Word文件下载.docx
- 文档编号:8390114
- 上传时间:2023-05-11
- 格式:DOCX
- 页数:35
- 大小:258.34KB
javaTankWar设计Word文件下载.docx
《javaTankWar设计Word文件下载.docx》由会员分享,可在线阅读,更多相关《javaTankWar设计Word文件下载.docx(35页珍藏版)》请在冰点文库上搜索。
敌方坦克,能将敌方坦克击毙
0.5
敌方坦克的人工智能
0.6
添加地图管理与显示、地图编辑器
0.7
实现坦克不能相互重叠
0.8
实现坦克子弹与地图的融合
0.9
实现爆炸效果
0.9.1
实现主坦克血条
0.9.2
实现浮动的物块(血块,后期会有装备)
0.9.3
坦克换装,地图管理器使用,以及关卡设计
1.0
单机版Ver1.0a测试
联机版
1.1
创建一个TankServer,负责建立坦克客服端之间的连接和转发消息
1.2
服务器建立UDP转发线程
1.3
服务器转发消息的设计
1.4
客服端看见其他坦克的行为
1.5
非友方坦克可以互相攻击
在线版
2.1
在线服务器的设计,在线游戏模式竞技和闯关设计与实现
2.2
5详细实现
0.1版本
任务:
新建java项目,创建一个800*600的JFrame。
创建一个TankClient继承自JFrame,首先设置窗口大小和禁用最大化框。
Frame双缓冲实现:
定义个缓冲画布:
ImageoffScreenImage=null;
因为repaint方法首先调用的是update方法,所以重写update方法实现双缓冲。
publicvoidupdate(Graphicsg){
//双缓冲技术
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);
}
很多时候我们是使用一个专门的线程来更新界面的!
PaintThreadpt=newPaintThread();
newThread(pt).start();
classPaintThreadimplementsRunnable{
publicvoidrun(){
intfps=1000/FPS;
while(true){
try{
Thread.sleep(fps);
repaint();
}catch(InterruptedExceptione){
System.out.println("
异常终止"
);
}
}
}
JFrame双缓冲,因为JFrame中,repaint只调用paint方法,所以可以在JFrame上加个JPanel在其上执行双缓冲,而swing的组件默认是实现双缓冲的,只要开启即可!
当然我们也可以简单点的(原创,不知道可有问题!
):
在JFrame构造的时候保存他的GraphicsgJF;
对象,直接重写repaint方法:
publicvoidrepaint(){
gJF.drawImage(offScreenImage,0,0,null);
0.2版本
创建一个可以8个方向移动的坦克,坦克类。
在主窗口中添加键盘事件:
this.addKeyListener(newKeyMonitor());
classKeyMonitorextendsKeyAdapter{
publicvoidkeyPressed(KeyEvente){
myTank.keyPressed(e);
}
publicvoidkeyReleased(KeyEvente){
myTank.keyReleased(e);
这样就可以把键盘事件交给我们的tank自己处理了!
Tank中,如何可以使坦克8个方向移动?
可以使用四个boolean变量来记录键盘四个方向的按键情况,按下为true,释放为false,比如DOWN和RIGHT按下时,坦克的方向就为RD,从而实现了坦克8个方向的移动!
实现代码如下:
privatebooleanbL=false,bU=false,bR=false,bD=false;
publicvoidkeyPressed(KeyEvente){
intkey=e.getKeyCode();
switch(key){
caseKeyEvent.VK_CONTROL:
//fire();
break;
caseKeyEvent.VK_RIGHT:
bR=true;
caseKeyEvent.VK_LEFT:
bL=true;
caseKeyEvent.VK_UP:
bU=true;
caseKeyEvent.VK_DOWN:
bD=true;
locaeDirection();
}
publicvoidkeyReleased(KeyEvente){
bR=false;
bL=false;
bU=false;
bD=false;
voidlocaeDirection(){
if(bL&
&
!
bU&
bR&
bD)dir=Direction.L;
elseif(bL&
bU&
bD)dir=Direction.LU;
elseif(!
bL&
bD)dir=Direction.U;
bR&
bD)dir=Direction.RU;
bD)dir=Direction.R;
bD)dir=Direction.RD;
bD)dir=Direction.D;
bD)dir=Direction.LD;
bD)dir=Direction.STOP;
0.3创建子弹类,坦克可以开火,建立坦克炮筒
子弹类:
首先子弹可以每个坦克持有一个Bullet的集合,在每帧画坦克的时候在画出坦克打出的所有子弹,这是一种比较常用的方法,但我们统一交给TankClient管理,在TankClient中有个bullets的List,每帧就重新画bullets中的所有Bullet,而我们的Bullet类负责画自己和管理自己,而我们的Tank只需要打出个子弹即可!
这样Tank和Bullet就用面向对象的思想给分离了。
主要代码:
publicclassBullet{
privateintx,y;
privateDirectiondir;
privateintxBulletSpeed=5,yBulletSpeed=5;
publicstaticfinalintBULLETHEIGHT=8,BULLETWIDTH=8;
privatebooleanlive=false;
publicbooleanisLive(){
returnlive;
publicvoidsetLive(booleanlive){
this.live=live;
TankClienttc=null;
publicBullet(intx,inty,Directiondir){
this.x=x;
this.y=y;
this.dir=dir;
live=true;
publicBullet(intx2,inty2,Directiondir2,TankClienttc){
this(x2,y2,dir2);
this.tc=tc;
publicvoiddraw(Graphicsg){
Colorc=g.getColor();
g.setColor(Color.BLACK);
g.fillOval(x,y,BULLETWIDTH,BULLETHEIGHT);
g.setColor(c);
move();
publicvoidmove(){
switch(dir){
caseL:
x-=xBulletSpeed;
caseLU:
y-=yBulletSpeed;
caseU:
caseRU:
x+=xBulletSpeed;
caseR:
caseRD:
y+=yBulletSpeed;
caseD:
caseLD:
caseSTOP:
if(x<
0||x>
TankClient.GAME_WIDTH||y<
0||y>
TankClient.GAME_HEIGHT){
live=false;
if(!
live){
tc.bullets.remove(this);
因为Bullet自己需要管理自己,当自己超出边界时,自己灭了自己,所以需要TankClient中的bullets对象,我们持有TankClient的引用,从而得到bullets对象,而这个需要在构造子弹的时候传递给子弹,而子弹是tank生成的,所以坦克自己要持有TankClient的引用!
添加tank一个构造方法:
publicTank(inti,intj,TankClienttankClient){
this(i,j);
this.tc=tankClient;
并且当tank中的监听键盘按下某个键时发出个子弹,我们以按下Ctrl,代码如下:
caseKeyEvent.VK_CONTROL:
fire();
break;
privatevoidfire(){
intx=this.x+TANKWIDTH/2-Bullet.BULLETWIDTH/2;
inty=this.y+TANKHEIGHT/2-Bullet.BULLETHEIGHT/2;
Bulletb=newBullet(x,y,dir/*dirPt*/,tc);
tc.bullets.add(b);
这样当坦克移动时可以打出子弹的,但当坦克静止时,构造出的子弹也是不动,所以我们必须给坦克指定个炮筒!
当坦克静止是也可以打出往某个方向的子弹:
DirectiondirPt=Direction.R;
而炮筒的方向怎么确定?
我们可以在locaeDirection,keyReleased,keyPressed中确定,但这样理论上可以但很难打出45度的子弹,按下和释放的间隔必须非常的短!
所以我们可以把确定方向放到tank的move中,可以得到比较满意的效果!
if(dir!
=Direction.STOP){
dirPt=dir;
还有画出炮筒,一个苦力活!
贴上一个方向的:
privatevoiddrawPt(Graphicsg){
switch(dirPt){
g.drawLine(x+Tank.TANKWIDTH/2,y+Tank.TANKHEIGHT/2,x,y+Tank.TANKHEIGHT/2);
//。
。
0.4敌方坦克,能将敌方坦克击毙
阵营:
Camp;
添加Camp枚举类型:
publicenumCamp{
FRIEND,ENEMY,UNKNOW,GOD,SELF;
publicstaticbooleanisEnemy(Campc1,Campc2){
if((c1==FRIEND&
c2==ENEMY)||(c1==ENEMY&
c2==FRIEND))
{
returntrue;
}
if((c1==SELF&
c2==SELF))
returntrue;
returnfalse;
给每个tank一个Camp,每个子弹一个camp,子弹的camp有所发出的tank的camp决定。
检测子弹是否击中坦克:
1)可以检测每个子弹是否击中敌方坦克;
2)检测坦克是否被子弹撞上;
这两种方法都是可以的,我们以检测子弹实现:
在TankClient画子弹之前检测:
b.hitTank(myTank);
//检测敌方是否击中主坦克
b.hitTanks(tanks);
//检测我放是否集中敌方坦克
publicbooleanhitTank(TankmyTank){
if(getRect().intersects(myTank.getRect())&
Camp.isEnemy(camp,myTank.getCamp())){
myTank.setLive(false);
this.setLive(false);
returnfalse;
publicbooleanhitTanks(ArrayList<
Tank>
tanks){
for(inti=0;
i<
tanks.size();
i++){
if(hitTank(tanks.get(i))){
在画坦克的时候如果发现坦克已经死亡live=false;
则从tanks中remove此坦克!
0.5敌方坦克的人工智能
坦克的移动主要又方向控制,所以我们可以从8个方向中随机产生一个方向移动,但这样很容易使坦克在原地打滚!
所以给出一个随机step变量,表示走多少步才转向,代码:
privatestaticRandomr=newRandom();
privateintstep=r.nextInt(12)+3;
在move中:
if(camp==Camp.ENEMY){
if(step==0){
step=r.nextInt(30)+3;
Direction[]dirs=Direction.values();
intrn=r.nextInt(dirs.length);
dir=dirs[rn];
step--;
if(r.nextInt(40)>
38)fire();
慢慢发现tank越来越大,越来越复杂,逻辑上有点混乱,所以我们把敌人坦克分离出去!
Tank分解为TankMain和TankEnemy,而上代码添加到TankEnemy中,在TankEnemy中重写move方法,添加我们简单的人工智能!
而在TankClient中只需要在生成Tank的时候修改代码,其他的可以使用多态精简代码!
0.6添加地图管理与显示地图编辑器
1、地图是一个20*15的矩阵,可用一个int[20][15]存储,其中的每个值代表一个图片。
思路很清楚的,本来准备用简单的矩形等代替的,但还是用实际的图片来代替吧!
Graphics中有画Image的方法,首先我们要加载图片,我们首先把要显示的图片加载到内存中:
privatestaticToolkittk=Toolkit.getDefaultToolkit();
privatestaticImage[]tankImages=null;
publicstaticMap<
String,Image>
imgs=newHashMap<
String,Image>
();
static{
tankImages=newImage[]{tk.getImage(MapMgr.class.getClassLoader().
getResource("
res/diban.gif"
)),
};
imgs.put("
diban"
tankImages[0]);
响应鼠标拖动事件时要注意:
MouseAdaptermAdapter=newMouseAdapter(){
publicvoidmouseDragged(MouseEvente){
System.out.println("
mouseDragged"
publicvoidmouseMoved(MouseEvente){
mouseMovedFRAME"
addMouseListener(mAdapter);
addMouseMotionListener(mAdapter);
2、Java中显示GIF,可以使用swing中的控件,并且可以重写控件的paint的方法重而改变控件的外观,并且支持GIF动态的显示,并可以在GIF上画画,实在是强大和方便啊!
在一个JLabel上显示GIF:
JLabeljl=newJLabel("
1111111111111"
jl.setIcon(newImageIcon("
D:
/1.GIF"
));
jl.setBounds(10,570,100,100);
this.add(jl);
在JLable上显示的GIF上画画:
classSyxGifLabelextendsJLabel{
publicvoidpaint(Graphicsg){
super.paint(g);
Graphics2Dg2D=(Graphics2D)g;
g2D.setStroke(newBasicStroke(5.0f));
g2D.drawRoundRect(5,5,this.getWidth()-10,this.getHeight()-10,10,10);
SyxGifLabelsjl=newSyxGifLabel();
sjl.setIcon(newImageIcon("
sjl.setBounds(130,570,100,100);
this.add(sjl);
在JButton上显示GIF图片:
JButtonjbtn=newJButton("
1111"
jbtn.setIcon(newImageIcon("
jbtn.setBounds(230,570,jbtn.getIcon().getIconWid
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- javaTankWar 设计