Java之简易版飞机大战

本篇博客主要利用java实现了一个简易版的飞机大战,适合初学者练手,所涉及到的知识主要有以下几点:
1,面向对象。飞机大战的功能是针对每个英雄机、子弹和敌机对象实现的。
2,数组。由于敌机和子弹对象有多个,创建一个List数组用来存储相应对象,之后针对数组中的对象进行操作。
3,图片。站在用户的角度,我们看到的是一张张飞机、子弹的图片,对对象的操作要和相应的图片联系起来。
4,线程。利用sleep()函数使线程休眠,让游戏效果可以用视觉捕捉。

一,游戏规则:

当英雄机发射的子弹击中敌机,敌机产生爆炸效果并消失,每击中一架敌机分数+10。

二,游戏效果:

在这里插入图片描述

三,游戏制作

1,窗口的实现

package PlaneGame;

import java.awt.Graphics;

import javax.swing.JFrame;

public class GameMain {
    
    
	//窗口宽高
	static int width=550;
	static int heigh=700;
	
	public void UI() {
    
    
		//创建一个窗体
		JFrame frame = new JFrame();
		frame.setTitle("飞机大战");//设置标题
		frame.setSize(width, heigh);//设置窗体大小
		frame.setLocationRelativeTo(null);//使窗体显示在屏幕中央
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//退出时关闭窗口
		frame.setVisible(true);//显示窗体可见
	}
	
	//主函数   程序入口
	public static void main(String[] args) {
    
    
		GameMain gamemain= new GameMain();
		gamemain.UI();		
	}

}

效果如下:
在这里插入图片描述

2,英雄机的显示与移动

<1>创建一个GamePanel类继承JPanel。JFrame是最顶层组件,每一个窗口都是JFrame,其他组件都要放在窗口里面。窗口里面可以有好几个区域,每个区域都可以称做一个面板,这个面板可以叫做JPanel(别的容器也可以)。JPanel是Java的一个轻量级容器,JPanel可以添加到JFrame中,反之则不行。本次游戏制作我们将主要功能全部放在GamePanel中,因为JPanel默认使用了双缓冲绘图,可以解决闪频问题。

package PlaneGame;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JPanel;

public class GamePanel extends JPanel{
    
    

}

<2>在主窗口类GameMain中创建一个GamePanel对象并添加到窗口对象中。

		//创建JPanel  添加到窗体上    监听在JPanel中
		GamePanel panel = new GamePanel();
		frame.add(panel);

<3>展示英雄机的图片。这里我们将游戏的图片全部放在Eclipse的一个img图片包中,这样只需要从img包中获取图片就可以了。
在这里插入图片描述

	//英雄机左上角坐标   初始位置   之后根据鼠标拖拽改变位置
	int heroX=300;
	int heroY=400;

	//英雄机图片                        图片路径
	ImageIcon heroImage = new ImageIcon("img/hero.png");

    //paint方法窗口对象自动调用   不需手动调用    绘制图片
	public void paint(Graphics g) {
    
    
		super.paint(g);

	    //绘制英雄机
		g.drawImage(heroImage.getImage(), heroX, heroY,null);

	}

得到效果如下:
在这里插入图片描述
<4>创建鼠标拖拽监听器,使英雄机位置随鼠标拖拽移动。

public class GamePanel extends JPanel implements MouseMotionListener{
    
    
 	//重写鼠标拖拽方法
    public void mouseDragged(MouseEvent e) {
    
    
    	//鼠标的x、y
    	int x=e.getX();
    	int y=e.getY();
    	//将鼠标拖拽位置赋给英雄机坐标
    	heroX=x;
    	heroY=y;
    }

    
    //重写鼠标移动方法
    public void mouseMoved(MouseEvent e) {
    
    
    	//System.out.println("鼠标移动");
    }

}
  • 在paint()方法中添加repaint()方法,这样英雄机坐标改变后自动重新绘制,达到移动效果。
		//重新绘制自动调用paint方法   更新图片  否则实现不了动画效果
		repaint();
  • 在GameMain类的UI方法中给窗口添加鼠标监听器。
		//添加鼠标移动监听器
		frame.addMouseMotionListener(panel); 

效果如下:
在这里插入图片描述
<5>使鼠标位置处于图片中央。按照上述移动图片发现鼠标位置始终处于图片的左上角,这是因为英雄机图片是一张去除背景的矩形图片,鼠标位置就是矩形图片的左上角。为了更好地控制英雄机,我们要让鼠标位置出现在图片中央,就是对英雄机的左上角位置进行处理,使英雄机左上角位置不再是鼠标位置,而变为鼠标坐标减去图片大小的1/2,这样鼠标就能出现在图片中央了。

    	heroX=x-(heroImage.getIconWidth()/2);//使鼠标在英雄机图片的中央
    	heroY=y-(heroImage.getIconHeight()/2);

效果如下:
在这里插入图片描述
<6>限制英雄机位置,使英雄机的移动不超过窗体。在上面的效果中我们发现英雄机会随着鼠标的拖拽移除窗体边界,但在游戏中我们不希望这样的效果发生,因此当英雄机的图片边缘移动到窗体边缘时,将英雄机图片位置限定为窗体边界,这样英雄机的移动就不会超过窗体。

    public void mouseDragged(MouseEvent e) {
    
    
    	//鼠标的x、y
    	int x=e.getX();
    	int y=e.getY();
    	//将鼠标拖拽位置赋给英雄机坐标
    	heroX=x-(heroImage.getIconWidth()/2);//使鼠标在英雄机图片的中央
    	heroY=y-(heroImage.getIconHeight()/2);
    	//使英雄机不超出边界
    	//右边界
    	if((x+heroImage.getIconWidth())>GameMain.width) {
    
    
    		heroX=GameMain.width-heroImage.getIconWidth()-20;
    	}
    	//左边界
    	if((x)<80) {
    
    
    		heroX=0;
    	}
    	//下边界
    	if((y+heroImage.getIconHeight())>GameMain.heigh) {
    
    
    		heroY=GameMain.heigh-heroImage.getIconHeight()-20;
    	}
    	//上边界
    	if(y<80) {
    
    
    		heroY=0;
    	}
    	
    	//System.out.println(heroX+"   "+heroY);
    }

效果如下:
在这里插入图片描述

3,敌机效果

<1>创建敌机类,每一个敌机都是一个敌机对象,在敌机类中得到敌机的图片,定义敌机的各种属性和方法,同时定义敌机类的构造方法,这样在之后创建敌机对象的同时就能初始化敌机的位置和宽高。

package PlaneGame;

import java.awt.Graphics;
import java.util.Random;

import javax.swing.ImageIcon;

//敌机对象
public class Enemy {
    
    
	//敌机宽高
	private int width;
	private int heigth;
	//敌机坐标
	private int x;
	private int y;
	//敌机图片
	private ImageIcon enemyImageIcon = new ImageIcon("img/enemy.png");
	
	//构造方法   获取敌机宽高   敌机x、y位置随机出现
	//敌机对象有多个  每次创建对象的时候立刻得到该对象的宽高和位置
	public Enemy() {
    
    
		this.width=enemyImageIcon.getIconWidth();
		this.heigth=enemyImageIcon.getIconHeight();
		
		//随机产生敌机位置
		Random random = new Random();
		//不超出窗体范围   且在英雄机能击中的范围内
		this.x=random.nextInt(GameMain.width-width-25);//-width-25是为了使敌机位置不超出左边界且不太偏使子弹无法击中
		this.y=-random.nextInt(GameMain.heigh-heigth);//敌机从上往下飞   默认初始化在窗体外   窗体上方(-heigth)
		
	}
	
	//私有属性    宽的get方法
	public int getWidth() {
    
    
		return width;
	}
	//私有属性    宽的set方法
	public void setWidth(int width) {
    
    
		this.width = width;
	}
	
	//私有属性    高的get方法
	public int getHeigth() {
    
    
		return heigth;
	}
	//私有属性    高的set方法
	public void setHeigth(int heigth) {
    
    
		this.heigth = heigth;
	}
	
	//私有属性    敌机x坐标的get方法
	public int getX() {
    
    
		return x;
	}
	//私有属性    敌机x坐标的set方法
	public void setX(int x) {
    
    
		this.x = x;
	}
	
	//私有属性    敌机y坐标的get方法
	public int getY() {
    
    
		return y;
	}
	//私有属性    敌机y坐标的set方法
	public void setY(int y) {
    
    
		this.y = y;
	}
	



}

<2>在创建容器对象时创建敌机对象。

  • 创建一个List数组用来保存敌机对象。
	//存储敌机的数组
	List<Enemy> enemys = new ArrayList<Enemy>();

  • 在GamePanel中增加一个构造方法,在创建GamePanel对象时就创建敌机对象,使得敌机对象一开始就出现在面板中。
	//构造方法   在创建GamePanel对象时就创建敌机对象   使得敌机对象一开始就出现在面板中
	public  GamePanel() {
    
    
		//创建10个敌机   加入数组中
		for(int i=0;i<10;i++) {
    
    
			enemys.add(new Enemy());
		}
		
	}

<3>init()方法

  • GamePanel定义init()方法初始化容器,在该方法中实现整个游戏的具体逻辑。
//	//初始化使用   逻辑操作
	public void init() {
    
    

	}
  • 在GameMain类的UI()方法中调用init()方法。
	public void UI() {
    
    
		//创建一个窗体
		JFrame frame = new JFrame();
		frame.setTitle("飞机大战");//设置标题
		frame.setSize(width, heigh);//设置窗体大小
		frame.setLocationRelativeTo(null);//使窗体显示在屏幕中央
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//退出时关闭窗口
		
		//创建JPanel  添加到窗体上    监听在JPanel中
		GamePanel panel = new GamePanel();
		frame.add(panel);
		//添加鼠标移动监听器
		frame.addMouseMotionListener(panel); 

		frame.setVisible(true);//显示窗体可见
		//调用初始化方法   游戏逻辑操作
		panel.init();
	}

<4>让敌机动起来

  • 在Enemy类中定义移动方法,敌机往下移动即增加敌机的y坐标。
	//敌机移动方法   往下移   可视为敌机的移动速度
	public void move() {
    
    
		this.y+=1;
	
	}
  • 在init()方法中调用move方法,使所有敌机都移动起来。
	public void init() {
    
    
//			//让所有敌机往下移动
			for(int i=0;i<enemys.size();i++) {
    
    
				Enemy enemy =enemys.get(i);
				enemy.move();//移动敌机   使敌机往下移  改变敌机y值

			}
	}

<5>绘制敌机

  • 在Enemy类中定义绘制敌机的方法。
	//画敌机的方法
	public void drawImage(Graphics g) {
    
    
		g.drawImage(enemyImageIcon.getImage(), x, y,null);
		
	}
  • 在paint()方法中调用,将全部敌机绘制出来。
	//窗口对象自动调用   不需手动调用    绘制图片
	public void paint(Graphics g) {
    
    
		super.paint(g);
		//绘制英雄机
		g.drawImage(heroImage.getImage(), heroX, heroY,null);
	    //绘制全部敌机
		for(int i=0;i<enemys.size();i++) {
    
    
			Enemy enemy = enemys.get(i);
			enemy.drawImage(g);
		}

//		//重新绘制自动调用paint方法   更新图片  否则实现不了动画效果
//		repaint();

	}
  • 让敌机位置不断改变。上述只实现了敌机的一次移动,要实现敌机的不断移动就要增加while(true)模块。
		while(true) {
    
    			
//			//让所有敌机往下移动
			for(int i=0;i<enemys.size();i++) {
    
    
				Enemy enemy =enemys.get(i);
				enemy.move();//移动敌机   使敌机往下移  改变敌机y值

		}
  • 增加休眠,否则敌机移动太快,视觉无法捕捉。
		while(true) {
    
    
		
//			//让所有敌机往下移动
			for(int i=0;i<enemys.size();i++) {
    
    
				Enemy enemy =enemys.get(i);
				enemy.move();//移动敌机   使敌机往下移  改变敌机y值
            }	
            	
			//休息5ms  否则敌机移动太快   视觉无法捕捉
			try {
    
    
				Thread.sleep(5);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 

		}

实现效果如下:
在这里插入图片描述
<6>删除超过边界的敌机,增加新的敌机。
在上面的效果中我们发现10架敌机移动完之后界面中就没有敌机了,因此我们要向敌机列表中不断加敌机,但这样做会导致敌机列表中存储的敌机越来越多。从用户角度来看,移出界面的敌机就相当与无效,因此我们可以把移动到界面外的敌机删除,然后再添加新的敌机,这样敌机数组中的敌机一直只有10架。

//			//让所有敌机往下移动
			for(int i=0;i<enemys.size();i++) {
    
    
				Enemy enemy =enemys.get(i);
				enemy.move();//移动敌机   使敌机往下移  改变敌机y值
				//判断敌机是否飘出下界     敌机y坐标是否超过窗体  超过则将该架敌机移除释放内存   重新添加一架敌机否则敌机移除完就没有了
				if(enemy.getY()>GameMain.heigh) {
    
    
					enemys.remove(enemy);
					enemys.add(new Enemy());
				}
			}

效果如下:
在这里插入图片描述

4,子弹效果

<1>创建子弹类Bullet。

  • 每个子弹都是一个对象,在子弹类中得到子弹图片定义子弹类的属性和方法。
package PlaneGame;

import java.awt.Graphics;

import javax.swing.ImageIcon;

//子弹类
public class Bullet {
    
    
	//子弹宽高
	private int width;
	private int heigth;
	//子弹位置
	private int x;
	private int y;
	//子弹图片
	private ImageIcon bulletImageIcon = new ImageIcon("img/bullet.png");
	
	
	//私有属性  子弹宽度width的get方法
	public int getWidth() {
    
    
		return width;
	}
	//私有属性  子弹宽度width的set方法
	public void setWidth(int width) {
    
    
		this.width = width;
	}
	
	//私有属性  子弹高度heigth的get方法
	public int getHeigth() {
    
    
		return heigth;
	}
	//私有属性  子弹高度heigth的set方法
	public void setHeigth(int heigth) {
    
    
		this.heigth = heigth;
	}
	
	//私有属性  子弹坐标x的get方法
	public int getX() {
    
    
		return x;
	}
	//私有属性  子弹坐标x的set方法
	public void setX(int x) {
    
    
		this.x = x;
	}
	 
	//私有属性  子弹坐标y的get方法
	public int getY() {
    
    
		return y;
	}
	//私有属性  子弹坐标y的set方法
	public void setY(int y) {
    
    
		this.y = y;
	}
    

}
  • 在构造器中初始化子弹位置。
	//构造方法   初始化子弹    子弹的位置x、y跟随鼠标位置    通过构造方法将鼠标位置传入
	public Bullet(int x,int y) {
    
    
		this.x=x;
		this.y=y;
		this.width=bulletImageIcon.getIconWidth();
		this.heigth=bulletImageIcon.getIconHeight();
	}

<2>创建子弹对象

  • 创建一个用来保存子弹对象的数组。
	//存储敌机的数组
	List<Enemy> enemys = new ArrayList<Enemy>();
	//创建子弹的数组
	List<Bullet> bullets = new ArrayList<Bullet>();
  • 创建子弹对象,将英雄机上方中间位置传入子弹对象中,将子弹对象添加到子弹数组中,
		while(true) {
    
    
			//创建一些子弹    修改子弹位置  使子弹位置在飞机正中间
			Bullet bullet = new Bullet(heroX+(heroImage.getIconWidth()/2)-2,heroY-15);
			//添加到子弹数组中
			bullets.add(bullet);


			//让所有敌机往下移动
			for(int i=0;i<enemys.size();i++) {
    
    
				Enemy enemy =enemys.get(i);
				enemy.move();//移动敌机   使敌机往下移  改变敌机y值
            }	
            	
            	
			//休息5ms  否则敌机移动太快   视觉无法捕捉
			try {
    
    
				Thread.sleep(5);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 

		}

<3>让子弹移动起来

  • 在Bullet类中定义移动方法。
	//子弹移动方法    子弹向上移   可视为子弹移动速度
	public void move() {
    
    
		this.y-=3;
		
	}
  • 在init()方法中调用移动方法,让所有子弹移动起来。
			//创建一些子弹    修改子弹位置  使子弹位置在飞机正中间
			Bullet bullet = new Bullet(heroX+(heroImage.getIconWidth()/2)-2,heroY-15);
			//添加到子弹数组中
			bullets.add(bullet);

			//让子弹移动起来
			for(int i=0;i<bullets.size();i++) {
    
    
				Bullet tempBullet =bullets.get(i);
				tempBullet.move();
			}

<4>绘制子弹

  • 在Bullet类中定义绘制方法。
    //子弹绘制方法
	public void drawImage(Graphics g) {
    
    
		g.drawImage(bulletImageIcon.getImage(), x, y, null);
		
	}
  • 在paint()方法中调用,将所有子弹绘制出来。
		//绘制英雄机
		g.drawImage(heroImage.getImage(), heroX, heroY,null);
	    //绘制全部敌机
		for(int i=0;i<enemys.size();i++) {
    
    
			Enemy enemy = enemys.get(i);
			enemy.drawImage(g);
		}
		
		//英雄机只有一架  而敌机子弹等有多架  因此定义绘制函数   每个对象都调用自己的绘制绘制函数
		
		//绘制全部子弹
		for(int i=0;i<bullets.size();i++) {
    
    
			Bullet bullet = bullets.get(i);
			bullet.drawImage(g);
		}

得到的效果图如下:
在这里插入图片描述
<5>降低子弹增加频率。
在上面的效果中我们发现子弹过密影响游戏效果,因此我们利用flag计数来控制子弹的添加,将添加代码修改如下:

//	//初始化使用   逻辑操作
	public void init() {
    
    
		int flag=0;//用来使子弹的创建不过密
		//一直进行添加、删除、判断是否碰撞等操作
		while(true) {
    
    
			flag++;
			//创建子弹      使子弹的创建不过密
			if(flag%20==0) {
    
    
				//创建一些子弹    修改子弹位置  使子弹位置在飞机正中间
				Bullet bullet = new Bullet(heroX+(heroImage.getIconWidth()/2)-2,heroY-15);
				//添加到子弹数组中
				bullets.add(bullet);
			}
			//让子弹移动起来
			for(int i=0;i<bullets.size();i++) {
    
    
				Bullet tempBullet =bullets.get(i);
				tempBullet.move();
			}
			
		...//省略其他代码
		...
		
		}
	}

在这里插入图片描述

<6>删除越界的子弹
和敌机一样,当子弹飘出界面时,我们将其删除,可以有效控制子弹数组的长度,这时删除完越界的子弹后不用添加新的子弹,因为之前的flag计数部分一直在添加新的子弹。

			//让子弹移动起来
			for(int i=0;i<bullets.size();i++) {
    
    
				Bullet tempBullet =bullets.get(i);
				tempBullet.move();
			}
//			//System.out.println(bullets.size());
			//删除越界的子弹    否则子弹一直在增加    不利于内存
			for(int i=0;i<bullets.size();i++) {
    
    
				Bullet bullet = bullets.get(i);
				if(bullet.getY()<0) {
    
    //子弹越界了
					bullets.remove(bullet);
				}
			}

5,爆炸效果

<1>碰撞方法,即子弹是否运动到敌机图片范围。在GamePanel中定义碰撞方法:

	//碰撞方法   判断子弹是否打中敌机   即子弹是否运动到敌机所处区域
	public boolean isHit(Enemy enemy,Bullet bullet) {
    
    
		//指定一个区域  即敌机图片所处区域
		Rectangle rect = new Rectangle(enemy.getX(),enemy.getY(),enemy.getWidth(),enemy.getHeigth());
		//表示(x,y)坐标空间中的位置的点   即子弹的中心位置
		Point p = new Point(bullet.getX()+bullet.getWidth()/2,bullet.getY()+bullet.getHeigth());
		//如果子弹处在敌机的区域  则认为子弹击中了敌机   返回true
		return rect.contains(p);
	}

<2>碰撞后删除该敌机和子弹,同时添加新的敌机和子弹,使得不会因为在界面内全部被击中后界面中没有敌机和子弹(上文中是在敌机和子弹飘出边界后才删除和添加)。

//			//让所有敌机往下移动
			for(int i=0;i<enemys.size();i++) {
    
    
				Enemy enemy =enemys.get(i);
				enemy.move();//移动敌机   使敌机往下移  改变敌机y值
				//判断敌机是否飘出下界     敌机y坐标是否超过窗体  超过则将该架敌机移除释放内存   重新添加一架敌机否则敌机移除完就没有了
				if(enemy.getY()>GameMain.heigh) {
    
    
					enemys.remove(enemy);
					enemys.add(new Enemy());
				}
			}	
		
			//判断是否发生碰撞    子弹打到敌机   获取当前所有敌机和子弹   任意两个相撞都是碰撞了 
			for(int i=0;i<enemys.size();i++) {
    
    
				Enemy enemy = enemys.get(i);//敌机
				for(int j=0;j<bullets.size();j++) {
    
    
					Bullet bullet = bullets.get(j);//子弹
					//判断是否击中
					if(isHit(enemy,bullet)) {
    
    
敌机消失    将敌机删除移除敌机列表
						enemys.remove(enemy);
						//再新加入一个敌机  否则如果所有敌机打完全部删光   没有敌机了
						enemys.add(new Enemy());
						//删除该子弹
						bullets.remove(bullet);
						//添加新的子弹进去
						//bullets.add(new Bullet(heroX+(heroImage.getIconWidth()/2)-2,heroY-15));
						
					}
					
				}
			}

在这里插入图片描述
<3>定义爆炸图片类Bomb。

  • 在Bomb类中得到爆炸图片,定义爆炸图片的属性和方法。
package PlaneGame;

import java.awt.Graphics;

import javax.swing.ImageIcon;

//碰撞类
public class Bomb {
    
    
	//碰撞图片的坐标    左上角
	private int x;
	private int y;
	//碰撞图片的宽高
	private int width;
	private int heigth;
	
	//碰撞图片
	private ImageIcon bombImageIcon  = new ImageIcon("img/bomb.png");
	
    
	//私有属性    爆炸图片x坐标的get方法
	public int getX() {
    
    
		return x;
	}    
	//私有属性   爆炸图片x坐标的set方法
	public void setX(int x) {
    
    
		this.x = x;
	}

	//私有属性   爆炸图片y坐标的get方法
	public int getY() {
    
    
		return y;
	}
    //私有属性   爆炸图片y坐标的set方法
	public void setY(int y) {
    
    
		this.y = y;
	}

	//私有属性   爆炸图片宽度的get方法
	public int getWidth() {
    
    
		return width;
	}
    //私有属性   爆炸图片宽度的set方法
	public void setWidth(int width) {
    
    
		this.width = width;
	}

	//私有属性   爆炸图片高度的get方法
	public int getHeigth() {
    
    
		return heigth;
	}
    //私有属性    爆炸图片高度的set方法
	public void setHeigth(int heigth) {
    
    
		this.heigth = heigth;
	}


}

  • 定义构造方法,使得创建对象时能够初始化爆炸图片的位置。
	//构造方法   初始化爆炸图片  爆炸图片显示再敌机位置   视觉效果为敌机被子弹击中敌机爆炸   通过创建对象时将敌机位置传入
	public Bomb(int x,int y) {
    
    
		this.x=x;
		this.y=y;
		this.width=bombImageIcon.getIconWidth();
		this.heigth=bombImageIcon.getIconHeight();
	    
	}

<4>当发生碰撞时展示爆炸图片。

  • 创建一个保存爆炸图片的数组。
	//存储敌机的数组
	List<Enemy> enemys = new ArrayList<Enemy>();
	//创建子弹的数组
	List<Bullet> bullets = new ArrayList<Bullet>();
	//创建保存碰撞图片的数组
	List<Bomb> bombs = new ArrayList<Bomb>();
  • 发生爆炸时创建爆炸图片对象,将爆炸图片对象添加到爆炸图片数组中。
					if(isHit(enemy,bullet)) {
    
    
						
						//如果击中   则敌机消失    将敌机删除移除敌机列表
						enemys.remove(enemy);
						//再新加入一个敌机  否则如果所有敌机打完全部删光   没有敌机了
						enemys.add(new Enemy());
						
						//删除该子弹
						bullets.remove(bullet);
						//添加新的子弹进去
						bullets.add(new Bullet(heroX+(heroImage.getIconWidth()/2)-2,heroY-15));
						
						//创建一个爆炸图片的对象    爆炸图片的位置就是被击中敌机的位置
						Bomb bomb = new Bomb(enemy.getX(),enemy.getY());
						//添加到爆炸图片集合中
						bombs.add(bomb);						
					}
  • 在Bomb类中定义展示爆炸图片的方法。
	//绘制爆炸图片方法
	public void drawImage(Graphics g) {
    
    
		g.drawImage(bombImageIcon.getImage(), x, y, null);
		
	}
  • 在paint()方法中绘制全部爆炸图片。
		//绘制全部子弹
		for(int i=0;i<bullets.size();i++) {
    
    
			Bullet bullet = bullets.get(i);
			bullet.drawImage(g);
		}
		
		//绘制全部爆炸图片
		for(int i=0;i<bombs.size();i++) {
    
    
			Bomb bomb = bombs.get(i);
			bomb.drawImage(g);
		}

展示效果如下:
在这里插入图片描述
<5>使爆炸图片展示一段时间后消失。
在上一个效果中我们发现爆炸图片展示后一直停留在界面中并没有消失,这样以来整个屏幕都会被爆炸图片占满,因此我们在展示完爆炸图片后要使其消失。

  • 删除爆炸图片,我们的爆炸图片都需要删除,在init中添加代码:
//			//删除爆炸图片   否则爆炸图片一直停留在界面上
			for(int i=0;i<bombs.size();i++) {
    
    
				Bomb bomb = bombs.get(i);
				bombs.remove(bomb);

				}

得到效果图如下:
在这里插入图片描述

  • 在上述效果中我们发现原本显示的爆炸图片消失了,这是因为爆炸图片一画出来就被删除了,速度太快肉眼无法捕捉,因此我们利用count计数,使爆炸效果展示一段时间后再消失。

在Bomb类中增加count属性,同时定义其get和set方法

	//删除的次数
	private int count;
	
    ...
    ...
    
	//私有属性   爆炸图片次数的get方法
	public int getCount() {
    
    
		return count;
	}
    //私有属性    爆炸图片次数的set方法
	public void setCount(int count) {
    
    
		this.count = count;
	}

在Bomb类中定义move方法,对count进行增加。

	//多次调用move方法爆炸图片并不移动  只拖延时间   count达到一定次数之后再删除爆炸图片
	public void move() {
    
    
		count++;		
	}

多次调用move方法 当同一个爆炸图片对象的count次数超过8之后才删除该爆炸图片。

			//删除爆炸图片   否则爆炸图片一直停留在界面上
			for(int i=0;i<bombs.size();i++) {
    
    
				Bomb bomb = bombs.get(i);
				bomb.move();
				//多次调用move方法 当同一个爆炸图片对象的 count次数超过8之后才删除该爆炸图片   这样不会使爆炸图片转瞬即逝  视觉无法捕捉
				if(bomb.getCount()>8) {
    
    
					bombs.remove(bomb);
					}
				}

得到的效果如下:
在这里插入图片描述

6,分数的展示

<1>在GamePanel类中定义一个socre属性,每碰撞一次score+10.

	//英雄机左上角坐标   初始位置   之后根据鼠标拖拽改变位置
	int heroX=300;
	int heroY=400;
	int score=0;//记录分数   当子弹击中敌机则+10分
					//判断是否击中
					if(isHit(enemy,bullet)) {
    
    
						//击中   +10分
						score=score+10;
						
						//如果击中   则敌机消失    将敌机删除移除敌机列表
						enemys.remove(enemy);
						//再新加入一个敌机  否则如果所有敌机打完全部删光   没有敌机了
						enemys.add(new Enemy());
						
						//删除该子弹
						bullets.remove(bullet);
						//添加新的子弹进去
						bullets.add(new Bullet(heroX+(heroImage.getIconWidth()/2)-2,heroY-15));
						
						//创建一个爆炸图片的对象    爆炸图片的位置就是被击中敌机的位置
						Bomb bomb = new Bomb(enemy.getX(),enemy.getY());
						//添加到爆炸图片集合中
						bombs.add(bomb);						
					}

<2>在paint()函数中显示分数。

	//窗口对象自动调用   不需手动调用    绘制图片
	public void paint(Graphics g) {
    
    
		super.paint(g);
		
		//设置字体
		g.setFont(new Font("",Font.BOLD,30));//字体、字体样式、字体大小
		//在窗体上展示一个字符串   即得分情况
		g.drawString("当前得分:"+score, 10, 30);
		//绘制英雄机
		g.drawImage(heroImage.getImage(), heroX, heroY,null);

        ...
        ...

		}

得到效果图如下:
在这里插入图片描述

7,背景图片

<1>得到背景图片

	//英雄机图片                        图片路径
	ImageIcon heroImage = new ImageIcon("img/hero.png");
	//背景图片
	ImageIcon bgImage = new ImageIcon("img/background.png");

<2>在paint()方法中绘制背景图片

	//窗口对象自动调用   不需手动调用    绘制图片
	public void paint(Graphics g) {
    
    
		super.paint(g);
		//展示背景图片   注意与字体的顺序   若在字体后可能会掩盖字体
		g.drawImage(bgImage.getImage(), 0, 0, null);		
		//设置字体
		g.setFont(new Font("",Font.BOLD,30));//字体、字体样式、字体大小
		//设置字体颜色
		g.setColor(Color.WHITE);
		//在窗体上展示一个字符串   即得分情况
		g.drawString("当前得分:"+score, 10, 30);
		//绘制英雄机
		g.drawImage(heroImage.getImage(), heroX, heroY,null);

		}

基于以上我们就能得到博文开始的游戏效果啦!
完整代码如下:
百度网盘链接
提取码:0z7h

猜你喜欢

转载自blog.csdn.net/weixin_43722843/article/details/114140691