知识回顾:前一篇讲解了窗口里面绘制的正方形已经可以跟随着键盘的上下左右键移动了
如果按键改变的不是每次+10或者减10,这时候又要改变源码,所以优化代码
思路:使用枚举定义坦克的方向:
新建一个枚举类(Dir.class):定义坦克的方向,然后在TankFrame中调用
1.定义坦克方向的enum,根据按键状态确定坦克方向,根据坦克方向确定坦克位移
public enum Dir {
//方向向左
LEFT,
//方向向上
UP,
//方向向右
RIGHT,
//方向向下
DOWN
}
2.修改TankFrame的方法让Tank运动起来:
public class TankFrame extends Frame {
private static int x = 200;
private static int y = 200;
//默认方向向下
Dir dir = Dir.DOWN;
//默认坦克的速度为10
private static int SPEED = 3;
public TankFrame(){
//设置窗口的尺寸
this.setSize(800,600);
//设置窗口位置
this.setLocation(500,200);
//参数设置为true,当程序运行时,窗口显示
this.setVisible(true);
//设置窗口标题
this.setTitle("Tank war");
//添加键盘监听事件
this.addKeyListener(new MyKeyListener());
//点击窗口X关闭窗口,添加监听事件
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
/**
* 重写paint方法,然后在窗口中指定位置画出一个矩形
* @param g
*/
@Override
public void paint(Graphics g){
g.fillRect(x,y,25,25);
switch (dir){
case LEFT:
x-=SPEED;
break;
case UP:
y-=SPEED;
case RIGHT:
x+=SPEED;
break;
case DOWN:
y+=SPEED;
break;
default:
break;
}
}
/**
* 键盘监听处理类
*/
class MyKeyListener extends KeyAdapter{
//定义几个默认的方向
boolean bL = false;
boolean bU = false;
boolean bR = false;
boolean bD = false;
//按下一个键的时候
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
switch (key){
case KeyEvent.VK_LEFT:
bL = true;
x-=SPEED;
break;
case KeyEvent.VK_UP:
bU = true;
y-=SPEED;
break;
case KeyEvent.VK_RIGHT:
bR = true;
x+=SPEED;
break;
case KeyEvent.VK_DOWN:
bD = true;
y+=SPEED;
break;
default:
break;
}
setTankDir();
}
//按键抬起的时候
@Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
switch (key){
case KeyEvent.VK_LEFT:
bL = false;
break;
case KeyEvent.VK_UP:
bU = false;
break;
case KeyEvent.VK_RIGHT:
bR = false;
break;
case KeyEvent.VK_DOWN:
bD = false;
break;
default:
break;
}
setTankDir();
}
private void setTankDir(){
if (bL) dir=Dir.LEFT;
if (bU) dir=Dir.UP;
if (bR) dir=Dir.RIGHT;
if (bD) dir=Dir.DOWN;
}
}
}
3.现在我们都在TankFrame里面做的一系列操作,现在我们是否需要抽象一个Tank的类,封装相应的属性和方法
抽象出坦克类,封装相应的属性和方法,在tankFrame中直接调用
package com.tank;
import java.awt.*;
/**
* @author litao
* @date 2020-05-25
* Tank封装类
*/
public class Tank {
//定义初始位置
private int x,y;
//定义初始速度
private static final int SPEED = 10;
//默认方向
private Dir dir = Dir.DOWN;
public Dir getDir() {
return dir;
}
public void setDir(Dir dir) {
this.dir = dir;
}
//定义坦克的构造方法
public Tank(int x,int y ,Dir dir){
this.x = x;
this.y = y;
this.dir = dir;
}
/**
* tank里面封装画坦克的方法,直接在TankFrame里面调用
* @param g
*/
public void paint(Graphics g){
g.fillRect(x,y,50,50);
switch (dir){
case LEFT:
x-=SPEED;
break;
case UP:
y-=SPEED;
case RIGHT:
x+=SPEED;
break;
case DOWN:
y+=SPEED;
break;
default:
break;
}
}
}
4.怎样处理坦克静止的状态,因为现在我们的坦克默认是运动状态的:moving = false;
思路:在tank类里面添加一个boolean moving标识,如果上下左右键有按下,moving为true,反之为false;(在tankFrame里面设置是否为true或者false)
//TankFrame类里面的方法,具体源码看gitHub
private void setMainTankDir(){
//上下左右键都没有按下时,设置moving为false
if(!bL && !bU && !bR && !bD){
myTank.setMoving(false);
}else {
myTank.setMoving(true);
if (bL) {
myTank.setDir(Dir.LEFT);
}
if (bU) {
myTank.setDir(Dir.UP);
}
if (bR) {
myTank.setDir(Dir.RIGHT);
}
if (bD) {
myTank.setDir(Dir.DOWN);
}
}
}
5.按下Ctrl键,主站坦克打出一个子弹,用面向对象的思想考虑
思路:5.1封装一个子弹,新建一个Bullect类
package com.tank;
import java.awt.*;
/**
* @author litao
* @date 2020-05-25
* 子弹封装类
*/
public class Bullet {
//定义子弹的宽度和高度
private static final int BULLET_WIDTH = 30,BULLET_HEIGHT = 30;
private static final int SPEED = 10;
//定义初始位置
private int x,y;
//定义方向
private Dir dir;
//子弹的构造方法
public Bullet(int x,int y,Dir dir){
this.x = x;
this.y = y;
this.dir = dir;
}
public void paint(Graphics g){
Color color =g.getColor();
g.setColor(Color.RED);
g.fillOval(x,y,BULLET_WIDTH,BULLET_HEIGHT);
g.setColor(color);
move();
}
private void move() {
switch (dir){
case LEFT:
x-=SPEED;
break;
case UP:
y-=SPEED;
case RIGHT:
x+=SPEED;
break;
case DOWN:
y+=SPEED;
break;
default:
break;
}
}
}
5.2按下ctrl键,tank会fire出一颗子弹,必须持有tankframe的引用,把子弹传递给窗口,画出来
在Tank类里面写一个fire的方法:
按下ctrl键,tank会fire出一颗子弹,必须持有tankframe的引用,把子弹传递给窗口,画出来
//坦克发射子弹
public void fire() {
tf.bullet = new Bullet(this.x,this.y,this.dir);
}
6.连续按Ctrl可以打出许多的子弹,讲子弹装在一个容器里就可以了。
List<Bullet> bullets = new ArrayList<>();
7.当我们打出的子弹数量超出我们画的边框的时候,子弹的数量还是在无限增加,所以当子弹飞出窗口以后,就把子弹remove掉。
消除子弹列表的内存泄漏问题,小心处理迭代器中的删除问题,1:使用普通方式迭代 2:在迭代过程中删除(iterator.remove)
和判断静止还是移动是一个道理,当超出边界范围的时候,直接remove掉bullets里面的子弹数量
if(x < 0 || y < 0 || x > TankFrame.GAME_WIDTH || y > TankFrame.GAME_HEIGHT) living = false;