这个项目呢,我仍然是边看马士兵老师的Java教程的坦克大战视频,边写的。
当然,很多地方都是经过了我的思考,然后改写的,版本顺序也不一样,因为有时候我会停下视频然后思考我的代码,然后改进了继续看,这部分改进的也许就是后面的视频要讲的。但是我个人觉得,这样提前思考,比一味的观看视频收获要大得多,特别是提前想到很难解决的问题,思考好几种解决办法,都不确定是否可行的时候,再看视频中是怎样解决的,有种豁然开朗的感觉,这时候印象是最深刻的。好了,闲话到此为止,接下来说明我写整个项目的历程。
V0.1:首先我先建一个窗口,也就是一个Frame,然后设置右上角X按钮的事件反应addWindowListener
V0.2:基于面向对象的考虑,新建一个Tank类,将跟坦克对象的有关属性和方法都封装在内部,在这一版本中只是添加构造方法、draw方法和keyPressed方法,draw方法是用来将坦克对象显示在屏幕上,我暂时用一个圆型来表示坦克,keyPressed方法是当按下方向键时进行的事件处理,即按上下左右时分别向上下左右移动。然后在之前的Frame中,编写paint方法(用来绘制界面),在paint方法中调用坦克对象的draw方法来绘制一个坦克(圆);添加键盘监听器addKeyListener,用来监听按键事件,按键事件发生后转交给tank对象的keyPressed方法进行处理。
V0.3:在0.2版本中,我们应该可以通过按键来修改坦克的位置,但测试后发现,坦克位置的变化只有在最小化然后再还原的时候才会显示出来,这是因为只有在窗口重新激活的时候系统才会调用paint方法重新绘制界面,基于游戏的实时性考虑,我们需要不间断的调用paint方法,所以在这个版本中,我们新建一个线程,用来每隔30毫秒刷新一次界面,刷新界面的方法是Java提供的repaint方法,这个方法会自动调用paint方法。
至此为止我们有两个文件,一个窗口文件,我命名为TankClient,一个Tank类文件。此时版本的代码如下:
TankClient.java
import java.awt.*;
import java.awt.event.*;
public class TankClient {
public static void main(String[] args) {
//生成窗口
new TankFrame("TankWar").launchFrame();
}
}
//窗口类
class TankFrame extends Frame {
public static final int FRAME_H = 600; //窗口高度
public static final int FRAME_W = 800; //窗口宽度
Tank myTank = new Tank(200,200); //创建一个坦克对象
//窗口的构造方法
public TankFrame(String s){
super(s);
}
//窗口初始化
public void launchFrame () {
setLocation(100,100);
setSize(FRAME_W,FRAME_H);
setVisible(true);
//窗口右上角X按钮的监听器
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);
}
//键盘监听器
class KeyMonitor extends KeyAdapter {
public void keyPressed(KeyEvent e) {
myTank.keyPressed(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;//坦克方向
//方向枚举类
public enum Direction {
Right,Left,Up,Down
}
//构造方法
public Tank(int x, int y){
this.tank_x = x;
this.tank_y = y;
}
//坦克的绘制方法
public void draw(Graphics g) {
Color c = g.getColor();
g.setColor(Color.GREEN);
g.fillOval(tank_x, tank_y, 50, 50);
g.setColor(c);
}
//按键事件的处理方法
public void keyPressed(KeyEvent e){
int code = e.getKeyCode();//获取所按键的编码
//根据编码,修改方向并移动
switch(code) {
case KeyEvent.VK_UP:
direction = Direction.Up;
tank_y -= Y_SPEED;
break;
case KeyEvent.VK_DOWN:
direction = Direction.Down;
tank_y += Y_SPEED;
break;
case KeyEvent.VK_LEFT:
direction = Direction.Left;
tank_x -= X_SPEED;
break;
case KeyEvent.VK_RIGHT:
direction = Direction.Right;
tank_x += X_SPEED;
break;
}
}
}