飞机大战小游戏
目录
项目介绍框架搭建代码实现总结分析
前言: 前一段时间写了一个仿QQ的聊天室,主要涉及Java通信有关的知识,今天写的这个飞机大战主要是复习之前写过的,这次我会加入一些其他的元素和对界面进行一些完善。
这篇博客我会写的比较通透,主要是自己用来复习和巩固知识点的,所以大家如果感兴趣的话,可以参考我的这篇博客!特别是正在学习Java的同学哦!!!
项目介绍
实现飞机大战设计的知识点还是比较多的,所以特别推荐编程小白先上手这种简单的游戏,这样既可以巩固简单的Java基础知识,又可以实现自己编写的小作品,展现自己的能力,何乐而不为呢?下面我就简单地介绍一下实现飞机大战要了解和使用的相关知识吧ƪ(˘⌣˘)ʃ
继承机制 一. 为什么要用继承? 简单说:首先要实现飞机大战,根据Java面向对象编程的特性,我们就必须明白整个游戏有哪几个对象?不难知道有:自己的战机、敌机、子弹(敌机和我机子弹)和背景。观察这几个对象可以知道,它们都有一些共同的属性,如战机和敌机的血量、它们的坐标和移动的方法等等。 由上所述,我们可以合理地采用接口或者抽象类来让子类继承,这样可以充分地复用代码,大大地减少了代码的冗余。让我们更快地完成飞机大战这个小游戏。 二.如何使用继承? 对于为什么要用继承上面我已经说明了,下面就详详细说一下接口和抽象类。我们都知道,但我们用extends去继承一个类的时候,子类就拥有了父类的属性和方法,自然就可以去调用。同时我们也可以覆写父类的方法(如果父类的方法比较不具体),这里就有一个名词:抽象类,该类本身就要声明为abstract,同时抽象的方法也要声明。如果我们就是想要一个十分抽象的类,该类中的方法全部是抽象的方法(只是定义了方法的名称,没有方法体的方法),目的是为了子类可以继承该类从而覆写其中的的所有方法,我们把这个类称为接口,我们用interface来定义接口,implement来实现接口。 接口和抽象类的比较: 从上面可知,按抽象的程度来比较,一般的类<抽象类<接口,下面简单说一下接口和抽象类用法的比较: 大家可以参考这篇博客:java面向对象编程技术详解监听事件 我们都知道,在飞机大战游戏中我们要用鼠标点击开始游戏和停止游戏的按钮,而且我们要通过键盘操控飞机的移动和开火。这里就要用到鼠标点击监听器和键盘监听器,有童鞋可能也会用鼠标移动监听器来控制飞机的运动。不管怎么用,这些监听都是接口,我们用implement实现其中的方法就可以啦!!!
框架搭建
综合前面的分析我们就可以搭建一个简单的框架:
Entity.java:接口,功能:定义子类共有的抽象的方法:移动、画图、设置和获得坐标。MyFeiji.java:自己的战机类,功能:实现接口中的方法,控制移动和检测是否是受到攻击。DiJi.java:敌机类,功能:实现接口中的方法,随机产生敌机并设置敌机相关的一些属性。MyFeiJiBullet.java:战机炮弹类,功能:实现接口中的方法,检测是否攻击到敌方。DiJiBullet.java:敌机炮弹类,功能:实现接口中的全部方法,同时要控制边界。BackGroundPhotosMusic.java:背景图片音乐类,功能:实现接口中的方法,画出移动的图片、一些界面的设置。MainUI.java:主界面类,功能:创建队列控制所有的对象的移动和画图,同时实现键盘监听器控制战机的移动和发射炮弹。RemoveThread.java:去除线程类,功能:创建线程来实现对越界子弹敌机的去除,控制飞机的碰撞。 上面就是大致的飞机大战的一个框架,对于这种封装性比较强的代码,我们接下来的编写是比较容易的,让我们来实现它吧!
代码实现
Entity.java:接口
public interface Entity {
public void move();
public void draw(Graphics g
);
public void setX(int startX
);
public void setY(int startY
);
public int
getX();
public int
getY();
}
MyFeiji.java:自己的战机类
public class MyFeiJi implements Entity {
boolean up
=false;
boolean down
=false;
boolean left
=false;
boolean right
=false;
private int x
,y
;
private int scores
;
private int hp
;
private Image image_myfeiji
;
public MyFeiJi(){
image_myfeiji
=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\战机.png").getImage();
}
public void move() {
if(x
<=40){
x
=40;
}
if(x
>=1790){
x
=1790;
}
if(y
<=100){
y
=100;
}
if(y
>=910){
y
=910;
}
if(up
){
y
-=3;
}
if(down
){
y
+=3;
}
if(left
){
x
-=4;
}
if(right
){
x
+=3;
}
}
public void draw(Graphics g
) {
g
.setFont(new Font("黑体",Font
.BOLD, 35));
g
.setColor(Color
.RED);
g
.drawString("我的血量:"+hp
,30,90);
g
.setFont(new Font("黑体",Font
.BOLD, 35));
g
.setColor(Color
.RED);
g
.drawString("我的得分:"+scores
,30,140);
g
.drawImage(image_myfeiji
, x
, y
, null);
}
public boolean
isDefeat(){
if(hp
<=0){
return true;
}else{
return false;
}
}
public void setX(int startX
) {
this.x
=startX
;
}
public void setY(int startY
) {
this.y
=startY
;
}
public int
getX() {
return x
;
}
public int
getY() {
return y
;
}
public boolean
isShootByDiJiBullet(DiJiBullet dijibullet
){
int x1
=this.x
;
int y1
=this.y
;
int width1
=this.image_myfeiji
.getWidth(null);
int height1
=this.image_myfeiji
.getHeight(null);
int x11
=x1
+width1
/2;
int y11
=y1
+height1
/2;
int x2
=dijibullet
.getX();
int y2
=dijibullet
.getY();
int width2
=dijibullet
.getDiJiBulletWidth();
int height2
=dijibullet
.getDiJiBulletHeight();
int x22
=x2
+width2
/2;
int y22
=y2
+height2
/2;
int distanceX
=Math
.abs(x22
-x11
);
int distanceY
=Math
.abs(y22
-y11
);
if(distanceX
<=30&&distanceY
<=25){
return true;
}else{
return false;
}
}
public boolean
isShootByDiJi(Diji diji
){
int x1
=this.x
;
int y1
=this.y
;
int width1
=this.image_myfeiji
.getWidth(null);
int height1
=this.image_myfeiji
.getHeight(null);
int x11
=x1
+width1
/2;
int y11
=y1
+height1
/2;
int x2
=diji
.getX();
int y2
=diji
.getY();
int width2
=diji
.getDiJiWidth();
int height2
=diji
.getDiJiHeight();
int x22
=x2
+width2
/2;
int y22
=y2
+height2
/2;
int distanceX
=Math
.abs(x22
-x11
);
int distanceY
=Math
.abs(y22
-y11
);
if(distanceX
<=100&&distanceY
<=100){
return true;
}else{
return false;
}
}
public void subBlood(){
this.hp
--;
}
public void subDoubleBlood(){
this.hp
-=5;
}
public void addScores(){
scores
+=5;
}
public void addDoubleScores(){
scores
+=10;
}
public void setHP(int hp
){
this.hp
=hp
;
}
public int
getHP(){
return hp
;
}
}
DiJi.java:敌机类
public class Diji implements Entity{
private int x
,y
;
private int automaticMove
;
private boolean isDied
;
File musicFile
;
private Image dijiImage
;
private Image diedImage
;
private int dijiBulletCount
;
public Diji(){
musicFile
=new File("C:\\Users\\梦想少年\\Music\\"
+ "AudioConvert\\大爆炸.wav");
dijiImage
=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\diji.png").getImage();
diedImage
=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\1\\7.png").getImage();
Random random
=new Random();
x
=random
.nextInt(100)+1900;
y
=random
.nextInt(800)+80;
}
public void move() {
if(automaticMove
==1){
y
--;
}
if(automaticMove
==2){
y
++;
}
x
-=2;
}
public void draw(Graphics g
) {
g
.drawImage(dijiImage
,x
,y
,128,128,null);
if(isDied
){
g
.drawImage(diedImage
, x
, y
, null);
}
}
public boolean
isOutBounds(){
if(x
<0){
return true;
}else{
return false;
}
}
public void Blast(){
isDied
=true;
}
@
SuppressWarnings("deprecation")
public void startMusic(){
URL url
;
try {
url
= musicFile
.toURL();
AudioClip aud
;
aud
=Applet
.newAudioClip(url
);
aud
.play();
try {
Thread
.sleep(1000);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
aud
.stop();
} catch (MalformedURLException e
) {
}
}
public void setX(int startX
) {
this.x
=startX
;
}
public void setY(int startY
) {
this.y
=startY
;
}
public int
getX() {
return x
;
}
public int
getY() {
return y
;
}
public void addBulletCount(){
dijiBulletCount
++;
}
public int
getBulletCount(){
return dijiBulletCount
;
}
public int
getDiJiHeight(){
return dijiImage
.getHeight(null);
}
public int
getDiJiWidth(){
return dijiImage
.getWidth(null);
}
}
MyFeiJiBullet.java:战机炮弹类
public class MyFeiJiBullet implements Entity {
private int bulletType
;
private int x
,y
;
Image image
;
public MyFeiJiBullet(){
image
=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\bullet2.png").getImage();
}
public void move() {
x
+=7;
}
public void draw(Graphics g
) {
if(bulletType
==1){
g
.drawImage(image
, x
, y
, 100, 100, null);
}
}
public boolean
isOutBounds(){
if(x
>1920){
return true;
}else{
return false;
}
}
public void setBulletType(int bulletType
){
this.bulletType
=bulletType
;
}
public void setX(int startX
) {
this.x
=startX
;
}
public void setY(int startY
) {
this.y
=startY
;
}
public int
getX() {
return x
;
}
public int
getY() {
return y
;
}
public boolean
isShootDiJiBullet(DiJiBullet dijibullet
){
int x1
=this.x
;
int y1
=this.y
;
int width1
=this.image
.getWidth(null);
int height1
=this.image
.getHeight(null);
int x11
=x1
+width1
/2;
int y11
=y1
+height1
/2;
int x2
=dijibullet
.getX();
int y2
=dijibullet
.getY();
int width2
=dijibullet
.getDiJiBulletWidth();
int height2
=dijibullet
.getDiJiBulletHeight();
int x22
=x2
+width2
/2;
int y22
=y2
+height2
/2;
int distanceX
=Math
.abs(x22
-x11
);
int distanceY
=Math
.abs(y22
-y11
);
if(distanceX
<=200&&distanceY
<=100){
return true;
}else{
return false;
}
}
public boolean
isShootDiJi(Diji diji
){
int x1
=this.x
;
int y1
=this.y
;
int width1
=this.image
.getWidth(null);
int height1
=this.image
.getHeight(null);
int x11
=x1
+width1
/2;
int y11
=y1
+height1
/2;
int x2
=diji
.getX();
int y2
=diji
.getY();
int width2
=diji
.getDiJiWidth();
int height2
=diji
.getDiJiHeight();
int x22
=x2
+width2
/2;
int y22
=y2
+height2
/2;
int distanceX
=Math
.abs(x22
-x11
);
int distanceY
=Math
.abs(y22
-y11
);
if(distanceX
<=100&&distanceY
<=100){
return true;
}else{
return false;
}
}
}
DiJiBullet.java:敌机炮弹类
public class DiJiBullet implements Entity {
private int x
,y
;
private Image image
;
public DiJiBullet(){
image
=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\1\\5.PNG").getImage();
}
public void move() {
x
-=3;
}
public void draw(Graphics g
) {
g
.drawImage(image
, x
, y
, null);
}
public boolean
isOutBounds(){
if(x
<0){
return true;
}else{
return false;
}
}
public void setX(int startX
) {
this.x
=startX
;
}
public void setY(int startY
) {
this.y
=startY
;
}
public int
getX() {
return x
;
}
public int
getY() {
return y
;
}
public int
getDiJiBulletHeight(){
return image
.getHeight(null);
}
public int
getDiJiBulletWidth(){
return image
.getWidth(null);
}
}
BackGroundPhotosMusic.java:背景图片类
public class BackGroundPhotosMusic implements Entity{
private int x
,y
;
private Image image
;
private AudioClip audio
;
private boolean isMusic
;
private String musicString
;
private boolean isGame
;
private String gameString
;
public BackGroundPhotosMusic(){
image
=new ImageIcon("D:\\常用\\图片\\"
+ "Camera Roll\\image\\bg11.jpg").getImage();
File musicFile
=new File("C:\\Users\\梦想少年\\Music\\"
+ "AudioConvert\\薛之谦 - 天后 (Live).wav");
try {
audio
=Applet
.newAudioClip(musicFile
.toURI().toURL());
} catch (MalformedURLException e
) {
e
.printStackTrace();
}
}
public void move() {
x
--;
}
public void draw(Graphics g
) {
if((x
+image
.getWidth(null)<=0)){
x
=0;
}
g
.drawImage(image
, x
, y
, 1920, 1080, null);
g
.drawImage(image
, x
+image
.getWidth(null), y
, 1920, 1080, null);
g
.setFont(new Font("黑体",Font
.BOLD,30));
g
.setColor(Color
.BLUE);
g
.drawString("游戏设置",30,180);
g
.setFont(new Font("黑体",Font
.BOLD,20));
g
.setColor(Color
.RED);
g
.drawString(musicString
,35,220);
g
.setColor(Color
.WHITE);
g
.drawRect(35,200,85,25);
g
.setFont(new Font("黑体",Font
.BOLD,20));
g
.setColor(Color
.RED);
g
.drawString(gameString
,35,250);
g
.setColor(Color
.WHITE);
g
.drawRect(35,230,85,25);
g
.setFont(new Font("黑体",Font
.BOLD,20));
g
.setColor(Color
.RED);
g
.drawString("退出游戏",35,280);
g
.setColor(Color
.WHITE);
g
.drawRect(35,260,85,25);
}
public void setX(int startX
){}
public void setY(int startY
){}
public int
getX() {
return 0;
}
public int
getY() {
return 0;
}
public void startMusic(){
if(!isMusic
){
audio
.loop();
musicString
="暂停音乐";
isMusic
=true;
}else{
audio
.stop();
musicString
="打开音乐";
isMusic
=false;
}
}
public void StartGame(){
if(!isGame
){
gameString
="暂停游戏";
isGame
=true;
}else{
gameString
="开始游戏";
isGame
=false;
}
}
public boolean
gameState(){
return isGame
;
}
}
MainUI.java:主界面类
public class MainUI implements KeyListener,MouseListener
{
ArrayList
<Entity
> wholelist
=new ArrayList<>();
public static void main(String
[] args
){
MainUI ui
=new MainUI();
ui
.showUI();
}
MyFeiJi myfeiji
=new MyFeiJi();
BackGroundPhotosMusic background
=new BackGroundPhotosMusic();
public void showUI(){
JFrame jframe
=new JFrame("雷霆战机");
jframe
.setSize(1920, 1080);
jframe
.setLayout(new FlowLayout());
jframe
.setDefaultCloseOperation(JFrame
.EXIT_ON_CLOSE);
jframe
.setLocationRelativeTo(null);
jframe
.addKeyListener(this);
jframe
.addMouseListener(this);
jframe
.setVisible(true);
background
.startMusic();
background
.StartGame();
wholelist
.add(background
);
myfeiji
.setX(300);
myfeiji
.setY(500);
myfeiji
.setHP(100);
wholelist
.add(myfeiji
);
RemoveThread remove
=new RemoveThread(wholelist
);
remove
.start();
Timer timer
=new Timer();
timer
.schedule(new TimerTask() {
public void run(){
Diji diji1
=new Diji();
wholelist
.add(diji1
);
for(int i
=0;i
<=30;i
++){
DiJiBullet dijizidan1
=new DiJiBullet();
dijizidan1
.setX(diji1
.getX()-20);
dijizidan1
.setY(diji1
.getY()+45);
diji1
.addBulletCount();
wholelist
.add(dijizidan1
);
try {
Thread
.sleep(1000);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
if(diji1
.getBulletCount()==5){
break;
}
}
}
},1,1);
Graphics g
=jframe
.getGraphics();
BufferedImage bufferedimage
=new BufferedImage(jframe
.getWidth(),
jframe
.getHeight(), BufferedImage
.TYPE_3BYTE_BGR);
Graphics bufferG
=bufferedimage
.getGraphics();
while(true){
for(int i
=0;i
<wholelist
.size();i
++){
Entity whole
=wholelist
.get(i
);
whole
.draw(bufferG
);
if(!myfeiji
.isDefeat()&&background
.gameState()){
whole
.move();
}else if(myfeiji
.getHP()<=0){
int result
=JOptionPane
.showConfirmDialog(jframe
,""
+ "是否退出游戏?");
if(result
==0){
System
.exit(0);
}
}
}
g
.drawImage(bufferedimage
,0,0,null);
try {
Thread
.sleep(5);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
}
public void keyPressed(KeyEvent e
){
int code
=e
.getKeyCode();
if(code
==KeyEvent
.VK_F){
MyFeiJiBullet myfeijibullet1
=new MyFeiJiBullet();
myfeijibullet1
.setX(myfeiji
.getX()+100);
myfeijibullet1
.setY(myfeiji
.getY()-10);
myfeijibullet1
.setBulletType(1);
wholelist
.add(myfeijibullet1
);
MyFeiJiBullet myfeijibullet2
=new MyFeiJiBullet();
myfeijibullet2
.setX(myfeiji
.getX()+100);
myfeijibullet2
.setY(myfeiji
.getY()+55);
myfeijibullet2
.setBulletType(1);
wholelist
.add(myfeijibullet2
);
}
if(code
==KeyEvent
.VK_UP){
myfeiji
.up
=true;
}
if(code
==KeyEvent
.VK_DOWN){
myfeiji
.down
=true;
}
if(code
==KeyEvent
.VK_LEFT){
myfeiji
.left
=true;
}
if(code
==KeyEvent
.VK_RIGHT){
myfeiji
.right
=true;
}
}
public void keyTyped(KeyEvent e
){}
public void keyReleased(KeyEvent e
){
int code
=e
.getKeyCode();
if(code
==KeyEvent
.VK_UP){
myfeiji
.up
=false;
}
if(code
==KeyEvent
.VK_DOWN){
myfeiji
.down
=false;
}
if(code
==KeyEvent
.VK_LEFT){
myfeiji
.left
=false;
}
if(code
==KeyEvent
.VK_RIGHT){
myfeiji
.right
=false;
}
}
public void mouseClicked(MouseEvent e
){}
public void mousePressed(MouseEvent e
){
int x
=e
.getX();
int y
=e
.getY();
if(x
>=35&&x
<=85&&y
>=200&&y
<=225){
background
.startMusic();
}else if(x
>=35&&x
<=85&&y
>=230&&y
<=255){
background
.StartGame();
}else if(x
>=35&&x
<=85&&y
>=260&&y
<=285){
System
.exit(0);
}
}
public void mouseReleased(MouseEvent e
){}
public void mouseEntered(MouseEvent e
){}
public void mouseExited(MouseEvent e
){}
}
RemoveThread.java:去除线程类
public class RemoveThread extends Thread{
ArrayList
<Entity
> list
;
private boolean isShootDiJiBullet
;
private boolean isShootDiJi
;
public RemoveThread(ArrayList
<Entity
> list
) {
this.list
=list
;
}
public void run(){
while(true){
try {
Thread
.sleep(2);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
for(int i
=0;i
<list
.size();i
++){
Entity entity
=list
.get(i
);
if(entity
instanceof Diji){
Diji diji
=(Diji
)entity
;
if(diji
.isOutBounds()){
list
.remove(diji
);
}
}else if(entity
instanceof MyFeiJiBullet){
MyFeiJiBullet myfeijibullet
=(MyFeiJiBullet
)entity
;
if(myfeijibullet
.isOutBounds()){
list
.remove(myfeijibullet
);
}
}else if(entity
instanceof DiJiBullet){
DiJiBullet dijibullet
=(DiJiBullet
)entity
;
if(dijibullet
.isOutBounds()){
list
.remove(dijibullet
);
}
}
}
for(int i
=0;i
<list
.size();i
++){
Entity entity1
=list
.get(i
);
for(int j
=0;j
<list
.size();j
++){
Entity entity2
=list
.get(j
);
if(entity1
instanceof MyFeiJi){
MyFeiJi myfeiji
=(MyFeiJi
)entity1
;
if(isShootDiJi
){
myfeiji
.addDoubleScores();
isShootDiJi
=false;
}else if(isShootDiJiBullet
){
myfeiji
.addScores();
isShootDiJiBullet
=false;
}
if(entity2
instanceof DiJiBullet){
DiJiBullet dijibullet
=(DiJiBullet
)entity2
;
if(myfeiji
.isShootByDiJiBullet(dijibullet
)){
list
.remove(dijibullet
);
myfeiji
.subBlood();
}
}
else if(entity2
instanceof Diji){
Diji diji
=(Diji
)entity2
;
if(myfeiji
.isShootByDiJi(diji
)){
diji
.Blast();
diji
.startMusic();
list
.remove(diji
);
myfeiji
.subDoubleBlood();
}
}
}
else if(entity1
instanceof MyFeiJiBullet){
MyFeiJiBullet myfeijibullet
=(MyFeiJiBullet
)entity1
;
if(entity2
instanceof DiJiBullet){
DiJiBullet dijibullet
=(DiJiBullet
)entity2
;
if(myfeijibullet
.isShootDiJiBullet(dijibullet
)&&
i
<list
.size()&&j
<list
.size()){
list
.remove(myfeijibullet
);
list
.remove(dijibullet
);
isShootDiJiBullet
=true;
}
}else if(entity2
instanceof Diji){
Diji diji
=(Diji
)entity2
;
if(myfeijibullet
.isShootDiJi(diji
)&&
i
<list
.size()&&j
<list
.size()){
list
.remove(myfeijibullet
);
diji
.Blast();
diji
.startMusic();
list
.remove(diji
);
isShootDiJi
=true;
}
}
}
}
}
}
}
}
总结分析
先来看看效果吧!!!! 整体效果还是不错的,代码的可拓展性还是比较好的,大家如果想要添加一些别的功能:大boss、奖赏、添加技能等等。但是在编写的时候我遇到了一些问题: 1.清除线程存在错位和碰撞检测不及时的问题 2.不能很好地解决敌机爆炸和爆炸背景音乐播放的延时问题
大家如果有问题我们可以相互讨论啊ヾ(≧▽≦*)o,最后记得留下一个赞哦!!!