MyFighter Myfig;
ArrayList<FlyObject> enemyFighters = new ArrayList<>();
ArrayList<FlyObject> enemyBullets = new ArrayList<>();
ArrayList<FlyObject> MyBullets = new ArrayList<>();
boolean gift = true;
BufferedImage img = new BufferedImage (800, 600, BufferedImage.TYPE_INT_ARGB);
首先声明Myfig对象,表示我的飞机。接着利用基于数组上的链表ArrayList,泛型数据类型为FlyObject,分别声明了敌机,敌方子弹和我的子弹。接着声明了布尔类型变量gift为true,接着调用类BufferedImage,其生成的图片在内存中有一个图片缓冲区,声明对象img的宽高800,600,ARGB表示带透明色的对象。
{
Myfig = new MyFighter();
BattleThread battleThread = new BattleThread(this);
new Thread(battleThread).start();
AutoThread autoThread = new AutoThread(this);
new Thread(autoThread).start();
EnemyBulletThread enemyBulletThread = new EnemyBulletThread(this);
new Thread(enemyBulletThread).start();
MybulletThread mybulletThread = new MybulletThread(this);
new Thread(mybulletThread).start();
CollisionThread collisionThread = new CollisionThread(this);//这一段代码段什么结构。this充当什么成分?
new Thread(collisionThread).start();
}
再次声明Myfig对象,我的飞机。然后先后声明并调用了几个线程,分别是battleThread,autoThread,enemyBulletThread,mybulletThread,collisionThread,以便后续运行。
public void init(){
setTitle ("飞机大战");
setSize (800, 600);
setDefaultCloseOperation (EXIT_ON_CLOSE);
setVisible (true);
Graphics g = this.getGraphics ();
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
Myfig.setX(e.getX());
Myfig.setY(e.getY());
}
});
}
接着是init方法。设定标题“飞机大战”,设定大小宽高。设置默认的关闭操作,参数“在关闭动作时退出”。设置窗体可见,增加鼠标监听器,写图片类对象g,增加鼠标动作监听器,重写鼠标动作类,Myfig.setX(e.getX())实现我的飞机随着鼠标移动的坐标移动而移动。
public static void main(String[] args){ //java虚拟机首先运行的就是main函数
GameUI g = new GameUI();
g.init();
AudioPlay1.play();
}
main方法,游戏界面对象g来启动界面,同时调用AudioPlay1来播放。
class AudioPlay1{
static File file = new File ("resource/background.wav");
public static void play(){
URI uri = file.toURI ();
System.out.println (uri);
// AudioClip 只能播放 wav 格式的music
AudioClip audioClip = null;
try {
audioClip = Applet.newAudioClip (uri.toURL ());
} catch (MalformedURLException e) {
throw new RuntimeException (e);
}
audioClip.play ();// 独立线程播放
//循环播放
audioClip.loop ();
}
}
类AudioPlay1,声明静态类文件resource,静态方法play,获取图片文件的URI地址,Applet实现独立线程播放和循环播放。
public class FlyObject {
private int x;
private int y;
private int speedx;
private int speedy;
private int height;
private int width;
private Image img;
private int state = 1;
private int ph;
String path; // ?
public int getPh() { // ?
return ph;//血量
}
public void setPh(int ph){
this.ph = ph;
}
// state = 1 存活
// state = 2 碰撞
// state = 3 死亡
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getSpeedx() {
return speedx;
}
public void setSpeedx(int speedx) {
this.speedx = speedx;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getSpeedy() {
return speedy;
}
public void setSpeedy(int speedy) {
this.speedy = speedy;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public void setImg(String path){
this.path = path;
this.img = new ImageIcon(path).getImage();
}
public String getImg(){
return path;
}
public void show(Graphics g){
g.drawImage (img, x-width/2, y-height/2, width, height, null);
}
public void move(){
x -= speedx;
y += speedy;
}
}
声明飞行物类,定义私有属性横坐标纵坐标高宽,图片,飞行状态,飞行器的血量,并声明一些方法来获取并设置血量状态宽度高度飞行速度,和坐标,并获得图片的信息,声明展示和移动方法。
class MyFighter extends FlyObject{
// 初始化飞机的信息
MyFighter(){
int x = 350;
int y = 350;
int width=65;
int height=85;
int speedx=2;
int speedy=2;
setX (x);
setY (y);
setWidth (width);
setHeight (height);
setSpeedx (speedx);
setSpeedy(speedy);
setImg("resource/myfighter.png");
setPh(500);
}
}
声明类MyFighter继承FlyObject对象,用构造函数初始化飞机的信息。
class EnemyFighter extends FlyObject{
public boolean getGift() {
return gift;
}
public void setGift(boolean gift) {
this.gift = gift;
}
boolean gift;
EnemyFighter(int x,int y,int speedy){
int width=50;
int height=60;
int speedx=20; // 设置敌机横向飞行速度
setX (x);
setY (y);
setWidth (width);
setHeight (height);
setSpeedx (speedx);
setSpeedy(speedy);
setImg ("resource/Elmfighter.png");
setPh(5);
setGift(false);
}
}
声明类EnemyFighter继承飞行物类,并声明布尔类型方法,获取并设置布尔值gift。接着用构造方法来设置了敌机的相关属性,包括血量为5等。
class Enemybullet extends FlyObject{
Enemybullet(int x, int y,int speedy){
int width=13;
int height=11;
int speedx=60; // 设置子弹横向飞行速度
setX (x);
setY (y);
setWidth (width);
setHeight (height);
setSpeedx (speedx);
setSpeedy(speedy);
setImg ("resource/bullet.png");
}
}
class Mybullet extends FlyObject{
// private int x;
// private int y;
// private int speedx;
// private int speedy;
// private int height;
// private int width;
// private Image img = null;
Mybullet(int x, int y){
int width=13;
int height=11;
int speedx=-60; // 设置子弹横向飞行速度
int speedy = 0;
setX (x);
setY (y);
setWidth (width);
setHeight (height);
setSpeedx (speedx);
setSpeedy(speedy);
setImg ("resource/Mybullet.png");
}
// @Override
// public void move(){
// x += speedx;
// y += speedy;
// }
}
敌方子弹,我的子弹的类的声明,同样是继承了飞行物的类,并利用构造函数设定了相关属性参数。
public class CollisionThread implements Runnable{
MyFighter Myfighter;
ArrayList<FlyObject> enemyFighters;
ArrayList<FlyObject> enemyBullets;
ArrayList<FlyObject> MyBullets;
GameUI gui;
BufferedImage img;
CollisionThread(GameUI gui){
this.gui = gui;
this.Myfighter = gui.Myfig;
this.enemyBullets = gui.enemyBullets;
this.enemyFighters = gui.enemyFighters;
this.MyBullets = gui.MyBullets;
this.img = gui.img;
}
冲撞线程继承Runnable接口,初始化对象:几个飞行物。接着仍然是用构造函数this给属性赋值。
if(MyBullets != null & enemyBullets != null){
// 判断子弹之间有没有碰撞
for (int i=0;i<MyBullets.size();i++){
for(int j=0;j<enemyBullets.size();j++){
Mybullet mybullet = (Mybullet) MyBullets.get(i);
Enemybullet enemybullet = (Enemybullet) enemyBullets.get(j);
if(is_collision(mybullet.getX(),mybullet.getY(),enemybullet.getX(),enemybullet.getY(),
mybullet.getHeight(),mybullet.getWidth(),enemybullet.getHeight(),enemybullet.getWidth())){
mybullet.setState(3);
enemybullet.setState(3);
}
if(is_Out(mybullet.getX(),mybullet.getY())){
mybullet.setState(3);
}
if(is_Out(enemybullet.getX(),enemybullet.getY())){
enemybullet.setState(3);
}
}
}
}
判断我方飞机的子弹和敌方飞机的子弹有没有碰撞,如果碰撞了,则二者都执行设置为第三种状态,即setState(3)。
if(enemyBullets != null & Myfighter != null){
// 判断敌机的子弹和飞机有没有碰撞
for(int j=0;j<enemyBullets.size();j++){
Enemybullet enemybullet = (Enemybullet) enemyBullets.get(j);
if(is_collision(Myfighter.getX(),Myfighter.getY(),enemybullet.getX(),enemybullet.getY(),
Myfighter.getHeight(),Myfighter.getWidth(),enemybullet.getHeight(),enemybullet.getWidth())){
Myfighter.setPh(Myfighter.getPh()-3); // 飞机减少的ph值
enemybullet.setState(3);
}
if(is_Out(enemybullet.getX(),enemybullet.getY())){
enemybullet.setState(3);
}
}
}
判断敌机的子弹和我方的飞机有没有碰撞,同时调用is_collision方法,如果碰撞了,则二者都执行设置为第三种状态,即setState(3)。
if(MyBullets != null & enemyFighters != null){
// 判断敌机和我们飞机的子弹有没有碰撞
for (int i=0;i<MyBullets.size();i++){
for(int j=0;j<enemyFighters.size();j++){
Mybullet mybullet = (Mybullet) MyBullets.get(i);
EnemyFighter enemyFighter = (EnemyFighter) enemyFighters.get(j);
if(is_collision(mybullet.getX(),mybullet.getY(),enemyFighter.getX(),enemyFighter.getY(),
mybullet.getHeight(),mybullet.getWidth(),enemyFighter.getHeight(),enemyFighter.getWidth())){
mybullet.setState(3);
enemyFighter.setPh(enemyFighter.getPh()-3);
if(enemyFighter.getPh()<0){
enemyFighter.setImg("resource/bomb.png");
enemyFighter.show(img.getGraphics());
enemyFighter.setState(3);
}
}
if(is_Out(enemyFighter.getX(),enemyFighter.getY())){
enemyFighter.setState(3);
}
}
}
}
判断我方子弹和敌方飞机有没有发生碰撞,同时调用is_collision方法,如果碰撞了,则二者都执行设置为第三种状态,即setState(3)。若果发生了collision,则敌方飞机就设置血量为原有血量-3,如果敌方飞机的血量<0,此时敌方飞机setimag为bomb.png即设置爆炸图片。如果敌方飞机的横纵坐标离开了界面,那么等同于消失,同时设置状态3。
if(enemyFighters != null & Myfighter != null){
// 判断敌机和我们的飞机有没有碰撞
for(int j=0;j<enemyFighters.size();j++){
EnemyFighter enemyFighter = (EnemyFighter) enemyFighters.get(j);
if(is_collision(Myfighter.getX(),Myfighter.getY(),enemyFighter.getX(),enemyFighter.getY(),
Myfighter.getHeight(),Myfighter.getWidth(),enemyFighter.getHeight(),enemyFighter.getWidth())){
Myfighter.setPh(Myfighter.getPh()-3);
enemyFighter.setState(2);
enemyFighter.setImg("resource/boom.png");
System.out.println("Myfighter剩余的血量是:"+Myfighter.getPh());
AudioPlay.play();
//enemyFighter.setState(3);
}
if(is_Out(enemyFighter.getX(),enemyFighter.getY())){
enemyFighter.setState(3);
}
}
}
判断敌方飞机和我放飞机有没有发生碰撞,如果发生了碰撞,则播放音乐。同时输出我方飞机的剩余血量。
long end = System.currentTimeMillis();;
if( end - start > 6000){ // 1分钟进行一次删除
delet();
start = end;
System.out.println("!!!!!!!!!!!!!!!!!!delete_not_useful_ones!!!!!!!!!!!!!!!!!!");
}
一分钟进行一次删除操作。
public boolean is_collision(int X1, int Y1, int X2, int Y2, int height1, int width1, int height2, int width2){
if(Math.abs(X1-X2)<= (width1/2 + width2/2) & Math.abs(Y1-Y2)<= (height1/2 + height2/2)){
return true;
}
return false;
}
这时定义布尔型方法碰撞操作。如果两个飞行物横坐标差的绝对值小于两者各自宽度二分之一的和,或者纵坐标的差的绝对值小于两者各自高度的二分之一的和,则返回true表示已碰撞,如果不满足条件则返回值false。
public boolean is_Out(int x, int y){
if(x < 0 || x > 800 || y < 0 || y > 600){
return true;
}
return false;
}
接着定义布尔型方法出局操作。如果飞行物的横坐标小于0或者大于800,或者纵坐标小于0或者大于600的话,则该布尔型方法返回值true表示已经出局,如果没有,则返回值false。
class AudioPlay{
static File file = new File ("resource/bomb.wav");
public static void play(){
URI uri = file.toURI ();
System.out.println (uri);
// AudioClip 只能播放 wav 格式的music
AudioClip audioClip = null;
try {
audioClip = Applet.newAudioClip (uri.toURL ());
} catch (MalformedURLException e) {
throw new RuntimeException (e);
}
audioClip.play ();// 独立线程播放
//循环播放
//audioClip.loop ();
}
}
静态方法play,统一资源标志符声明,用类Applet来创建播放音乐的对象audioClip,并且实现独立播放和循环播放。
public class BattleThread implements Runnable{
MyFighter Myfighter;
private GameUI gui;
Graphics g;
private Random random = new Random();
ArrayList<FlyObject> MyBullets;
ArrayList<FlyObject> enemyFighters;
ArrayList<FlyObject> enemyBullets;
BufferedImage img;
public BattleThread(GameUI gui){
this.gui = gui;
this.Myfighter = gui.Myfig;
this.g = gui.getGraphics();
this.MyBullets = gui.MyBullets;
this.enemyFighters = gui.enemyFighters;
this.enemyBullets = gui.enemyBullets;
this.img = gui.img;
}
战斗线程继承Runnable接口,然后构造方法BattleThread来为对象的属性赋值。
@Override
public void run(){
while(true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(g == null){
g = gui.getGraphics();
}
else{
Graphics2D g2d = (Graphics2D) img.getGraphics ();
// 设置背景图片
Image background = new ImageIcon("resource/planet.jpg").getImage();
g2d.drawImage(background,-150,-200,950,800,null);
//g2d.setColor (new Color (238, 238, 238));
//g2d.fillRect (0, 0, 800, 600);
Myfighter.show(g2d);
g2d.drawString(""+(Myfighter.getPh()),0,500);
for(int i = 0;i<MyBullets.size();i++){
if(MyBullets.get(i).getState()==1){
MyBullets.get(i).move();
MyBullets.get(i).show(g2d);
}
}
// let the enenmyfighters move
for(int i = 0;i<enemyFighters.size();i++){
if(enemyFighters.get(i).getState() != 3) {
enemyFighters.get(i).move();//链式表达式
// EnemyFighter fighter = enemyFighters.get(i);
// fighter.move();
enemyFighters.get(i).show(g2d);
if(enemyFighters.get(i).getState() == 2){
enemyFighters.get(i).setState(3);
}
}
}
// let the enenmyfighters move
for(int i = 0;i<enemyBullets.size();i++){
if(enemyBullets.get(i).getState() != 3){
enemyBullets.get(i).move();
enemyBullets.get(i).show(g2d);
}
}
g.drawImage (img, 0, 0, null);
}
}
}
}
线程休眠0.5秒,调用抽象图2D类,设置背景图片background,同时让我方飞机出现在背景中,输出字符串“我方飞机的血量”。接下来遍历全体子弹,并设置子弹的状态为1,并让他们得到并移动。接着遍历敌机的数量,如果状态为2,则设置敌机的状态为3,如果子弹的状态不为3,则表明子弹存活,此时展示g2d的图像。
class AutoThread implements Runnable{
private Random random = new Random();
boolean flag;
GameUI gui;
ArrayList<FlyObject> enemyFighters;
AutoThread(GameUI gui){
this.flag = true;
this.gui = gui;
this.enemyFighters = gui.enemyFighters;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(200); // 生成飞机的速度
} catch (InterruptedException e) {
e.printStackTrace();
}
// 随机生成敌机
int x = random.nextInt(10)+800;
int y = random.nextInt(600);
int speedy;
if(flag){
speedy = random.nextInt(10);
flag = false;
}
else{
speedy = -1*random.nextInt(10);
flag = true;
}
synchronized (gui){
EnemyFighter enemyFighter = new EnemyFighter(x,y,speedy);
enemyFighters.add(enemyFighter);
}
System.out.println("number of enemyFighters:"+enemyFighters.size());
}
}
}
接着是自动攻击线程继承了Runnable接口,私有变量随机数Random,敌机存储在一个可以动态修改的数组ArrayList里面,接着随机生成敌机,
int x = random.nextInt(10)+800; int y = random.nextInt(600);
即横纵坐标都是random的随机数,如果标识符flag为true,纵速度就为10附近的一个随机整数,并设置flag为false。否则flag为false的话,则设置纵速度为-1*random.nextInt(10),synchronizd关键字声明的方法同一时间只能被一个线程访问,线程执行同步代码块是分顺序的,保证了有序性。
class EnemyBulletThread implements Runnable{
ArrayList<FlyObject> enemyBullets;
ArrayList<FlyObject> enemyFighters;
GameUI gui;
EnemyBulletThread(GameUI gui){
this.gui = gui;
this.enemyFighters = gui.enemyFighters;
this.enemyBullets = gui.enemyBullets;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<enemyFighters.size();i++){
EnemyFighter enemyFighter = (EnemyFighter) enemyFighters.get(i);
synchronized (gui){//只能一个线程使用,参数可以是对象或者是基本数据类型
if(enemyFighter.getState() != 3){
enemyBullets.add(new Enemybullet(enemyFighter.getX(),enemyFighter.getY(),enemyFighter.getSpeedy()));
}
}
}
}
}
}
敌方子弹线程,依然是一个synchronized加锁,表明这一方法同时只能被一个线程访问,如果敌机的状态不为3,敌方子弹增加到新的子弹里面,敌机的x值y值。
class MybulletThread implements Runnable{
ArrayList<FlyObject> MyBullets;
MyFighter myFighter;
GameUI gui;
boolean gift;
MybulletThread(GameUI gui){
this.gui = gui;
this.MyBullets = gui.MyBullets;
this.myFighter = gui.Myfig;
this.gift = gui.gift;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (gui){
if(gift){
Mybullet mybullet=new Mybullet(myFighter.getX(),myFighter.getY());
MyBullets.add(mybullet);//
MyBullets.add(new Mybullet(myFighter.getX(),myFighter.getY()-30));
MyBullets.add(new Mybullet(myFighter.getX(),myFighter.getY()+30));
}
else{
MyBullets.add(new Mybullet(myFighter.getX(),myFighter.getY()));
}
}
}
}
}
接下来是我的子弹线程,依然是用构造函数this来给对象的属性赋值。启动run方法循环执行,创建我的子弹对象,并把它添加到子弹类中,我的子弹类增加新的子弹,子弹的坐标就是我的飞机的横坐标,纵坐标为我的飞机的纵坐标-30,和+30。