小的时候我们玩过很多小游戏,比如:坦克大战、贪吃蛇、超级玛丽、推箱子、飞机大战等等。今天就用java写一个小游戏,飞机大战。飞机大战的主要知识点就是线程,只要对线程有基本的了解就能完成飞机大战的编程。
关于飞机大战的编写,接下来分几个步骤来完成。
(用一张球的图片来替代敌方飞机,之后更换图片就好)
1.首先要有一个图形界面(定义为BallFream类)
继承JPanel是为了获取它的画笔,这不是最简便的方法。接下来要使用画笔,你用其他的方法获取也可以.
import javax.swing.JFrame;
import javax.swing.JPanel;
public class BallFrame extends JPanel{
public static void main(String[] args) {
BallFrame bf = new BallFrame();
bf.showFrame();
}
public void showFrame() {
JFrame frame = new JFrame();
frame.setTitle("球");
frame.setSize(500,600);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(3);
frame.setResizable(false);
//将JPanel添加到JFrame,this就是JPanel
frame.add(this);
frame.setVisible(true);
}
}
2.定义一个(Ball类)类来存储对象(现在用的图片是球,定义一个球类存储相应的参数)
同时将球的图片引入进来,之后会使用。引入球的图片用ImageIcon,将图片直接复制到当前包下。通过新建一个对象new ImageIcon(this.getClass().getResource("图片名.类型名")).getImage();
public class Ball {
private int x,y,width,height;
private Image image;
public Ball(int x, int y, int width, int height) {
super();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.image = new ImageIcon(this.getClass().getResource("ball.png")).getImage();
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
3.将图片画出来和实现球的移动
在定义的对象类(Ball类)里写画球方法和移动方法。球并不是真的移动,而是我们改变了球的坐标。画完一个球后,画一个窗体大小的颜色与窗体背景色一样的矩形,会把之前画的球覆盖掉,接下来按照新的坐标再画一次。因为电脑画的非常快,在我们看来就达到了移动的效果。
//画球的方法,把图画出来,给定了位置、大小
public void draw(Graphics2D g) {
g.drawImage(image, x - 80/2, y - 80/2, 80, 80,null);
}
//移动的方法,改变坐标y的值,让球看起来在下落
public void move() {
y+=5;
}
4.添加监听和继承线程接口(Runnable)
我们希望在窗体某个位置点击鼠标的时候就生成一个球,并开始运动。这就要添加鼠标监听机制,还要有事件处理类(BallListener类)。
继承Runnable是为了让移动方法不断执行,这样球的纵坐标就会不断改变,达到移动的效果。
添加监听方法并启动线程
在图形界面类(BallFrame类)中添加和启动,启动线程要先与Runnable建立联系,也就是实例化Thread类。同时在实例化事件处理类BallListener是将需要使用的参数传递过去,这里把窗体传递过去,可以通过窗体获取参数。
BallListener bl = new BallListener(this);
this.addMouseListener(bl);
Thread td = new Thread(bl);
td.start();
5.事件处理类BallListener
继承MouseAdapter类只需重写用到的抽象方法;
继承Runnable接口,重写run()方法。
实现球的移动,需要将以前画的球去掉,留下最新画的球。画一个界面大小的矩形,将以前的球覆盖掉再画新的球。
在界面上可能不止一个球在移动,而是多个球在移动,就需要把所有的球存储起来。建立一个数组队列用来存储生成的球, 鼠标点击一次就生成一个球,在鼠标点击后就将球存储起来。当线程运行的时候,遍历数组队列,将所有的球都画一遍。
将球和矩形画出来就需要画笔,而我们已经将窗体传过来了,如果画笔为空,就获取画笔。
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
public class BallListener extends MouseAdapter implements Runnable{
private BallFrame bf;
private int x,y;
private Ball ball;
//创建数组队列,直接通过Ball类来存储
private ArrayList list = new ArrayList();
//你也可以用Graphics,不用Graphics2D
private Graphics2D g;
//构造函数
public BallListener(BallFrame bf) {
this.bf = bf;
}
//重写鼠标点击方法,获取鼠标点击的坐标,把球的坐标、宽度、高度存储起来
public void mouseClicked(MouseEvent e) {
x = e.getX();
y = e.getY();
ball = new Ball(x,y,80,80);
list.add(ball);
}
//重写线程的抽象方法
public void run() {
//while循环,让代码不断执行
while(true) {
//判断画笔是否为空,如果是则获取画笔
if(g == null) {
g = (Graphics2D) bf.getGraphics();
//画笔抗锯齿(让图形边缘变得光滑)
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
//获取窗体的背景颜色
g.setColor(bf.getBackground());
//画一个窗体大小矩形,去掉移动痕迹
g.fillRect(0, 0, 500, 600);
//遍历数组队列,将队列里的球都画出来
for(int i = 0; i < list.size(); i++) {
//判断语句,当球移动出窗体可见范围后就将它从数组队列移除,减少内存消耗
if(y < bf.getHeight()) {
ball = list.get(i);
//调用画球的方法
ball.draw(g);
//调用移动方法
ball.move();
}else {
//移除不可见的球
list.remove(ball);
}
}
//线程休眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
6.补充
关于调用Ball类的移动方法后数组队列里存储的值也会改变问题。
ball = list.get(i);只是把地址赋给了ball,并不是直接把存储的值给了ball。当ball调用移动方法后改变了y的值,改变的值是数组队列中的值。
7.总结
以上实现的功能为:鼠标点击窗体的某一个位置时,生成一个球,点击多次,生成多个球。之后球会向下移动。
但存在屏闪问题。有可能在画一个球的同时,一个清除移动痕迹的矩形也在绘制,会造成相互冲突。我们希望球在同一时间画出来,接下来就解决这个问题。
注意,显示缓冲区是和显示器一起的,显示器只负责从显示缓冲区取数据显示。我们通常所说的在显示器上画一条直线,其实就是往该显示缓冲区中写入数据。显示器通过不断的刷新(从显示缓冲区取数据),从而使显示缓冲区中数据的改变及时的反映到显示器上。
这也是显示复杂图形时造成闪烁的原因,比如你现在要显示从屏幕中心向外发射的一簇射线,你开始编写代码用一个循环从0度开始到360度,每隔一定角度画一条从圆心开始向外的直线。你每次画线其实是往显示缓冲区写入数据,如果你还没有画完,显示器就从显示缓冲区取数据显示图形,此时你看到的是一个不完整的图形,然后你继续画线,等到显示器再次取显示缓冲区数据显示时,图形比上次完整了一些,依次下去直到显示完整的图形。你看到图形不是一次性完整地显示出来,而是每次显示一部分,从而造成闪烁。
--------------------- 本文来自 Smith先生 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/acs713/article/details/16359551?utm_source=copy
所以,我们引入次画布。将数组队列里存储的信息遍历完后将图形在次画布上画出来,再把次画布和次画布上的图形画到窗体上。
在BallListener类进行操作,对run()方法进行修改。
private BufferedImage bi;//可用来构建次画布
public void run() {
while(true) {
//引入次画布,先在次画布上消除移动的痕迹,然后把图形按照数组队列逐一画出来
BufferedImage bi = new BufferedImage(bf.getWidth(), bf.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
//获取次画布上的画笔
ig =(Graphics2D) bi.getGraphics();
ig.setColor(bf.getBackground());
//用次画布上的画笔在次画布上消除移动痕迹
ig.fillRect(0, 0, bf.getWidth(), bf.getHeight());
for(int i = 0; i < list.size(); i++) {
//图形移动出窗体可见后就移除,减少占用
if(x > 0 && x < bf.getWidth() && y > 0 && y < bf.getHeight()) {
ball = list.get(i);
//用次画布上的画笔画球
ball.draw(ig);
ball.move();
}else {
list.remove(ball);
}
}
//如果画笔为空,则获取窗体上的画笔
if(g == null) {
g = (Graphics2D) bf.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
//将次画布上的图形画到窗体上
g.drawImage(bi, null, bf);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三、敌方飞机自动生成和发射子弹
界面类PlaneFrame
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PlaneFrame extends JPanel{
public static void main(String[] args) {
PlaneFrame pf = new PlaneFrame();
pf.showFrame();
}
public void showFrame() {
JFrame frame = new JFrame();
frame.setTitle("球");
frame.setSize(700,600);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(3);
frame.setResizable(false);
//将JPanel添加到JFrame,this就是JPanel
frame.add(this);
frame.setVisible(true);
PlaneListener bl = new PlaneListener(this);
//添加鼠标监听方法
this.addMouseListener(bl);
//与线程建立联系
Thread td = new Thread(bl);
//调用启动线程的方法
td.start();
}
}
事件处理类PlaneListener
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
public class PlaneListener extends MouseAdapter implements Runnable{
private PlaneFrame pf;
private int x,y,width,height;
private Plane plane;
private ArrayList list = new ArrayList();//创建球类数组队列,存储数据
private Graphics g;
private BufferedImage bi;//可用来构建次画布
private Graphics ig;
private Image background;
//构造函数,把窗体传递过来
public PlaneListener(PlaneFrame pf) {
this.pf = pf;
}
public void run() {
while(true) {
//引入次画布,先在次画布上消除移动的痕迹,然后把图形按照数组队列逐一画出来
BufferedImage bi = new BufferedImage(pf.getWidth(), pf.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
//获取次画布上的画笔
ig = bi.getGraphics();
// ig.setColor(bf.getBackground());
//用次画布上的画笔在次画布上消除移动痕迹
// ig.fillRect(0, 0, bf.getWidth(), bf.getHeight());
//引入背景图片,用背景图片替代矩形达到察除移动痕迹的目的
background = new ImageIcon(this.getClass().getResource("background.jpg")).getImage();;
ig.drawImage(background, 0, 0, pf.getWidth(), pf.getHeight(),null);
for(int i = 0; i < list.size(); i++) {
if(x < pf.getWidth() && y < pf.getHeight()) {
plane = list.get(i);
//用次画布上的画笔画球
plane.draw(ig);
plane.move();
}else {
list.remove(plane);
}
}
//如果画笔为空,则获取窗体上的画笔
if(g == null) {
g = pf.getGraphics();
}
//将次画布上的图形画到窗体上
g.drawImage(bi,0,0, null);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
定义存储类Plane
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ImageIcon;
public class Plane {
private int x,y,width,height;
private Image image,img;
private int size;
private PlaneFrame pf;
private int addx;
private int speed;
//构造方法
//获取图片小球的数据
public Plane(int x, int y, int width, int height,int speed,,PlaneFrame pf,Image image) {
super();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
this.pf = pf;
this.image = image;
this.img = new ImageIcon(this.getClass().getResource("bullet.png")).getImage();
}
//画图
public void draw(Graphics g) {
g.drawImage(image, x-width/2, y-height/2, width, height,null); //画图
}
//移动
public void move() {
y+=speed;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
1.之前我们是点击鼠标才会生成图片,现在我们要让图片自动生成。这就需要新建一个线程,在程序开始执行的时候通过start()方法来启动线程的run()方法来达到我们目的 。
在PlaneListener类的构造方法里启动新的线程PlaneThread类(未定义),并传递参数。
public PlaneListener(PlaneFrame pf) {
this.pf = pf;
//这里的10为产生敌机的数量,可修改
PlaneThread pt = new PlaneThread(pf,list,10,width,height);
//启动新线程
pt.start();
}
在run()方法里写一个循环,给定size的大小,也就是产生敌方飞机的数量。再利用休眠,每隔一段时间就产生一架敌机,并且利用数组队列存储起来。
接下来是PlaneThread类
import java.awt.Image;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ImageIcon;
public class PlaneThread extends Thread{
private int size;
private PlaneFrame pf;
private ArrayList list;
private int x,y,width,height;
private Image image;
//构造方法
public PlaneThread(PlaneFrame pf,ArrayList list,int size,int width, int height) {
this.pf = pf;
this.list = list;
this.size = size;
this.width = width;
this.height = height;
//敌机的图片
this.image = new ImageIcon(this.getClass().getResource("fighter.png")).getImage();
}
public void run() {
//循环,利用随机数让敌机生成的横坐标无规律
for(int i = 0; i < size; i++) {
Random rand = new Random();
x = 40 + rand.nextInt(pf.getWidth() - 200) + 1;
y = 20;
//利用数组队列将敌机存储起来
//x-80/2,y-80/2为坐标,80,80为图片的宽度和高度,2为移动速度,image为图片
Plane ball = new Plane(x-80/2,y-80/2,80,80,2,list,pf,image);
list.add(ball);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.敌机自动发射子弹,我这里是让敌机每向下移动50单位距离就产生一枚子弹。在Plane类的移动方法里进行操作。当敌机的y坐标除以50没有余数的时候就生成一枚子弹。同时需要将数组队列传过来,把生成的子弹存储起来。传递数组对列这里就不再重复了,通过构造函数,还要将子弹的图片引入,下面是Plane类里移动方法的修改。
public void move(ArrayList list) {
//用speed来指代每次移动距离
y+=speed;
//判断是否应该生成子弹
if(y%50 ==0) {
//将子弹存储起来
//x,y为子弹生成的坐标;20,20为子弹图片的大小;6为子弹的移动速度;img为子弹的图片
Plane bullet = new Plane(x,y,20,20,6,list,pf,img);
list.add(bullet);
}
注意:到达这一步运行程序后会发现,敌机每隔50个单位距离会产生一颗子弹,而之前产生的子弹也会每隔50个单位生产一颗子弹。这是因为判断生成子弹的时候只根据距离来判断,没有区分敌机还是子弹,这个问题后面会解决。
四、己方飞机和子弹
1.通过鼠标控制己方飞机,当鼠标在窗体上移动时己方飞机跟着移动,同时还需给定己方飞机的初始位置。创建一个myplane对象来对应己方飞机,实时更新己方飞机的坐标值。在PlaneListener类进行操作
//给定己方飞机的初始坐标值
private int x = 330,y = 520,width,height;
//引入己方飞机的图片
private Image imagemyplane = new ImageIcon(this.getClass().getResource("myplane.png")).getImage();
private Plane myplane;
//构造函数,把窗体传递过来
public PlaneListener(PlaneFrame pf) {
this.pf = pf;
//存储己方飞机的数据
myplane = new Plane(x,y,80,80,0,list,pf,imagemyplane);
list.add(myplane);
//启动生成敌机的线程
PlaneThread pt = new PlaneThread(pf,list,50,width,height);
pt.start();
}
public void mouseMoved(MouseEvent e){
//获取鼠标移动时的坐标值
x = e.getX();
y = e.getY();
//将坐标赋给myplane对象
myplane.setX(x);
myplane.setY(y);
}
2.己方飞机发射不断发射子弹,创建一个己方飞机的子弹线程MyBullet。己方飞机的子弹通过获取己方飞机的坐标来生成子弹。
import java.awt.Image;
import java.util.ArrayList;
import javax.swing.ImageIcon;
public class MyBullet extends Thread{
private PlaneFrame pf;
private ArrayList list;
private int x,y,width,height;
private Image imagemybullet;
private Plane myplane;
//构造函数
public MyBullet(PlaneFrame pf,ArrayList list,int x,int y,int width, int height,Plane myplane) {
this.pf = pf;
this.list = list;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.myplane = myplane;
//引入己方飞机的子弹图片
this.imagemybullet = new ImageIcon(this.getClass().getResource("mybullet.png")).getImage();
}
public void run() {
while(true) {
//获取己方飞机的坐标
x = myplane.getX();
y = myplane.getY();
//存储己方飞机的子弹
//x,y为坐标,20,20为图片的宽度和高度,-6为移动速度(负号是移动方向)
Plane mybullet = new Plane(x,y,20,20,-6,list,pf,imagemybullet);
list.add(mybullet);
//每隔500毫秒生成一颗子弹
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
五、判断飞机大战输赢
1.为解决子弹每隔50个单位距离生成一颗子弹,用字符对存储的信息进行标识,确认是敌机才会生成子弹。
在Plane类里操作
public void move(ArrayList list) {
y+=speed;
for(int i = 0;i < list.size();i++) {
//如果是敌机且移动50个单位距离
if(type.equals("e_p") && y%50 == 0) {
Plane bullet = new Plane(x,y,20,20,6,list,pf,imagebullet,"e_b");
list.add(bullet);
}break;
}
}
2.在Plane类里写一个碰撞crash方法,敌机和敌机子弹碰到己方飞机子弹时会消失(从数组队列里一起移除);敌机和敌机子弹碰到己方飞机时,游戏结束,线程停止。static关键字的运用可定义静态变量,在不同的类可通过类名.对象名调用,根据这个来控制PlaneListener类里的run()方法的
Plane类里的crash()方法
public void crash() {
for(int j = 0;j < list.size();j++){
Plane lt = list.get(j);
if(lt.getType().equals("m_p")) {
for(int i = 0;i < list.size();i++) {
Plane temp = list.get(i);
if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {
double xx = lt.getX() - temp.getX();
double yy = lt.getY() - temp.getY();
double ww = lt.getWidth() + temp.getWidth();
if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {
list.remove(lt);
list.remove(temp);
//给静态属性赋值
PlaneListener.flag=false;
}
}
}
}
if(lt.getType().equals("m_b")) {
for(int i = 0;i < list.size();i++) {
Plane temp = list.get(i);
if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {
double xx = lt.getX() - temp.getX();
double yy = lt.getY() - temp.getY();
double ww = lt.getWidth() + temp.getWidth();
if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {
list.remove(temp);
list.remove(lt);
}
}
}
}
}
}
下面贴上完整代码(此版本的飞机大战很简单,可以在此基础上进行开发,你可以发挥自己的想象,增设关卡、血条、奖励、Boss等等)(可能存在少许未使用代码和注释掉的代码没有删除)
import java.awt.Color;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PlaneFrame extends JPanel{
public static void main(String[] args) {
PlaneFrame pf = new PlaneFrame();
pf.showFrame();
}
public void showFrame() {
JFrame frame = new JFrame();
frame.setTitle("飞机大战");
frame.setSize(700,600);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(3);
frame.setResizable(false);
//将JPanel添加到JFrame,this就是JPanel
frame.add(this);
frame.setVisible(true);
PlaneListener bl = new PlaneListener(this);
//添加鼠标监听方法
// this.addMouseListener(bl);
this.addMouseMotionListener(bl);
//与线程建立联系
Thread td = new Thread(bl);
//调用启动线程的方法
td.start();
//让线程里的代码执行
bl.setFlag(true);
}
}
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import javax.swing.ImageIcon;
public class PlaneListener extends MouseAdapter implements Runnable{
private PlaneFrame pf;
private int x = 330,y = 520,width,height;
private Plane plane;
private ArrayList list = new ArrayList();//创建飞机类数组队列,存储数据
private Graphics2D g;
private BufferedImage bi;//可用来构建次画布
private Graphics ig;
private Image background;
private Image imagemyplane = new ImageIcon(this.getClass().getResource("myplane.png")).getImage();;
private Plane myplane;
private String type;
//设置为静态属性,可在别的类直接调用
public static boolean flag = true;
//构造函数,把窗体传递过来
public PlaneListener(PlaneFrame pf) {
this.pf = pf;
//创建我的飞机对象,并存储起来
myplane = new Plane(x,y,80,80,0,list,pf,imagemyplane,"m_p");
list.add(myplane);
//创建敌机线程,让敌机自动出现,把用到的参数传递过去
PlaneThread pt = new PlaneThread(pf,list,50,width,height);
pt.start();
//创建我的飞机的子弹线程,让子弹自动发射并传递参数
MyBullet mb = new MyBullet(pf,list,x,y,width,height,type,myplane);
mb.start();
}
public PlaneListener() {
}
public void mouseMoved(MouseEvent e){
//获取坐标
x = e.getX();
y = e.getY();
//将坐标赋给我的飞机对象
myplane.setX(x);
myplane.setY(y);
}
public void run() {
while(true) {
if(flag) {
//引入次画布,先在次画布上消除移动的痕迹,然后把图形按照数组队列逐一画出来
BufferedImage bi = new BufferedImage(pf.getWidth(), pf.getHeight(), BufferedImage.TYPE_4BYTE_ABGR);
//获取次画布上的画笔
ig = bi.getGraphics();
background = new ImageIcon(this.getClass().getResource("background.jpg")).getImage();;
ig.drawImage(background, 0, 0, pf.getWidth(), pf.getHeight(),null);
for(int i = 0; i < list.size(); i++) {
if(x < pf.getWidth() && y < pf.getHeight()) {
plane = list.get(i);
//用次画布上的画笔来画
plane.draw(ig);
plane.move(list);
plane.judge(pf);
plane.crash();
}else {
list.remove(plane);
}
}
//如果画笔为空,则获取窗体上的画笔
if(g == null) {
g = (Graphics2D) pf.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
//将次画布上的图形画到窗体上
g.drawImage(bi,0,0, null);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
import java.awt.Image;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ImageIcon;
public class PlaneThread extends Thread{
private int size;
private PlaneFrame pf;
private ArrayList list;
private int x,y,width,height;
private Image imageplane;
private String type;
public PlaneThread(PlaneFrame pf,ArrayList list,int size,int width, int height) {
this.pf = pf;
this.list = list;
this.size = size;
this.width = width;
this.height = height;
this.imageplane = new ImageIcon(this.getClass().getResource("plane.png")).getImage();
}
public void run() {
//自动产生敌机
for(int i = 0; i < size; i++) {
Random rand = new Random();
x = 80 + rand.nextInt(pf.getWidth() - 100);
y = 20;
Plane plane = new Plane(x-80/2,y-80/2,80,80,2,list,pf,imageplane,"e_p");
list.add(plane);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.awt.Image;
import java.util.ArrayList;
import javax.swing.ImageIcon;
public class MyBullet extends Thread{
private PlaneFrame pf;
private ArrayList list;
private int x,y,width,height;
private Image imagemybullet;
private String type;
private Plane myplane;
public MyBullet(PlaneFrame pf,ArrayList list,int x,int y,int width, int height,String type,Plane myplane) {
this.pf = pf;
this.list = list;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.type = type;
this.myplane = myplane;
this.imagemybullet = new ImageIcon(this.getClass().getResource("mybullet.png")).getImage();
}
public void run() {
//自动产生子弹
while(true) {
//通过我的飞机对象来获取子弹发射的坐标
x = myplane.getX();
y = myplane.getY();
Plane mybullet = new Plane(x,y,20,20,-6,list,pf,imagemybullet,"m_b");
list.add(mybullet);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.awt.Graphics;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.ImageIcon;
public class Plane {
private int x,y,width,height;
private Image imageplane,imagebullet;
private int size;
private ArrayList list;
private PlaneFrame pf;
private int addx;
private int speed;
private String type;
private PlaneListener pr;
//构造方法
//获取图片小球的数据
public Plane(int x, int y, int width, int height,int speed,
ArrayList list,PlaneFrame pf,Image imageplane,String type) {
super();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.speed = speed;
this.list = list;
this.pf = pf;
this.imageplane = imageplane;
this.type = type;
this.imagebullet = new ImageIcon(this.getClass().getResource("bullet.png")).getImage();
//随机数的运用
Random rand = new Random();
addx = -10 + rand.nextInt(20);
}
//画图
public void draw(Graphics g) {
g.drawImage(imageplane, x-width/2, y-height/2, width, height,null); //画图
// g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
// RenderingHints.VALUE_ANTIALIAS_ON);
}
//移动
public void move(ArrayList list) {
//x+=addx;敌机的运动可以左右随机
// x+=addx;
y+=speed;
for(int i = 0;i < list.size();i++) {
if(type.equals("e_p") && y%50 == 0) {
Plane bullet = new Plane(x,y,20,20,6,list,pf,imagebullet,"e_b");
list.add(bullet);
}break;
}
}
//判断是否到达边界
//敌机左右运动到达边界后反弹
public void judge(PlaneFrame pf) {
if(x-40 <= 0) {
addx = -addx;
}else if(x+40 >= pf.getWidth()) {
addx = -addx;
}
}
//碰撞
public void crash() {
for(int j = 0;j < list.size();j++){
Plane lt = list.get(j);
if(lt.getType().equals("m_p")) {
for(int i = 0;i < list.size();i++) {
Plane temp = list.get(i);
if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {
double xx = lt.getX() - temp.getX();
double yy = lt.getY() - temp.getY();
double ww = lt.getWidth() + temp.getWidth();
if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {
list.remove(lt);
list.remove(temp);
//给静态属性赋值
PlaneListener.flag=false;
}
}
}
}
if(lt.getType().equals("m_b")) {
for(int i = 0;i < list.size();i++) {
Plane temp = list.get(i);
if(temp.getType().equals("e_p") || temp.getType().equals("e_b")) {
double xx = lt.getX() - temp.getX();
double yy = lt.getY() - temp.getY();
double ww = lt.getWidth() + temp.getWidth();
if(Math.sqrt(xx*xx + yy*yy) <= ww/2) {
list.remove(temp);
list.remove(lt);
}
}
}
}
}
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}