Java学习总结之坦克大战项目(二)

接上篇

V0.4:坦克类初步建好了,接下来考虑子弹,这里我曾纠结子弹到底是坦克的内部类还是兄弟类。根据面向对象的分析,子弹打出后,无论坦克是否消亡,子弹都会存在,所以子弹不是坦克的内部类,所以我们新建一个子弹类Cannonball。

在这个版本中我们只是初步构造子弹类,只编写构造方法、draw方法(绘制一颗子弹,仍然是用圆表示)和move方法(子弹的飞行)。构造方法确定子弹的初始坐标和方向,move方法根据方向来不停的运动。此时我们可以再Frame中进行一下测试,new一个子弹对象,在paint方法中调用其draw方法。

V0.5:子弹类建好了那么就要开始调用了。假设我们是按ctrl键来发射子弹,那么问题来了,我们究竟在哪里创建子弹对象?一开始,我想到的是Tank类中,按键处理的地方,如果按键为ctrl,则产生一个子弹对象,但是问题又来了,在Tank类中创建的对象,我们怎么在Frame中调用它的draw方法?毕竟在是在Tank的方法里面创建的对象,而不是Tank的成员,所以没有办法调用。或许有人说,那声明为Tank的成员呢?在我的理解来说,这不行,因为这样一旦坦克对象死亡,其发出的子弹也就死亡了,可是实际坦克游戏中,即使坦克被干掉,它之前发出的子弹也仍旧存在,所以说,声明为Tank的成员不行。

在Tank中不行,那在Frame中呢?在键盘监听器调用Tank的keyPressed方法之前,先判断按键是否为ctrl?然后get到坦克对象的x,y,还有方向来new一个子弹对象?应该可以实现,但是感觉好像跟坦克的联系没有那么密切了,无法体现出坦克是子弹的生产者这一条件,不过我比较懒,只是这样整理出这个思路,并没有动手写,有兴趣的可以试试,接下来我是看视频来验证我的思路

在视频中,是用到了持有对方引用这个方法,也就是说用的是我第一个思路,解决调用问题的办法是在Tank类中持有一个Frame的引用,这样就可以直接将new出来的子弹对象赋值给Frame中声明的子弹引用。

解决了思路,那么就开始编写代码,首先在Tank的keyPressed方法中,当按ctrl 时,调用fire方法并返回一个子弹类型的对象,为了将该对象赋值给一个Frame中的引用,需要Tank持有Frame的引用,那么在Tank中添加一个Frame类的变量,修改构造方法,增加一个Frame类型的参数,然后修改Frame类中的创建语句,将this指针传递给Tank的构造方法。此时Tank类的Frame引用就指向了游戏的Frame。

然后是在Frame中声明接收子弹对象的容器。考虑到子弹不止一个,所以我声明了一个ArrayList,然后编写了一个add方法用来添加一个子弹对象。此时再返回fire的调用处,将fire的返回值当做add的参数传递出来。酱紫算是终于将创建出来的子弹对象传递到Frame手上了,接下来就好办了,在paint中遍历ArrayList,调用每个子弹对象的draw方法。

到此为止算是把“打出一发子弹”这个事件实现了。

V0.6:在这个版本中处理几个小问题。首先是子弹是从坦克左上角打出的,这是因为坦克的坐标和子弹的坐标都是左上角点的坐标,只要修改创建子弹时的参数就行了,只是个简单的几何问题,就不多说了。

然后是在按住方向键的同时,按ctrl 键,打出子弹时,坦克会停下的问题,这是因为按下ctrl 键就激发了另一个KeyEvent ,所以原本按下方向键的KeyEvent 就被打断了,我的解决办法是不再将移动这个动作写在keyPressed方法中,每当按下一个方向键,只是确定方向和开始移动(定义一个isMoving 的布尔型变量),另外写一个move方法,根据方向和isMoving 来判断如何移动,然后添加keyReleased 方法,当松开方向键时将isMoving设置为false。

最后是添加一个炮筒,没有方向打子弹总是觉得别扭有没有,添加炮筒也很简单,就是在draw方法里面调用一个drawLine 而已,稍微有点麻烦的是炮筒的方向,这里需要根据direction 也就是坦克方向的不同来计算不同的坐标值。

到V0.6版的代码如下:

TankClient.java

import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;

public class TankClient {


 public static void main(String[] args) {
  new TankFrame("TankWar").launchFrame();
 }
}
 
class TankFrame extends Frame {
 public static final int FRAME_H = 800;
 public static final int FRAME_W = 600;
 private ArrayList<Cannonball> cannonballs = new ArrayList<Cannonball>();
 Tank myTank = new Tank(200,200,this);
 
 public TankFrame(String s){
  super(s);
 }
 
 public void launchFrame () {
  setLocation(100,100);
  setSize(FRAME_H,FRAME_W);
  setVisible(true);
  addWindowListener(new WindowAdapter() {
   public void windowClosing(WindowEvent e) {
    System.exit(0);
   }
  });
  this.addKeyListener(new KeyMonitor());
  new Thread(new PaintThread()).start();
 }
 
 public void paint(Graphics g) {
  myTank.draw(g);
  for(Cannonball c : cannonballs){
   c.draw(g);
  }
 }
 
 public void addCannonball(Cannonball c) {
  cannonballs.add(c);
 }
 
 class KeyMonitor extends KeyAdapter {

  public void keyPressed(KeyEvent e) {
   myTank.keyPressed(e);
  }
  
  public void keyReleased(KeyEvent e) {
   myTank.keyReleased(e);
  }
 }
 
 class PaintThread implements Runnable {
  public void run() {
   while(true){
    repaint();
    try {
     Thread.sleep(30);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }
 }
}

Tank.java

import java.awt.*;
import java.awt.event.*;


public class Tank {
 private static final int X_SPEED = 5;
 private static final int Y_SPEED = 5;
 
 private int tank_x = 0;
 private int tank_y = 0;
 private Direction direction = Direction.Up;
 private TankFrame client = null;
 private boolean isMoving = false;
 
 public enum Direction {
  Right,Left,Up,Down
 }
 
 public Tank(int x, int y, TankFrame client){
  this.tank_x = x;
  this.tank_y = y;
  this.client = client;
 }
 
 public void draw(Graphics g) {
  Color c = g.getColor();
  g.setColor(Color.GREEN);
  g.fillOval(tank_x, tank_y, 50, 50);
  
  int x1,x2,y1,y2;
  x1 = x2 = tank_x + 25;
  y1 = y2 = tank_y + 25;
  switch(direction){
  case Up:
   y1 -= 25;
   y2 -= 50;
   break;
  case Down:
   y1 += 25;
   y2 += 50;
   break;
  case Left:
   x1 -= 25;
   x2 -= 50;
   break;
  case Right:
   x1 += 25;
   x2 += 50;
   break;
  }
  g.drawLine(x1, y1, x2, y2);
  g.setColor(c);
  move();
 }
 
 private void move() {
  if(!isMoving) return;
  switch(direction) {
  case Up:
   tank_y -= Y_SPEED;
   break;
  case Down:
   tank_y += Y_SPEED;
   break;
  case Left:
   tank_x -= X_SPEED;
   break;
  case Right:
   tank_x += X_SPEED;
   break;
  }
 }

 public Cannonball fire(){
  Cannonball c = new Cannonball(tank_x + 20,tank_y + 20,direction);
  return c;
 }
 
 public void keyPressed(KeyEvent e){
  int code = e.getKeyCode();
  switch(code) {
  case KeyEvent.VK_UP:
   direction = Direction.Up;
   isMoving = true;
   break;
  case KeyEvent.VK_DOWN:
   direction = Direction.Down;
   isMoving = true;
   break;
  case KeyEvent.VK_LEFT:
   direction = Direction.Left;
   isMoving = true;
   break;
  case KeyEvent.VK_RIGHT:
   direction = Direction.Right;
   isMoving = true;
   break;
  case KeyEvent.VK_CONTROL:
   client.addCannonball(fire());
   break;
  }
 }

 public void keyReleased(KeyEvent e) {
  int code = e.getKeyCode();
  switch(code) {
  case KeyEvent.VK_UP:
  case KeyEvent.VK_DOWN:
  case KeyEvent.VK_LEFT:
  case KeyEvent.VK_RIGHT:
   isMoving = false;
   break;
  }
 }
}

Cannonball.java

import java.awt.*;


public class Cannonball {
 private int x = 0;
 private int y = 0;
 private Tank.Direction direction = null;
 private static final int X_SPEED = 10;
 private static final int Y_SPEED = 10;
 
 public Cannonball(int x, int y, Tank.Direction dir) {
  this.x = x;
  this.y = y;
  this.direction = dir;
 }
 
 public void draw(Graphics g) {
  Color c = g.getColor();
  g.setColor(Color.BLACK);
  g.fillOval(x, y, 10, 10);
  g.setColor(c);
  move();
 }

 private void move() {
  switch(direction) {
  case Up :
   y -= Y_SPEED;
   break;
  case Down :
   y += Y_SPEED;
   break;
  case Left :
   x -= X_SPEED;
   break;
  case Right :
   x += X_SPEED;
   break;
  }
 }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值