先上效果圖:

視頻封面

00:32飛機大戰

首先我們定義一個GameFrame類來顯示一個窗口:

public class GameFrame extends JFrame{
public void showGameFram(){
JFrame f = new JFrame();
f.setTitle("飛機大戰");
f.setSize(600, 1000);
f.setLayout(null);
f.setResizable(false);
f.setLocationRelativeTo(null);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}

然後我們來製作用來顯示飛機的類BasicPlain:

public class BasicPlane {

int locx;
int locy;
int speedx = 15;
int speedy = 15;
int ideax;
int ideay;
int type = 1; //敵機的種類區分標識符
private boolean ifenter = false; //判斷敵機是否進入了理想範圍

public BasicPlane() {
// TODO Auto-generated constructor stub
}
public BasicPlane(int locx,int locy,int ideax,int ideay,int type){
this.locx = locx;
this.locy = locy;
this.ideax = ideax;
this.ideay = ideax;
this.type = type;
}

Image img = chooseimage(type);
//根據選擇的敵機類型進行圖案繪製
public Image chooseimage( int type){
ImageIcon imageicon;
Image imag = null;
switch (type) {
case 1:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\1-1.png");
imag = imageicon.getImage();
return imag;
case 2:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\1-2.png");
imag = imageicon.getImage();
return imag;
case 3:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\1-5-1.png");
imag = imageicon.getImage();
return imag;
case 4:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\1-3--2.png");
imag = imageicon.getImage();
return imag;
case 5:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\1-3--1.png");
imag = imageicon.getImage();
return imag;
case 6:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\1-6.png");
imag = imageicon.getImage();
return imag;
default:
break;
}
return imag;
}
public void draw(Graphics g){
g.drawImage(chooseimage(type), locx, locy, null);
}
public void move(){
// System.out.println("x y speedx speedy"+locx+" "+locy+" "+speedx+" "+speedy);
//如果還沒有進入預定區域
if(!ifenter){
switch (type) {
case 1:
locx = locx+speedx;
locy = locy+speedy;
if(locx == ideax||locy == ideay){
ifenter = true;
}
break;
case 2:
locx = locx-speedx;
locy = locy+speedy;
if(locx == ideax||locy == ideay){
ifenter = true;
}
break;
case 3:
locx = locx +0;
locy = locy+speedy;
if(locy == 150){
ifenter = true;
}
break;
case 4 :
locx = locx-speedx;
locy = locy+0;
break;
case 5:
locx = locx+speedx;
locy = locy+0;
break;
case 6:
break;
default:
break;
}
}
//進入預定區域後,進行設想的飛行模式
if(ifenter){
switch (type) {
case 1:
move1();
break;
case 2:
move2();
break;
case 3:
move3();
break;

default:
break;
}
}
}

public void fire(ArrayList<Boom> booms){
int x = (int)(img.getHeight(null)/2);
int y = (int)(img.getHeight(null)/2);
Boom boom = new Boom(type,locx+x,locy+y);
booms.add(boom);
}

public boolean isout(){
boolean result = false;

if(locx>600||locy>1000||locx+this.img.getWidth(null)<0||locy+this.img.getHeight(null)<0){
result = true;
}
return result;
}

private void move1(){

locx = locx + 12;
locy = locy + 6;

}
private void move2(){
locx = locx-2;
locy = locy+5;
}
private void move3() {
locx = locx-speedx;
locy = locy;
if(locx<15||locx+img.getWidth(null)+15>600){
speedx = -speedx;
}
}
}

這裡我們給飛機的類定義了這樣幾個方法:

draw方法——用於繪製自身的圖案,具體的圖案根據chooseimage進行選擇

move方法——每次調用實現飛機位置的移動,這裡我們可以考慮給每個飛機的飛行設置一個預定要到達的位置,到達後就產生其他的一系列動作,就是裡面的ifenter參數。

fire方法——每次調用,產生一個Boom類(開火!)

isout方法——判斷飛機是否飛出邊框,如果飛出,就返回一個True.

這裡我們為了使飛機的圖案更加的多樣,我們在move和chooseimage方法處都使用了switch的選擇,開始時也可以選擇直接指定為一類。這樣以後,敵機類就可以通過直接實例化這個BasicPlain類來實現了。

由於我們控制的飛機(以下稱為英雄機HeroPlain)和普通的飛機有很大的不同,我們在這兒就新定義一個HeroPlain類來處置:

public class HeroPlane {

int locx = 240;
int locy = 850;
int HP = 100;
int TP = 100;
int type = 0;
ImageIcon imageicon;
Image image
public Image chooseimage(int type){
ImageIcon imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\2-1.png");
Image image = imageicon.getImage();
retuen image;
}

int speedx = 5;
int speedy = 5;

public void draw(Graphics g){
Image img = chooseimage(type);
g.drawImage(img, locx, locy, null);
}
public void move(){
//使用KeyListener進行操作
}
public void fire(){
//使用KeyListener進行發射彈丸
}
public boolean hit(ArrayList<BasicPlane> bp){
boolean result = false;
return result;
}
}

HeroPlain中,我們給他設置了血量(HP)和能量值(TP),也有draw和move的方法,由於我們是要用鍵盤來控制飛機的移動,那我們在這兒就不用把移動的方法展開來寫,只需要知道,他是有這樣一個方法就好。

然後對於發射的炮彈,我們也把他們作為對象來處理,新建一個Boom的類

public class Boom {

int locx;
int locy;

int type; //炮彈的種類的標識符

public Boom(int type,int locx,int locy) {
// TODO Auto-generated constructor stub
this.type = type;
this.locx = locx;
this.locy = locy;
}
Image imag = chooseBoom(type);
//根據選擇的類型選擇彈丸的圖案
private Image chooseBoom(int type){
ImageIcon imageicon;
Image imag = null;
switch (type) {
case 0: // 英雄機彈丸
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\3-3-1.png");
imag = imageicon.getImage();
return imag;
case 1:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\3-1-1.png");
imag = imageicon.getImage();
return imag;
case 2:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\3-1-1.png");
imag = imageicon.getImage();
return imag;
case 3:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\3-2-1.png");
imag = imageicon.getImage();
return imag;
case 4:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\3-1--1.png");
imag = imageicon.getImage();
return imag;
case 5:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\3-1--1.png");
imag = imageicon.getImage();
return imag;
default:
break;
}
return imag;
}
public void draw(Graphics g){
g.drawImage(chooseBoom(type), locx, locy, null);

}
public void move(){
switch(type){
case 0: //英雄機的導彈發射方法
locy = locy-20;
break;
case 1: //第一類敵機的導彈發射方式
locy = locy+15;
break;
case 2: //第二類敵機的導彈發射方法
locy = locy+15;
break;
case 3:
locy = locy +22;
break;
case 4:
locy = locy +20;
break;
case 5:
locy = locy +20;
break;

default:
break;

}

}
//判斷是否擊中敵機
public boolean hit(ArrayList<BasicPlane> bp){
boolean result = false;
//對打中敵軍的結果進行判斷
for (int i = 0; i < bp.size(); i++) {
if(bp.get(i).locx<locx&&bp.get(i).locx+bp.get(i).img.getWidth(null)>locx+imag.getWidth(null)&&
bp.get(i).locy<locy&&bp.get(i).locy+bp.get(i).img.getHeight(null)>locy ){
bp.remove(i);
result = true;
break;
}
}
return result;
}
//判斷是否擊中英雄機
public boolean hithero(HeroPlane hp){
boolean result = false;
//對打中敵軍的結果進行判斷

if(hp.locx<locx&&hp.locx+hp.image.getWidth(null)>locx+imag.getWidth(null)&&
hp.locy<locy&&hp.locy+hp.image.getHeight(null)>locy ){
result = true;
}

return result;
}
public boolean hitboom(ArrayList<Boom> booms){
boolean result = false;
for(int i = 0;i<booms.size();i++){
int boomx = booms.get(i).locx;
int boomx2 = booms.get(i).locx+booms.get(i).imag.getWidth(null);
int heroboomx = locx;
int heroboomx2 = locx+imag.getWidth(null);
if((boomx<=heroboomx2&&boomx>=heroboomx)||
(boomx2>=heroboomx&&boomx2<=heroboomx2)||
(heroboomx>=boomx&&heroboomx<=boomx2+15)||
(heroboomx2>=boomx&&heroboomx2<=boomx2)){
if(booms.get(i).imag.getHeight(null)+booms.get(i).locy>locy){
booms.remove(i);
System.out.println(">>>>>>>>>>");
result = true;
break;
}

}
}
return result;
}

//判斷是否移出邊界
public boolean isout(){
boolean result = false;
//對是否移動出窗口進行判斷
if(locx>600||locy>1000||locx+imag.getWidth(null)<0||locy+imag.getHeight(null)<0){
result = true;
}
return result;
}

}

這裡對於炮彈的處理和對飛機的處理是類似的,開始時,我們也可以不適用這麼多的圖案選擇

然後我們來寫一下控制飛機移動的KeyListener:

c class Keylistener extends MouseAdapter implements KeyListener{

HeroPlane hp = new HeroPlane();
private boolean L;
private boolean R;
private boolean U;
private boolean D;

@Override
public void keyPressed(KeyEvent e) {
// TODO 自動生成的方法存根

switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
L=true;
break;
case KeyEvent.VK_RIGHT:
R=true;
break;
case KeyEvent.VK_UP:
U=true;
break;
case KeyEvent.VK_DOWN:
D=true;
break;
case KeyEvent.VK_SPACE:
if(hp.TP>=0){
Boom boom = new Boom(0,hp.locx+55,hp.locy);
herobooms.add(boom);
Boom boom2 = new Boom(0, hp.locx+15,hp.locy);
herobooms.add(boom2);
hp.TP = hp.TP - 10;
}
break;
default:
break;
}

if(D){
hp.locy = hp.locy+hp.speedy;
if(hp.locy+hp.image.getHeight(null)>960){
hp.locy = 960-hp.image.getHeight(null);
}
}else if(U){
hp.locy = hp.locy-hp.speedy;
if(hp.locy<40){
hp.locy = 40;
}
}
if(L){
hp.locx = hp.locx-hp.speedx;
if(hp.locx<2){
hp.locx = 2;
}
}else if(R){
hp.locx = hp.locx+hp.speedx;
if(hp.locx+hp.image.getWidth(null)>598){
hp.locx = 598-hp.image.getWidth(null);

}
}

}

@Override
public void keyReleased(KeyEvent e) {
// TODO 自動生成的方法存根
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT:
L=false;
break;
case KeyEvent.VK_RIGHT:
R=false;
break;
case KeyEvent.VK_UP:
U=false;
break;
case KeyEvent.VK_DOWN:
D=false;
break;
}

}

@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub

}

}

這裡,我們使用了記錄是否按下某個鍵的判斷標誌符:L,R,U,D,然後根據標識符的狀態來進行具體的移動變化,從而就可以實現同時響應兩個鍵的效果。

然後進入線程的部分,我們將多個任務分開來做,放在不同的線程中間來進行

第一個線程:

用來接收已經存在的飛機(包括英雄機)和炮彈,每隔一段時間,畫出此時的飛機和炮彈所處的位置,對於飛出或者被擊中的飛機和炮彈,及時的進行清理。

public class MyRunnable implements Runnable{

HeroPlane hp;
Graphics g;
ArrayList<Boom> booms;
ArrayList<BasicPlane> emenies;
ArrayList<Boom> herobooms;
ArrayList<Explore> ex = new ArrayList<>();

ScoreShow sshow;
//記錄得分的數值
int score = 0;
public void run(){
ImageIcon imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\0-1-1.jpg");
Image img = imageicon.getImage();
int count = 0;
//無限循環來處理畫圖和移動,移除消失的對象
while(true){

g.drawImage(img, 0, 0, null);
hp.draw(g);
g.setColor(Color.RED);
g.fillRect(10, 30, hp.HP,15);
g.setColor(Color.BLUE);
g.fillRect(10, 50, hp.TP, 15);
sshow.show(score);

count++;
if(count%100 == 0){
if(hp.TP<100){
hp.TP = hp.TP+5;
}
}else if(count == 1001){
count = 0;
}
for(int i = 0;i<emenies.size();i++){
emenies.get(i).move();
emenies.get(i).draw(g);
if(emenies.get(i).isout()){
emenies.remove(i);
}

}
for(int i = 0;i<booms.size();i++){
booms.get(i).move();
booms.get(i).draw(g);
if (booms.get(i).hithero(hp)) {
hp.HP = hp.HP-5;
if(hp.HP<=0){
System.out.println("遊戲結束");
}
booms.remove(i);
}
if(booms.get(i).isout()){
booms.remove(i);
}

}

for(int i = 0;i<herobooms.size();i++){

herobooms.get(i).move();
herobooms.get(i).draw(g);
if(herobooms.get(i).hit(emenies)){

Explore e = new Explore(herobooms.get(i).locx, herobooms.get(i).locy,
herobooms.get(i).imag.getHeight(null),herobooms.get(i).imag.getWidth(null));
ex.add(e);

score = score+100;

herobooms.remove(i);

if(hp.TP<100){
hp.TP = hp.TP+15;
if(hp.TP>100){
hp.TP = 100;
}
}

}else if(herobooms.get(i).hitboom(booms)){
herobooms.remove(i);
score =score +50;
}else if(herobooms.get(i).isout()){
herobooms.remove(i);
}
}

try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

}

第二個線程:產生敵機,並添加到隊列中去:

public class Myrunnable3 implements Runnable{
//該線程用於產生敵機
ArrayList<BasicPlane> emenies;
Graphics g;
@Override
public void run() {
// TODO Auto-generated method stub
int i = 0;

while(true){
if(i<10){
BasicPlane bp = new BasicPlane(20,20,370,350,1);
emenies.add(bp);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else if(i<20){
BasicPlane bp = new BasicPlane(370,20,20,350,2);
emenies.add(bp);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else if(i<30){
BasicPlane bp = new BasicPlane(240,0,0,110,3);
emenies.add(bp);
try {
Thread.sleep(700);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else if(i<50){

BasicPlane bp1 = new BasicPlane(599,300,0,0,4);
bp1.speedx = 10;
emenies.add(bp1);
BasicPlane bp2 = new BasicPlane(1,300,0,0,5);
bp2.speedx = 10;
emenies.add(bp2);
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
i = (int) Math.random()*40;
}
i++;
}

}

}

第三個線程:從敵機的隊列中取出敵機,發炮,將炮彈添加到炮彈的隊列中去:

public class MyRunnable2 implements Runnable {
ArrayList<BasicPlane> emenies;
ArrayList<Boom> booms;
public void run() {
while(true){

for(int i = 0;i<emenies.size();i++){
emenies.get(i).fire(booms);

}

try {
Thread.sleep(600);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}

為了添加我們打中後的爆炸效果我們先是建立一個新的對象叫做Explore:

public class Explore {

int bpx;
int bpy;
int bpwidth;
int bpheight;
int choose = 10;
public Explore( int bpx,int bpy,int bpheight,int bpwight) {
// TODO Auto-generated constructor stub
this.bpx = bpx;
this.bpy = bpy;
this.bpwidth = bpwight;
this.bpheight = bpheight;
}

Image image = chooseimage(choose).getImage();

public ImageIcon chooseimage(int choose){
ImageIcon imageicon = null;
switch (choose) {
case 10:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-1.png");
break;
case 9:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-2.png");
break;
case 8:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-3.png");
break;
case 7:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-4.png");
break;
case 6:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-5.png");
break;
case 5:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-5.png");
break;
case 4:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-4.png");
break;
case 3:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-3.png");
break;
case 2:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-2.png");
break;
case 1:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\5-1-1.png");
break;
default:
break;
}

return imageicon;
}
public int returnx(){
int x = 0;
Image image = this.image;
x = (int)(bpx+bpwidth/2-image.getWidth(null)/2);
return x;
}
public int returny(){
int y = 0;
Image image = this.image;
y = (int)(bpy+bpheight/2-image.getHeight(null)/2);
return y;
}
}

其中的returnx和returny兩個函數是為了確定這個圖片所處的最佳位置而設定的。

然後定義一個新的數組來 運行爆炸時的效果:

邏輯很簡單,遍歷列表,如果不為空,就去取一張圖片,畫在我們選擇好的最佳位置處,然後就循環完這一遍後就將每一個Explore類內部的choose減一,再次繪製,直到choose為零,將這個對象直接取消掉:

public class Myrunnable1 implements Runnable{

ArrayList<Explore> ex;
Graphics g;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){

for (int i = 0; i < ex.size(); i++) {
if(ex.get(i)!= null){

g.drawImage(ex.get(i).image, ex.get(i).returnx(), ex.get(i).returny(), null);
ex.get(i).choose--;
if(ex.get(i).choose==0){
ex.remove(i);
}
break;
}

}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

}

}

對於分數的顯示,為了能夠更清晰,我在這而定義了一個新的ScoreShow類,這個類接收一個數字,然後從右向左,依次畫出每一位數字:

public class ScoreShow {

int score;

Graphics g;
public ScoreShow(Graphics g) {
// TODO Auto-generated constructor stub
this.g= g;
}

public void show(int score){
int x = 590;
int y = 40;
for(int i = 0;;i++){
int number = score%10;
score = score/10;
Image imag = chooseimage(number);
x = x- imag.getWidth(null)-10;
g.drawImage(imag, x, y, null);
if(score == 0){
break;
}

}

}
public Image chooseimage(int number){
ImageIcon imageicon = null;
Image image = null;
switch (number) {
case 0:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-10.png");
image = imageicon.getImage();
break;
case 1:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-1.png");
image = imageicon.getImage();
break;
case 2:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-2.png");
image = imageicon.getImage();
break;
case 3:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-3.png");
image = imageicon.getImage();
break;
case 4:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-4.png");
image = imageicon.getImage();
break;
case 5:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-5.png");
image = imageicon.getImage();
break;
case 6:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-6.png");
image = imageicon.getImage();
break;
case 7:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-7.png");
image = imageicon.getImage();
break;
case 8:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-8.png");
image = imageicon.getImage();
break;
case 9:
imageicon = new ImageIcon("D:\eclipse\workspace\basicStudy\src\AirBattle190224\6-9.png");
image = imageicon.getImage();
break;

default:
break;
}

return image;
}
}

剩下的就是將各個類中的隊列進行統一,然後將各個線程都運行起來就可以了。我們還可以將線程的啟動方法加入到一個啟動窗口中去,這就可以實現視頻中的樣子了。

修改後的代碼見網盤:

鏈接:pan.baidu.com/s/13DZGTR

提取碼:l98f

運行之前,由於我使用的是絕對路徑請將圖片的路徑改為你文件下的位置,否則肯定是要有問題的。

我是進林,希望與你共同進步,歡迎大家關注我~


推薦閱讀:
相關文章