失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 《Java小游戏实现》:坦克大战(续2)

《Java小游戏实现》:坦克大战(续2)

时间:2020-02-18 01:08:06

相关推荐

《Java小游戏实现》:坦克大战(续2)

《Java小游戏实现》:坦克大战(续2)

相关博文:

《Java小游戏实现》:坦克大战/u010412719/article/details/51712663

《Java小游戏实现》:坦克大战(续一):/u010412719/article/details/51723570

博文《Java小游戏实现》:坦克大战(续1)中已经实现到了坦克可以发射一颗子弹了。这篇博文在此基础上继续实现更多的功能。

完成功能:坦克发射多颗子弹

有了坦克发射一颗子弹的基础,发射多颗子弹就相当简单的,只需要在TankClien类中加入一个容器来存放多枚子弹即可。由于容器的容量也是有限的,我们不能一直往里面装。因此,在子弹类中,我们为子弹对象引入了一个live属性,用来判断这个子弹是否还存活。判断子弹是否还存活是根据子弹的位置是否出界。

下面只贴出完成这个功能相关代码

TankClient.java

private List<Missile> missiles = new ArrayList<Missile> ();public List<Missile> getMissiles() {return missiles;}@Overridepublic void paint(Graphics g) {//直接调用坦克类的draw方法tk.draw(g); //画子弹for(int i=0;i<missiles.size();i++){Missile ms = missiles.get(i);//判断子弹是否还存活在,如果不是存活的,则移除if(!ms.isLive()){missiles.remove(ms);}else{ms.draw(g);}}}

Missile.java类

在move方法中根据子弹所在的位置判断子弹是否还存活。

public class Missile {private void move() {if(dir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOPx -= XSPEED;}else if(dir==Direction.LU){x -= XSPEED;y -= YSPEED;}else if(dir==Direction.U){y -= YSPEED;}else if(dir==Direction.RU){x += XSPEED;y -= YSPEED;}else if(dir==Direction.R){x += XSPEED;}else if(dir==Direction.RD){x += XSPEED;y += YSPEED;}else if(dir==Direction.D){y += YSPEED;}else if(dir==Direction.LD){x -= XSPEED;y += YSPEED;}//根据子弹所在的位置x,y来判断子弹是否还存活在if(x<0||x>TankClient.GAME_WIDTH||y<0||y>TankClient.GAME_HEIGHT){live = false;}}public boolean isLive() { return live;}}

Tank.java文件内容如下:

在坦克类中,在keyPressed方法中,每按一次Ctrl键,就发射一颗子弹。

public class Tank {//记录键盘的按键情况public void keyPressed(KeyEvent e){int key=e.getKeyCode();//System.out.println(key);switch(key){case 17:tc.getMissiles().add(fire());break;case KeyEvent.VK_LEFT:b_L=true;break;case KeyEvent.VK_UP:b_U=true;break;case KeyEvent.VK_RIGHT:b_R=true;break;case KeyEvent.VK_DOWN:b_D=true;break;}//根据上面的按键情况,确定坦克即将要运行的方向moveDirection();}}

完整代码下载链接:/detail/u010412719/9555292

完成功能:解决坦克越界问题

坦克越界问题比较简单,只需要判断坦克所在的位置是否越界,如果越界,则将其位置设置为边界位置即可。

实现代码如下:

/** 函数功能:处理坦克越界问题* */private void dealTankBorder() {if(x<0){x = 0;} else if(x > TankClient.GAME_WIDTH-this.WIDTH){x = TankClient.GAME_WIDTH-this.WIDTH ;} if(y<0){y = 0;}else if(y>TankClient.GAME_WIDTH - this.HEIGHT){y = TankClient.GAME_WIDTH - this.HEIGHT;}}

上面函数在move()方法最后进行调用即可。

完成功能:加入敌人的坦克

前面实现的所用功能是自己一个坦克在界面上面运动呀、发子弹呀等等。

下面我们完成在界面上加入一个敌人坦克。

实现敌人坦克有两种方式

1、第一种是再新建一个EnemyTank类,

2、第二种是在原来的Tank类中加入一个属性来表示此坦克的类型。

由于我们有一个坦克类了,为方便起见,这里采用第二种方式:在原来的Tank类中加入一个属性来表示此坦克的类型。

Tank.java中新增加的内容主要有

//添加一个属性,表示此坦克是好还是坏private boolean good;//更改下构造方法public Tank(int x, int y,boolean good) {this.x = x;this.y = y;this.good = good;}public Tank(int x, int y,boolean good, TankClient tc) {this(x,y,good);this.tc = tc;}//根据坦克的类型给出不同的颜色public void draw(Graphics g){Color c = g.getColor();if(good){g.setColor(Color.RED);}else{g.setColor(Color.BLUE);}g.fillOval(x, y, WIDTH, HEIGHT);g.setColor(c);//画一个炮筒drawGunBarrel(g);move();//根据键盘按键的结果改变坦克所在的位置}

Missile类没有任何变动。

总管家TankClient.java中需要添加的内容有:

1、new 出两个坦克对象

private Tank tk=new Tank(50,50,true,this);private Tank enemy = new Tank(100,100,false,this);

2、在paint方法中画出两个坦克

@Overridepublic void paint(Graphics g) {//直接调用坦克类的draw方法tk.draw(g); enemy.draw(g);//画子弹for(int i=0;i<missiles.size();i++){Missile ms = missiles.get(i);//判断子弹是否还存活在,如果不是存活的,则移除if(!ms.isLive()){missiles.remove(ms);}else{ms.draw(g);}}}

以上就实现了添加敌对坦克这一功能,但是还没有对其添加随机运动。

完成的功能:子弹打死敌人坦克

经过上面的实现,我们有了一个不会动且不会发子弹的傻傻的敌对坦克,但是我们也不能打死它,下面我们就来实现打死坦克。哈哈,是不是稍微变得有趣一点了。

分析:

1、由于是子弹打死敌人坦克,根据面向对象的思想,在子弹类中,加入一个hitTank方法。

public boolean hitTank(Tank t){}

2、那么hitTank这个方法应该如何来判断是否子弹打中了该坦克呢??这就涉及到一个碰撞的问题。由于我们的子弹和坦克都是矩形,因此碰撞问题就得到了很好的简化,只是判断两个矩形是否有重叠的部分。如果有,则就判断发生了碰撞,子弹就打中了坦克。

因此,在Missile类和Tank类中,,均添加一个getRect()方法,返回子弹和坦克所在的矩形区域对象。

public Rectangle getRect(){return new Rectangle(x, y, WIDTH, HEIGHT);}

3、当子弹打中了坦克,则子弹和坦克就应该都消失。因此,在坦克中需要引入一个布尔变量来判断坦克是否存活

public boolean hitTank(Tank t){//首先判断此坦克是否是存活的,如果是死的,就不打了if(!t.isLive()){return false;}if(this.getRect().intersects(t.getRect())){//判断是否有碰撞//碰撞之后,子弹和该坦克就应该都死了this.live = false;//子弹死了t.setLive(false);//坦克死了return true;}else{return false;}}

无论是子弹消失,在前面的版本中有处理,但是本版本上的坦克消失,目前的处理方法就是不绘画出来,即在draw方法中,首先判断一下,看此对象是否存活,如果存活,则绘画出来。

代码如下:

public void draw(Graphics g){if(!live){//判断坦克是否存活,如果死了,则不绘画出来,直接返回return ;}Color c = g.getColor();if(good){g.setColor(Color.RED);}else{g.setColor(Color.BLUE);}g.fillOval(x, y, WIDTH, HEIGHT);g.setColor(c);//画一个炮筒drawGunBarrel(g);move();//根据键盘按键的结果改变坦克所在的位置}

最后,贴下Tank类、Missile类、TankClient类的完整代码:

Tank类

public class Tank {//坦克所在的位置坐标private int x;private int y;//坦克的高度和宽度private static final int WIDTH = 30;private static final int HEIGHT = 30;//定义两个常量,表示运动的速度private static final int XSPEED = 5;private static final int YSPEED = 5;//定义四个布尔类型变量来记录按键的情况,默认状态下为false,表示没有键按下private boolean b_L,b_U,b_R,b_D;//添加一个属性,表示此坦克是好还是坏private boolean good;public boolean isGood() {return good;}//用来标识此坦克对象是否存活private boolean live =true;public boolean isLive() {return live;}public void setLive(boolean live) {this.live = live;}//定义一个枚举类型来表示运行的方向 public enum Direction{L,LU,U,RU,R,RD,D,LD,STOP}//定义一个变量来表示坦克要运行的方向,初始状态为STOPprivate Direction dir = Direction.STOP;//炮筒方向private Direction ptDir = Direction.D;private TankClient tc;public Tank(int x, int y,boolean good) {this.x = x;this.y = y;this.good = good;}public Tank(int x, int y,boolean good, TankClient tc) {this(x,y,good);this.tc = tc;}public void draw(Graphics g){if(!live){//判断坦克是否存活,如果死了,则不绘画出来,直接返回return ;}Color c = g.getColor();if(good){g.setColor(Color.RED);}else{g.setColor(Color.BLUE);}g.fillOval(x, y, WIDTH, HEIGHT);g.setColor(c);//画一个炮筒drawGunBarrel(g);move();//根据键盘按键的结果改变坦克所在的位置}private void drawGunBarrel(Graphics g) {int centerX = this.x + this.WIDTH/2;int centerY = this.y + this.HEIGHT/2;if(ptDir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOPg.drawLine(centerX, centerY, x, y + HEIGHT/2);}else if(ptDir==Direction.LU){g.drawLine(centerX, centerY, x, y );}else if(ptDir==Direction.U){g.drawLine(centerX, centerY, x+ WIDTH/2, y );}else if(ptDir==Direction.RU){g.drawLine(centerX, centerY, x + WIDTH, y );}else if(ptDir==Direction.R){g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT/2);}else if(ptDir==Direction.RD){g.drawLine(centerX, centerY, x+ WIDTH, y + HEIGHT);}else if(ptDir==Direction.D){g.drawLine(centerX, centerY, x+ WIDTH/2, y + HEIGHT);}else if(ptDir==Direction.LD){g.drawLine(centerX, centerY, x, y + HEIGHT);}}//记录键盘的按键情况public void keyPressed(KeyEvent e){int key=e.getKeyCode();//System.out.println(key);switch(key){//case 17://避免因Ctrl一直按下,一直发射子弹,因此将这一功能放入keyReleased方法中了// tc.getMissiles().add(fire());// break;case KeyEvent.VK_LEFT:b_L=true;break;case KeyEvent.VK_UP:b_U=true;break;case KeyEvent.VK_RIGHT:b_R=true;break;case KeyEvent.VK_DOWN:b_D=true;break;}//根据上面的按键情况,确定坦克即将要运行的方向moveDirection();}//键盘按键松下时,也要进行记录public void keyReleased(KeyEvent e) {int key=e.getKeyCode();switch(key){case 17:tc.getMissiles().add(fire());break;case KeyEvent.VK_LEFT:b_L=false;break;case KeyEvent.VK_UP:b_U=false;break;case KeyEvent.VK_RIGHT:b_R=false;break;case KeyEvent.VK_DOWN:b_D=false;break;}}//根据键盘的按键情况来确定坦克的运行方向private void moveDirection() {//L,LU,U,RU,R,RD,D,LD,STOPif(b_L&&!b_U&&!b_R&&!b_D){dir = Direction.L;}else if(b_L&&b_U&&!b_R&&!b_D){dir = Direction.LU;}else if(!b_L&&b_U&&!b_R&&!b_D){dir = Direction.U;}else if(!b_L&&b_U&&b_R&&!b_D){dir = Direction.RU;}else if(!b_L&&!b_U&&b_R&&!b_D){dir = Direction.R;}else if(!b_L&&!b_U&&b_R&&b_D){dir = Direction.RD;}else if(!b_L&&!b_U&&!b_R&&b_D){dir = Direction.D;}else if(b_L&&!b_U&&!b_R&&b_D){dir = Direction.LD;}else{//其它所有情况,都是不动dir = Direction.STOP;}//将坦克方向赋值给炮筒方向if(dir!=Direction.STOP){ptDir = dir;}}//上面有运行方向,但是还缺少具体的运行细节,例如:假设是按下了右键,则应该横坐标x+=XSPEED;private void move(){if(dir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOPx -= XSPEED;}else if(dir==Direction.LU){x -= XSPEED;y -= YSPEED;}else if(dir==Direction.U){y -= YSPEED;}else if(dir==Direction.RU){x += XSPEED;y -= YSPEED;}else if(dir==Direction.R){x += XSPEED;}else if(dir==Direction.RD){x += XSPEED;y += YSPEED;}else if(dir==Direction.D){y += YSPEED;}else if(dir==Direction.LD){x -= XSPEED;y += YSPEED;}else if(dir==Direction.STOP){//... nothing}//处理坦克越界问题dealTankBorder(); }/** 函数功能:处理坦克越界问题* */private void dealTankBorder() {if(x<0){x = 0;} else if(x > TankClient.GAME_WIDTH-this.WIDTH){x = TankClient.GAME_WIDTH-this.WIDTH ;} if(y<0){y = 0;}else if(y>TankClient.GAME_WIDTH - this.HEIGHT){y = TankClient.GAME_WIDTH - this.HEIGHT;}}public Missile fire(){//计算子弹的位置,并利用炮筒的方向来new一个子弹对象int x = this.x +(this.WIDTH)/2 - (Missile.WIDTH)/2;int y = this.y + (this.HEIGHT)/2 -(Missile.HEIGHT)/2;Missile ms = new Missile(x,y,this.ptDir);return ms;}/** 函数功能:得到坦克所在位置的矩形框* */public Rectangle getRect(){return new Rectangle(x, y, WIDTH, HEIGHT);}}

Missile类

代码如下

public class Missile {//定义两个常量,表示运动的速度private static final int XSPEED = 10;private static final int YSPEED = 10;//子弹所在的位置private int x;private int y;//坦克的高度和宽度public static final int WIDTH = 10;public static final int HEIGHT = 10;//子弹的运行方向private Direction dir;private boolean live = true;public Missile(int x, int y, Direction dir) {this.x = x;this.y = y;this.dir = dir;}public void draw(Graphics g){//如果该子弹不是存活的,则不进行绘图if(!live){return ;}Color c = g.getColor();g.setColor(Color.YELLOW);g.fillOval(x, y, WIDTH, HEIGHT);g.setColor(c);move();}private void move() {if(dir==Direction.L){//L,LU,U,RU,R,RD,D,LD,STOPx -= XSPEED;}else if(dir==Direction.LU){x -= XSPEED;y -= YSPEED;}else if(dir==Direction.U){y -= YSPEED;}else if(dir==Direction.RU){x += XSPEED;y -= YSPEED;}else if(dir==Direction.R){x += XSPEED;}else if(dir==Direction.RD){x += XSPEED;y += YSPEED;}else if(dir==Direction.D){y += YSPEED;}else if(dir==Direction.LD){x -= XSPEED;y += YSPEED;}//根据子弹所在的位置x,y来判断子弹是否还存活在if(x<0||x>TankClient.GAME_WIDTH||y<0||y>TankClient.GAME_HEIGHT){live = false;}}public boolean isLive() { return live;}public Rectangle getRect(){return new Rectangle(x, y, WIDTH, HEIGHT);}public boolean hitTank(Tank t){//首先判断此坦克是否是存活的,如果是死的,就不打了if(!t.isLive()){return false;}if(this.getRect().intersects(t.getRect())){//判断是否有碰撞//碰撞之后,子弹和该坦克就应该都死了this.live = false;//子弹死了t.setLive(false);//坦克死了return true;}else{return false;}}

TankClient类代码如下:

/** 此版本添加了子弹打死敌对坦克* */public class TankClient extends Frame{public final static int GAME_WIDTH=600;public final static int GAME_HEIGHT=600;private Tank tk=new Tank(50,50,true,this);private Tank enemy = new Tank(100,100,false,this);private List<Missile> missiles = new ArrayList<Missile> ();public List<Missile> getMissiles() {return missiles;}private Image offScreenImage = null;public static void main(String[] args) {new TankClient().launchFrame();}@Overridepublic void update(Graphics g) {if (offScreenImage == null) {offScreenImage = this.createImage(GAME_WIDTH, GAME_HEIGHT);}Graphics goffScreen = offScreenImage.getGraphics();// 重新定义一个画虚拟桌布的画笔//Color c = goffScreen.getColor();goffScreen.setColor(Color.darkGray);goffScreen.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);goffScreen.setColor(c);paint(goffScreen);g.drawImage(offScreenImage, 0, 0, null);}@Overridepublic void paint(Graphics g) {//直接调用坦克类的draw方法tk.draw(g); enemy.draw(g);//画子弹for(int i=0;i<missiles.size();i++){Missile ms = missiles.get(i); //判断子弹是否还存活在,如果不是存活的,则移除if(!ms.isLive()){missiles.remove(ms);}else{ms.hitTank(enemy);ms.draw(g);}}}public void launchFrame(){this.setTitle("坦克大战");this.setLocation(300, 400);this.setSize(GAME_WIDTH, GAME_HEIGHT);this.setBackground(Color.GRAY);//为关闭窗口添加响应this.addWindowListener(new WindowAdapter(){@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}});//设置是否允许用户改变窗口的大小,这里限制下,不允许this.setResizable(false);this.setVisible(true);new Thread(new MyRepaint()).start();this.addKeyListener(new KeyMonitor());}private class MyRepaint implements Runnable{@Overridepublic void run() {while(true){//每50ms重画一次repaint();try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}}}private class KeyMonitor extends KeyAdapter{@Overridepublic void keyPressed(KeyEvent e) {tk.keyPressed(e);}@Overridepublic void keyReleased(KeyEvent e) {tk.keyReleased(e);} }}

以上就完成了坦克发射子弹可以击打敌对坦克的功能。

未完,剩余功能见下篇博文

如果觉得《《Java小游戏实现》:坦克大战(续2)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。