V0.7:在这一个版本里主要处理边界问题。
首先是炮弹,炮弹出界后就死亡。先判断是否出界,这个只要通过比较炮弹的坐标和边界坐标即可,比较可以再move中进行,也可以在draw中进行,因为move本身就是在draw中调用,在我看来,差别不大。然后是死亡这个动作,我们通过将子弹对象移出 ArrayList 来表示,这样呢,我们就要在炮弹类中操作 ArrayList ,所以在炮弹类中我们需要一个TankFrame 的引用,添加引用,修改构造方法,然后在出界判断逻辑中调用ArrayList 的 remove 方法。
其次是坦克。坦克更好办。只要判断出界就重置位置坐标就可以了,就是说小于零就置为零,大于窗口宽度或高度就置为窗口宽度或高度。
V0.8:在这个版本中我们要加入敌方坦克。
说实话,刚开始在我自己思考的时候我觉得应该要新建一个RobotTank类,因为敌方坦克的移动、开火,都不是靠我们按键来实现的,也就是说除了构造方法和draw方法,其他的都跟已经写好的Tank类不一样,甚至为了区分敌我,draw 方法使用的颜色也不一样。但是!看了视频发现马士兵老师是直接用的Tank类,这个时候我真的挺纠结的,到底是新建一个类还是使用现有的类。感觉从面向对象的角度来说是应该使用现有的类对吧?毕竟两者都是坦克,只是内部方法略有不同。
而这时候!我又想到了抽象类,就是说将Tank类改为一个抽象类,然后有 MyTank 和 RobotTank 两个子类,将两者相同的部分在抽象类中实现,不同的方法则声明为抽象函数,在各自内部实现。感觉上这应该是最靠谱的解决方案了吧?
可惜的是!博主太懒了,太懒了,太懒了。。。最终还是直接新建一个类,然后复制粘贴了事,因为嫌抽象类改动太大,而使用现有的 Tank 类会在后面将Tank类改动太多,看起来会很杂乱,所以。。。you know ,有兴趣的同学可以自己试一下。
新建一个RobotTank 类,然后将基本属性和构造方法还有draw 方法复制过去之后,就可以在窗口类添加一个 RobotTank 类的ArrayList,临时创建一个RobotTank 对象,加入到ArrayList 中,然后调用其draw 方法来观察效果了。当然,仅仅是用来观察效果,因为博主打算每隔一段时间产生一定数量的坦克,只是还没想好应该在哪个方法中生成敌方坦克,感觉每隔一段时间执行一个操作和调用repaint 方法相似,或许可以新建一个线程?往下看吧。
V0.9:在这个版本中呢,我们处理游戏中最常碰到的一个经典问题,collision (碰撞)。也就是如何判断子弹击中对方坦克的问题。这个问题在任何游戏中都会涉及,比如两人pk时怎么判断攻击到了对方,对于这个问题不同环境下有不同的算法。我们这算是最简单的一个了,因为我们直接使用了Java 提供给我们的一个方法。
具体如下:
在炮弹类和RobotTank 类中分别添加一个getRectangle 方法,用来获取当前对象(圆)的一个外接正方形。
然后在子弹中添加一个collide 方法,遍历敌方坦克的ArrayList,使用Rectangle 类的intersects 方法来判断两个矩形是否相交。若相交则说明发生碰撞,炮弹击中坦克。击中坦克之后则双方都死亡,从各自的ArrayList 中移除。
到此版本的代码如下:
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 = 600;
public static final int FRAME_W = 800;
Tank myTank = new Tank(200,200,this);
private ArrayList<Cannonball> cannonballs = new ArrayList<Cannonball>();
public ArrayList<RobotTank> robots = new ArrayList<RobotTank>();
public TankFrame(String s){
super(s);
}
public void launchFrame () {
setLocation(100,100);
setSize(FRAME_W,FRAME_H);
setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
this.addKeyListener(new KeyMonitor());
new Thread(new PaintThread()).start();
robots.add(new RobotTank(400,300,this));
}
public void paint(Graphics g) {
// g.drawString("Cannonballs number : " + cannonballs.size(), 50, 50);
myTank.draw(g);
for(int i = 0; i < robots.size(); i++){
RobotTank rt = robots.get(i);
rt.draw(g);
}
for(int i = 0; i < cannonballs.size();i++){
Cannonball c = cannonballs.get(i);
c.draw(g);
}
}
public void addCannonball(Cannonball c) {
cannonballs.add(c);
}
public void deleteCannonball(Cannonball c) {
cannonballs.remove(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(50);
} 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 static final int TANK_R = 25;//坦克圆的半径
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,TANK_R *2, TANK_R *2);
int x1,x2,y1,y2;
x1 = x2 = tank_x + 25;
y1 = y2 = tank_y + 25;
switch(direction){
case Up:
y1 -= TANK_R;
y2 -= TANK_R * 2;
break;
case Down:
y1 += TANK_R;
y2 += TANK_R * 2;
break;
case Left:
x1 -= TANK_R;
x2 -= TANK_R * 2;
break;
case Right:
x1 += TANK_R;
x2 += TANK_R * 2;
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;
}
if(tank_x < 10) tank_x = 10;
if(tank_y < 30) tank_y = 30;
if(tank_x > client.FRAME_W - TANK_R * 2) tank_x = client.FRAME_W - TANK_R * 2;
if(tank_y > client.FRAME_H - TANK_R * 2) tank_y = client.FRAME_H - TANK_R * 2;
}
public void fire(){
Cannonball c = new Cannonball(tank_x + 20,tank_y + 20,direction,client);
client.addCannonball(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:
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;
}
}
}
RobotTank.java
import java.awt.*;
public class RobotTank {
private static final int X_SPEED = 5;
private static final int Y_SPEED = 5;
private static final int TANK_R = 25;//坦克圆的半径
private int tank_x = 0;
private int tank_y = 0;
private Tank.Direction direction = Tank.Direction.Down;
private TankFrame client = null;
public RobotTank(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.BLUE);
g.fillOval(tank_x, tank_y,TANK_R *2, TANK_R *2);
int x1,x2,y1,y2;
x1 = x2 = tank_x + 25;
y1 = y2 = tank_y + 25;
switch(direction){
case Up:
y1 -= TANK_R;
y2 -= TANK_R * 2;
break;
case Down:
y1 += TANK_R;
y2 += TANK_R * 2;
break;
case Left:
x1 -= TANK_R;
x2 -= TANK_R * 2;
break;
case Right:
x1 += TANK_R;
x2 += TANK_R * 2;
break;
}
g.drawLine(x1, y1, x2, y2);
g.setColor(c);
}
public Rectangle getRectangle(){
return new Rectangle (tank_x ,tank_y ,TANK_R *2, TANK_R *2);
}
}
Cannonball.java
import java.awt.*;
public class Cannonball {
private static final int X_SPEED = 10;
private static final int Y_SPEED = 10;
private int x = 0;
private int y = 0;
private Tank.Direction direction = null;
private TankFrame client = null;
public Cannonball(int x, int y, Tank.Direction dir,TankFrame t) {
this.x = x;
this.y = y;
this.direction = dir;
this.client = t;
}
public void draw(Graphics g) {
collide();
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;
}
if (x < 0 || y < 0 || x > client.FRAME_W || y > client.FRAME_H) client.deleteCannonball(this);
}
public Rectangle getRectangle(){
return new Rectangle (x ,y ,10,10);
}
public void collide () {
for(int i = 0; i < client.robots.size(); i++){
RobotTank rt = client.robots.get(i);
if (this.getRectangle().intersects(rt.getRectangle())) {
client.deleteCannonball(this);
client.robots.remove(rt);
}
}
}
}