Java多线程初使用3——飞机大战V0版本——ArrayList
上篇文章我们使用了双缓冲技术
进行了画笔优化,今天我们仍然使用双缓冲,但是对线程部分进行优化——使用ArrayList
实现线程的存放。
一、GameUI界面
public class GameUI extends JFrame {
public void initUI(){
setTitle("飞机大战V0");
setSize(800,600);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
// 流式布局
setLayout(new FlowLayout());
// 添加按钮
Button btn = new Button("start");
add(btn);
Graphics g = getGraphics();
// 按钮监听器
GameListener gl = new GameListener(g);
btn.addActionListener(gl);
}
public static void main(String[] args) {
new GameUI().initUI();
}
}
二、创建飞机类
先不急着创建按钮监听器,毕竟我们很熟悉流程了。
public class Fly {
protected int x,y; // 出现位置
protected int speedx,speedy;// 速度
protected int type; // 判断是敌机还是我机
protected ImageIcon imageIcon; // 图标
protected Image image;// 图标转成图片
}
创建我方飞机和敌方飞机类,很明显可以继承Fly(继承也能很好解决ArrayList的冲突问题,继承后无论是Enemy还是Our均不会冲突)。
public class Our extends Fly{
public Our(int x,int y) {
this.x = x;
this.y = y;
this.type = 0;
this.imageIcon = new ImageIcon("src\\xw_Class\\xw_18Class_Thread_PlainV0\\Our.png");
this.image = imageIcon.getImage();
}
}
public class Enemy extends Fly{
Random random = new Random();
public Enemy(int x,int y) {
this.x = x;
this.y = y;
this.type = 1;
this.imageIcon = new ImageIcon("src\\xw_Class\\xw_18Class_Thread_PlainV0\\Enemy.png");
this.image = imageIcon.getImage();
this.speedx = random.nextInt(40)-20;
this.speedy = random.nextInt(40)-20;
}
}
ImageIcon
和Image
类的使用大家要注意,用getImage()
即可获取ImageIcon。
这里两种飞机的图片大家可以自行上网查找,博主的图片是这样子的。
我方飞机:
敌方飞机:
三、本次监听器画图思路——ArrayList
这次我们不再像之前那样一次性创建多个线程,而是先把对象存放到ArrayList中,让线程类每隔固定时间取出一个对象进行画图,这样可以优化线程。
public class GameListener implements ActionListener {
Graphics g;
BufferedImage buffimg;
Graphics bg;
DrawImageThread dit;// 后续实现,为之前的while(true)死循环线程类,用于画buffimg
DrawThread dt;// 后续实现,为向buffimg画图的线程类,将向buffimg画图与飞机类分离开来。
// 使用ArrayList存放对象,后续让线程每隔一定时间取出对象,达到线程优化的目的
ArrayList<Fly> array = new ArrayList<>();
Random random = new Random();
public GameListener(Graphics g) {
this.g = g;
array.add(new Our(350,400));// 初识添加一个我方飞机
}
@Override
public void actionPerformed(ActionEvent e) {
if (buffimg == null){
buffimg = new BufferedImage(800,600,2);
}
if(bg == null){
bg = buffimg.getGraphics();
}
int x = random.nextInt(800);
int y = random.nextInt(200);
Enemy enemy = new Enemy(x,y); // 点击添加一个敌方飞机入队列
this.array.add(enemy);
if (dt == null){
dt = new DrawThread(bg,array); // 画进buffimg
dt.start();
}
if(dit == null){
dit = new DrawImageThread(g,buffimg);// 将buffimg画出
dit.start();
}
}
}
四、DrawThread画buffimg
public class DrawThread extends Thread{
Graphics g;
ArrayList<Fly> array = new ArrayList<>();// 所有类共享一个ArrayList
public DrawThread(Graphics g, ArrayList<Fly> array) {
this.g = g;// Gamelistener将buffimg的画笔传过来
this.array = array;
}
@Override
public void run() {
for(;;){
for (int i = 0; i < array.size(); i++) {
Fly f = array.get(i);
// 取出来的是敌机还是我机?
if(f.type == 1) {// 敌机
f.x += f.speedx;
f.y += f.speedy;
g.drawImage(f.image, f.x, f.y,50,50, null);
}
if(f.type == 0){// 我机
g.drawImage(f.image, f.x, f.y,50,50, null);
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
g.setColor(new Color(238,238,238));
g.fillRect(0,0,800,600);
}
}
}
五、DrawImageThread死循环线程
public class DrawImageThread extends Thread{
Graphics g;
BufferedImage buffimg;
public DrawImageThread(Graphics g, BufferedImage buffimg) {
this.g = g;// GameListener将窗体画笔传过来,用于将buffimg呈现
this.buffimg = buffimg;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(30);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
g.drawImage(buffimg,0,0,null);
}
}
}
六、效果展示
怎么样,是不是稍微有点像了,之后我们将继续完善项目。