Java实现飞机大战游戏思路
这两天跟着视频的老师做了一遍飞机大战游戏,下面分享一下自己的思路,当作让自己复习一遍了哈哈哈~
——————————————————————————————————————————————————————————
简单思路
1 先得把游戏界面的窗体实现
2 实现窗体里面背景的内容
3 选择游戏是采用鼠标监听还是键盘监听
4 把战机图片调用方法画在背景上面
5 让键盘或者鼠标监听战机的轨迹
6 将子弹始发点坐标定在战机坐标前面
7 将敌机图片也画在背景上面
8 定义方法规定敌机的飞行速度和轨迹
9 定义方法判断子弹是否击中敌机及战机是否与敌机相撞
10 定义一个敌机被子弹击中则消失的方法
11 定义一个相撞后战机的血量减少,敌机消失的方法
12 当战机血量为0时游戏结束,点击鼠标重新开始
13 定义不同型号敌机的属性
14 添加游戏音效
以上步骤最难的就是第九步,需要好好理解坐标轴中两个图片的对应关系
——————————————————————————————————————————————————————————
成品如下图
下面是代码的应用说明:
这是一个窗体类
定义游戏窗体的一些细节,以及在主方法中运行其他类的方法。
/*
* 游戏窗体类
*/
public class GameFrame extends JFrame {
public static void main(String[] args) {
GameFrame frame = new GameFrame();
frame.setVisible(true);// 显示窗体
GamePanel panel =new GamePanel(frame);//方便调用键盘监听器
panel.action();//调用启动游戏的方法
frame.add(panel);
frame.setVisible(true);
//调用背景音乐类中的方法
String filepath = "D:\\javagame\\飞机大战\\img\\game_music.wav";
musicStuff musicObject = new musicStuff();
musicObject.playMusic(filepath);
}
//构造方法设置窗体
public GameFrame() {
setTitle("全民飞机大战");// 设置标题
setSize(512, 768);//设置窗体大小
setLocationRelativeTo(null);// 相对于屏幕左上角居中
setResizable(false);// 设置不允许玩家拖动界面
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭游戏时把进程也关掉
}
}
——————————————————————————————————————————————————————————
这是一个画布背景类,我的绝大多数方法都在这个类中定义。
public class GamePanel extends JPanel {
BufferedImage bg = null;
// new战机对象
hero hero = new hero();
// 建立敌机集合 不使用数组做容器是因为敌机数量未知,建立数组先要定义数组长度
List<Ep> eps = new ArrayList<Ep>();
// 建立子弹的弹药库,与敌机集合想法一样
List<Fire> fs = new ArrayList<Fire>();
// 定义分数
int socre;
boolean gameover;// 设置开关
int power = 1;// 火力值
/*
* 开始游戏的方法 建立一个线程
*/
public void action() {
new Thread() {
public void run() {
while (true) {
// if语句判断是否gameover
if (!gameover) {
epEnter();// 敌机进场
epMove();
// 发射子弹
shoot();
// 子弹移动
fireMove();
shootEp();
hit();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
repaint();// 刷新界面
}
}
/*
* 检测子弹是否打到敌机
*/
public void shootEp() {
// 遍历所有子弹
for (int i = 0; i < fs.size(); i++) {
Fire f = fs.get(i);
bang(f);
}
}
/*
* 判断子弹是否击中敌机
*/
private void bang(Fire f) {
for (int i = 0; i < eps.size(); i++) {
Ep e = eps.get(i);
if (e.shootBy(f)) {
e.hp--;
// 当敌机没hp,死亡
if (e.hp <= 0) {
if (e.type == 12) {
power++;
if (power > 3) {
hero.hp++;
power = 3;
}
}
eps.remove(e);// 移除敌机
fs.remove(f);// 移除子弹
}
// 增加分数
socre += 10;
if(socre%1000==0) {
Random rd = new Random();
int index = rd.nextInt(5) + 1;
bg = ImageUtil.getImage("/img/bg" + index + ".jpg");
repaint();// 刷新界面
hero.hp++;
}
}
}
}
}.start();
}
/**
* 检测敌机是否撞到了英雄机
*/
protected void hit() {
// 遍历敌机
for (int i = 0; i < eps.size(); i++) {
// 获取一个敌机
Ep e = eps.get(i);
// 如果敌机被英雄机撞上了
if (e.hitBy(hero)) {
// 1.删除敌机
eps.remove(e);
// 2.英雄机的血量减少
hero.hp--;
// 英雄机的火力恢复为1
power = 1;
// 3.增加分数
socre += 10;
// 4.当英雄机的血量减少到0时,游戏结束
if (hero.hp <= 0) {
// 游戏结束
gameover = true;
}
}
}
}
public void fireMove() {
for (int i = 0; i < fs.size(); i++) {
Fire f = fs.get(i);
f.move();
}
}
// 发射子弹
int fireindax = 0;// 记录执行的次数
public void shoot() {
fireindax++;
if (fireindax >= 10) {
if (power == 1) {
// 创建子弹
Fire fire3 = new Fire(hero.x + 45, hero.y - 20, 1);
// 将子弹加入到弹药库
fs.add(fire3);
} else if (power == 2) {
// 创建子弹
Fire fire1 = new Fire(hero.x + 15, hero.y, 1);
// 将子弹加入到弹药库
fs.add(fire1);
// 创建子弹
Fire fire2 = new Fire(hero.x + 75, hero.y, 1);
// 将子弹加入到弹药库
fs.add(fire2);
} else if (power == 3) {
// 创建子弹
Fire fire1 = new Fire(hero.x + 15, hero.y, 0);
// 将子弹加入到弹药库
fs.add(fire1);
// 创建子弹
Fire fire2 = new Fire(hero.x + 75, hero.y, 2);
// 将子弹加入到弹药库
fs.add(fire2);
// 创建子弹
Fire fire3 = new Fire(hero.x + 45, hero.y - 20, 1);
// 将子弹加入到弹药库
fs.add(fire3);
}
fireindax = 0;// 计算器
}
}
public void epMove() {
for (int i = 0; i < eps.size(); i++) {
Ep e = eps.get(i);
e.move();
}
}
/*
* 敌机入场方法 该方法在死循环中一直调用
*/
int indax = 0;
public void epEnter() {
indax++;
if (indax >= 20) {
Ep e = new Ep();
eps.add(e);// 把敌机不断加入到集合里面
indax = 0;
}
}
public GamePanel(GameFrame frame) {
setBackground(Color.BLACK);
bg = ImageUtil.getImage("/img/bg2.jpg");
// 使用鼠标监听器
// 1创建鼠标监听器
MouseAdapter md = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
// 点击鼠标时,会执行的代码
// 如果游戏结束了,点击屏幕时重新开始游戏
if (gameover) {
// 重新开始游戏
// 需要做些什么事情?
// 重新创建一个英雄机
hero = new hero();
// 重置游戏开关
gameover = false;
// 分数清0
socre = 0;
eps.clear();// 清空敌机集合
fs.clear();// 清空子弹集合
// 随机背景图
Random rd = new Random();
int index = rd.nextInt(5) + 1;
bg = ImageUtil.getImage("/img/bg" + index + ".jpg");
repaint();
}
}
// 2确定鼠标监听事件
@Override
public void mouseMoved(MouseEvent e) {
int mX = e.getX();
int mY = e.getY();
// 判断游戏是否结束,结束则不能移动
if (!gameover) {
hero.MovetoMouse(mX, mY);
}
repaint();// 刷新界面
}
};
// 将适配器加到监听器中
addMouseListener(md);
addMouseMotionListener(md);
// ------------------------------------------
// 使用键盘监听器
// KeyAdapter kd = new KeyAdapter() {
// @Override
// public void keyPressed(KeyEvent e) {
// int KeyCode = e.getKeyCode();
// if (KeyCode == KeyEvent.VK_UP || KeyCode == KeyEvent.VK_W) {
// hero.MoveUp();
// repaint();// 刷新界面
// } else if (KeyCode == KeyEvent.VK_DOWN || KeyCode == KeyEvent.VK_S) {
// hero.MoveDown();
// repaint();// 刷新界面
// } else if (KeyCode == KeyEvent.VK_LEFT || KeyCode == KeyEvent.VK_A) {
// hero.MoveLeft();
// repaint();// 刷新界面
// } else if (KeyCode == KeyEvent.VK_RIGHT || KeyCode == KeyEvent.VK_D) {
// hero.MoveRight();
// repaint();// 刷新界面
// }
// }
// };
// frame.addKeyListener(kd);// 键盘适配器要加到窗体监听器
}
@Override
public void paint(Graphics g) {
super.paint(g);
// draw有先后顺序,先画会被后画覆盖
g.drawImage(bg, 0, 0, null);
g.drawImage(hero.img, hero.x, hero.y, hero.w, hero.h, null);
// 遍历集合里面的所有敌机并显示出来
for (int i = 0; i < eps.size(); i++) {
Ep ep = eps.get(i);
g.drawImage(ep.img, ep.x, ep.y, ep.w, ep.h, null);
}
// 遍历集合里面的所有子弹并显示出来
for (int i = 0; i < fs.size(); i++) {
Fire fire = fs.get(i);
g.drawImage(fire.img, fire.x, fire.y, fire.w, fire.h, null);
}
// 画分数
g.setColor(Color.white);
g.setFont(new Font("楷体", Font.BOLD, 20));
g.drawString("分数: " + socre, 10, 30);
// 画英雄机的血量
for (int i = 0; i < hero.hp; i++) {
g.drawImage(hero.img, 470, 5+ i * 30, 30, 30, null);
}
// 当游戏结束时,显示Gameover
if (gameover) {
g.setColor(Color.red);
g.setFont(new Font("楷体", Font.BOLD, 35));
g.drawString("GAMEOVER!", 35, 300);
g.setColor(Color.green);
g.setFont(new Font("楷体", Font.BOLD, 20));
g.drawString("提醒你点击屏幕重新开始游戏", 40, 350);
}
}
}
由于这个画布类我写得有点乱,我会单独拿写一篇解释这个类中的方法
——————————————————————————————————————————————————————————
接下来时我定义的一个属性类,由于战机,敌机,子弹这三个飞行物有着共同的的属性,所以我把他们的属性抽了出来做了一个属性类供他们继承。
/*
* 提高代码的复用性
* 定义飞行物的属性
*/
public class FlyObject {
BufferedImage img;
// 定义飞行物的横纵坐标及宽高
int x, y, w, h;
}
——————————————————————————————————————————————————————————
接着是一个工具类,工具类中是获取图片url的方法,当其他类需要实现获取图片url地址的时,直接调用本类方法即可。
需要注意的一个点,工具类用静态方法定义较为方便。
1:静态方法可以通过类名直接调用,而不需要实例化类的对象。
2:公用的,所有对象共用。
/*
* 处理图片的工具类
*/
public class ImageUtil {
public static BufferedImage getImage(String path) {
try {
BufferedImage img = ImageIO.read(ImageUtil.class.getResource(path));
return img;
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
return null;
}
}
——————————————————————————————————————————————————————————
下面是一个战机类,定义战机的移动方法
public class hero extends FlyObject{ //继承属性类属性
int hp;//英雄机的血量
public hero() {
img = ImageUtil.getImage("/img/hero.png");
x = 200;
y = 500;
w = img.getWidth();
h = img.getHeight();
hp = 3;//初始血量
}
/**
* 英雄机移动到鼠标位置上的方法
* @param mX 鼠标的横坐标
* @param mY 鼠标的纵坐标
*/
public void MovetoMouse(int mX, int mY) {
x = mX - w / 2;
y = mY - h / 2;
}
//当用键盘监听时,判断战机是否移动出边界,规定让其不会飞出窗体外
// public void MoveUp() {
// if (y == 0) {
// y = 0;
// } else {
// y-=10;
// }
// }
//
// public void MoveDown() {
// if (y == 650) {
// y=650;
// } else {
// y += 10;
//
// }
// }
//
// public void MoveLeft() {
// if (x == -50) {
// x=-50;
// } else {
// x -= 10;
//
// }
// }
//
// public void MoveRight() {
// if (x == 440) {
// x=440;
// } else {
// x += 10;
//
// }
// }
/**
* 向上移动的方法
*/
public void moveUp(){
y -= 10;
}
/**
* 向下移动的方法
*/
public void moveDown(){
y += 10;
}
/**
* 向左移动的方法
*/
public void moveLeft(){
x -= 10;
}
/**
* 向右移动的方法
*/
public void moveRight(){
x += 10;
}
}
——————————————————————————————————————————————————————————
下面是一个子弹类,与战机类作用相似
public class Fire extends FlyObject { //继承属性类属性
int dir;//定义子弹的飞行轨迹 0左上角飞 1中间飞 2右边飞
/**
* 构造方法:初始化子弹
* hx :英雄机的横坐标
* hy :英雄机的纵坐标
* dir:子弹移动的方向
*/
public Fire(int hx,int hy,int dir) {
img=ImageUtil.getImage("/img/fire.png");
//确定子弹大小
w=img.getWidth()/4;
h=img.getHeight()/4;
//确定子弹位置(在英雄机上面)
x=hx;
y=hy;
this.dir=dir;
}
/**
* 子弹移动的方法
*/
public void move() {
if(dir==0) {
x-=1;
y-=10;
}else if(dir==1) {
y-=10;
}else if(dir==2) {
x+=1;
y-=10;
}
}
}
——————————————————————————————————————————————————————————
下面是敌机类,我把判断敌机是否被击中的方法也写在了这个类中,大家画坐标轴好好理解一下如何判断是否碰撞。
public class Ep extends FlyObject { //继承属性类属性
int sp;// 定义速度
int hp;// 敌机的血量
int type;//定义敌机类型
/**
* 构造器:给敌机定型
*/
public Ep() {
Random rd = new Random();
// 生成一个随机数,用来选取图片
int index = rd.nextInt(15) + 1;
type =index;
// 三目运算符
String path = "/img/ep" + (index < 10 ? "0" : "") + index + ".png";
System.out.println(path);
img = ImageUtil.getImage(path);
w = img.getWidth();
h = img.getHeight();
x = rd.nextInt(512 - w);
y = -h;
sp = 17 - index;// 设置速度
// 设置血量
hp = index;
}
/**
* 敌机移动的方法
*/
public void move() {
if(type==5) { //类型5的敌机往左下飞
y += sp;
x -=5;
}else if(type==6) { //类型6的敌机往右下飞
y += sp;
x+=5;
}else if(type==15) { //加快类型15敌机的飞行速度
y += 14;
}
y += sp;
}
// 判断敌机是否被击中
public boolean shootBy(Fire f) {
boolean hit = x <= f.x + f.w && x >= f.x - f.w && y <= f.y + f.h && y >= f.y - h;
return hit;
}
//判断敌机与战机是否相撞
public boolean hitBy(hero f) {
boolean hit = x <= f.x + f.w && x >= f.x - w && y <= f.y + f.h && y >= f.y - h;
return hit;
}
}
——————————————————————————————————————————————————————————
下面是最后一个音乐类,负责循环播放背景音乐
public class musicStuff {
void playMusic(String musicLocation) {
try {
File musicPath = new File(musicLocation);
if (musicPath.exists()) {
AudioInputStream audioInput = AudioSystem.getAudioInputStream(musicPath);
Clip clip = AudioSystem.getClip();
clip.open(audioInput);
clip.start();
clip.loop(Clip.LOOP_CONTINUOUSLY);
} else {
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
——————————————————————————————————————————————————————————
这就是我这两天做的一个小东西了,跟着做了一遍发现复习了好多的基础知识,还是很不错的。
大家有啥疑问也可以留言~
下一篇: 分享在学习过程中体会到的编程思想