小游戏制作——飞机大战
java 实现
耗时四天,通过借鉴学习别人,到自己写代码。比较费头发。但最终也还是肝了出来。本来还想弄一个 菜单栏,用该改变战机和子弹,但迫于时间有限。能先写到这里了。等过段时间,我再来完善它!
1.首先 我将英雄机,敌机,子弹,玩家。都分别当做对象来处理。单独写.java文件。
2. 写一个窗口(GUI编程)。存放所有的对象。并行多线程,因为敌机 英雄机 子弹,都需要同时运行。不断检测敌机是否被子击中,英雄机是否碰到敌机。所以:这些对象 都要继承 Thread 重写 run 方法
3. 事件监听器也有写,在英雄机碰到敌机时触发,弹出窗口Gameover 并且关闭所有的窗口。结束进程。
4. 因为 敌机和子弹,在窗口中都会同时出现多个。我使用了vector 集合用来存放对象。 敌机和子弹都会消失,使用集合的 remove 方法就可以。
vector 和 list 的区别:
vector:连续存储结构:vector是可以实现动态增长的对象数组,支持对数组高效率的访问和在数组尾端的删除和插入操作,在中间和头部删除和插入相对不易,需要挪动大量的数据。
它与数组最大的区别就是vector不需程序员自己去考虑容量问题,库里面本身已经实现了容量的动态增长,而数组需要程序员手动写入扩容函数进形扩容。
list:非连续存储结构:list是一个双链表结构,支持对链表的双向遍历。每个节点包括三个信息:元素本身,指向前一个元素的节点(prev)和指向下一个元素的节点(next)。
因此list可以高效率的对数据元素任意位置进行访问和插入删除等操作。由于涉及对额外指针的维护,所以开销比较大。
5. 对方向键 的键值 做出了解。 玩家通过方向键来操控英雄机。需要,switch 语句进行识别 玩家按了哪个键。但同时,英雄机的移动范围也是有限的。不能移出窗口。
6. 将所有的java文件 存放在同一目录下,来自同一个包。
其余的小点:我在代码中有注释。大家多加理解吧!不理解可以私信我哦!
GameFrame.java 文件 窗口文件
package VacationTest.PlaneFight.src;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
import java.util.Vector;
public class GameFrame extends JFrame {
//对英雄机和敌机作提前声明
HeroPlane heroplane;
EnemyPlane enemyPlane;
// 定义子弹集合
Vector<bullet> bullets = new Vector<>();
//敌机集合
Vector<EnemyPlane> enemys = new Vector<>();
GameFrame frame;
public GameFrame() {
frame = this ;// 后面 敌机的线程中要使用
// 创建英雄机
heroplane = new HeroPlane();
//启动英雄机线程
heroplane.start();
// 设置窗体的宽高
this.setSize(500, 760);
this.setTitle("飞机大战---小帅制作");
this.setResizable(false);//设置 用户是否可以调整窗口的大小 这里设置为 否
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);//设置窗口 相对于屏幕的位置,null 指:处于屏幕中央
// 窗口可见
this.setVisible(true);
// 新建线程,让 画笔 不断 重复
new Thread(new Runnable() {//内部类。
@Override
public void run() {
while (true) {
repaint();//不断重复画
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
//产生敌机的线程
new Thread(new Runnable(){
//产生随机数
Random r = new Random();
@Override
//重写run 方法
public void run(){
while(true){
EnemyPlane enemyPlane = new EnemyPlane(r.nextInt(500), 0,frame);// 随机产生敌机,并创建敌机对象
enemyPlane.start(); // 启动线程
//随机产生敌机
enemys.add(enemyPlane); // 将 敌机对象 添加到 enemy 集合中
try{
Thread.sleep(500);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}).start();
}
/**
* 在窗口上画图片,需要不断重复的画,使用API中 paint 画笔 重写其中的方法
*
* @param g
*/
public void paint(Graphics g) {
//System.out.println("绘制画板");
// 画背景
BufferedImage image = (BufferedImage) this.createImage(this.getSize().width, this.getSize().height);
// 高效缓存画笔
Graphics pen = image.getGraphics();
pen.drawImage(new ImageIcon("G:\\Users\\Administrator\\eclipse-workspace\\test\\src\\VacationTest\\PlaneFight\\Image\\Background.jpg").getImage(),0, 0, null);
// 画英雄机
pen.drawImage(heroplane.img, heroplane.x, heroplane.y, heroplane.width, heroplane.height, null);
// 飞机发射子弹
for (int i = 0; i < bullets.size(); i++) {
//System.out.println("bullet");
// 实例化 子弹对象 b
bullet b = bullets.get(i);
if (b.y > 0)
pen.drawImage(b.img, b.x, b.y -= b.speed, b.width, b.height, null);
else
// 子弹超出上边界 移除
bullets.remove(b);
}
//产生敌机
for (int i = 0; i < enemys.size(); i++) {
//实例化 敌机对象 ep
EnemyPlane ep = enemys.get(i);
if (ep.y < 760)
pen.drawImage(ep.img, ep.x, ep.y += ep.speed, ep.width, ep.height, null);
else
enemys.remove(ep);
}
// 生效
g.drawImage(image, 0, 0, null);
}
public static void main(String[] args) {
GameFrame frame = new GameFrame();
Player player = new Player(frame);
//给 玩家注册事件监听器
frame.addKeyListener(player);
}
}
HeroPlane.java 文件 英雄机文件
package VacationTest.PlaneFight.src;
import java.awt.Image;
import javax.swing.ImageIcon;
public class HeroPlane extends Thread {
int x = 230, y = 650;
int width = 50, height = 50;
int speed = 8;
Image img = new ImageIcon(
"G:\\Users\\Administrator\\eclipse-workspace\\test\\src\\VacationTest\\PlaneFight\\Image\\HeroPlane.png")
.getImage();
// 定义方向键标志 up down left right
boolean up, down, left, right;
// 两种 构造器
public HeroPlane() {
}
public HeroPlane(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public void run() {
while (true) {
// 实现 英雄机的 移动
if (up) {
if (y > 20) {
y -= speed;
} else {
y -= 0;
}
}
if (down) {
if (y < 700)
y += speed;
else
y += 0;
}
if (left) {
if (x > 10)
x -= speed;
else
x -= 0;
}
if (right) {
if (x < 440)
x += speed;
else
x += 0;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
EnemyPlane.java 文件 敌机文件
package VacationTest.PlaneFight.src;
import java.awt.Image;
import java.awt.Rectangle;
import javax.swing.*;
import VacationTest.PlaneFight.src.Gameover.MyFrame;// 导入 游戏结束的 包 自己写的java 文件
public class EnemyPlane extends Thread {
public GameFrame gf;
// 敌机的位置 大小 速度
int x, y;
int width = 50, height = 50;
int speed = 3;
Image img = new ImageIcon(
"G:\\Users\\Administrator\\eclipse-workspace\\test\\src\\VacationTest\\PlaneFight\\Image\\EmenyPlane.png")
.getImage();
// 构造器
public EnemyPlane(int x, int y, GameFrame gf) {
super();
this.x = x;
this.y = y;
this.gf = gf;
}
// 构造器
public EnemyPlane(int x, int y, int width, int height, GameFrame gf) {
super();
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.gf = gf;
}
// 重写run方法 在线程启动时 执行
public void run() {
while (true) {
if (hit()) {
// System.out.println("hit.............");
// 敌机被击毁后,速度变为0 图片更换为 爆炸图片
this.speed = 0;
this.img = new ImageIcon(
"G:\\Users\\Administrator\\eclipse-workspace\\test\\src\\VacationTest\\PlaneFight\\Image\\boom3.png")
.getImage();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 子弹击毁 移除敌机
gf.enemys.remove(this);
break;
}
// 敌机超出下边界 移除敌机
if (this.y > 760) {
gf.enemys.remove(this);
break;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (crash()) {
this.speed = 0;
this.img = new ImageIcon().getImage();
new MyFrame(); //实例化MyFrame对象
gf.enemys.remove(this);
gf.heroplane.img = new ImageIcon(
"G:\\Users\\Administrator\\eclipse-workspace\\test\\src\\VacationTest\\PlaneFight\\Image\\HeroPlaneBoom.png")
.getImage();
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//gf.heroplane.addActionListener(new broken());
System.exit(0);
}
}
}
// 检测碰撞
public boolean hit() {
// Swing技术中,已经实现相关算法
// 给出敌机的矩形
Rectangle myrect = new Rectangle(this.x, this.y, this.width, this.height);
Rectangle rect = null;// 给出子弹的矩形
for (int i = 0; i < gf.bullets.size(); i++) {
bullet b = gf.bullets.get(i);
rect = new Rectangle(b.x, b.y, b.width, b.height);
// 碰撞检测
if (myrect.intersects(rect)) {
return true;
}
}
return false;
}
// 检测 英雄机和 敌机碰撞
public boolean crash() {
Rectangle myrect = new Rectangle(this.x, this.y, this.width, this.height);
Rectangle rect2 = new Rectangle(gf.heroplane.x, gf.heroplane.y, gf.heroplane.width, gf.heroplane.height);
if (myrect.intersects(rect2)) {
return true;
}
return false;
}
}
bullet.java文件 子弹文件
package VacationTest.PlaneFight.src;
import java.awt.Image;
import javax.swing.ImageIcon;
public class bullet {
int x;
int y;
int width=30;
int height=40;
int speed = 15;
Image img = new ImageIcon("G:\\Users\\Administrator\\eclipse-workspace\\test\\src\\VacationTest\\PlaneFight\\Image\\bullet2.png").getImage();
public bullet(int x,int y){
this.x = x;
this.y = y;
}
public bullet(int x,int y ,int width , int height){
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
}
Player.java 文件 玩家文件
package VacationTest.PlaneFight.src;
import java.awt.event.*;
public class Player extends KeyAdapter{
//传入 frame 对象 也就是 整个窗体中的所有属性值 使用 对象名.方法 进行调用
GameFrame frame;
public Player(GameFrame frame){
this.frame = frame;
}
/**
* 定义一个玩家,操作键盘的上下左右。以及 发射子弹 空格
* 38 40 37 39 对应 上下左右
* 重写 keyPressed 和 keyReleased 方法 并使用监听器 判断玩家按哪个键
*/
public void keyPressed(KeyEvent e){
int keycode = e.getKeyCode();
//System.out.println(keycode);//利用该语句 可以 知道 键盘上的按键 值是多少。
switch(keycode){
case 38: frame.heroplane.up = true;break;
case 40: frame.heroplane.down = true;break;
case 37: frame.heroplane.left = true;break;
case 39:frame.heroplane.right = true;break;
case 32:addBullet();break;
}
}
public void keyReleased(KeyEvent e){
int keycode = e.getKeyCode();
switch(keycode){
case 38: frame.heroplane.up = false;break;
case 40: frame.heroplane.down = false;break;
case 37: frame.heroplane.left = false;break;
case 39:frame.heroplane.right = false;break;
}
}
// 发射子弹方法 在 键值为 32 时 触发
public void addBullet(){
frame.bullets.add(new bullet(frame.heroplane.x+15, frame.heroplane.y-20));
}
}
Gameover.java文件 游戏结束文件
package VacationTest.PlaneFight.src;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.WindowConstants;
public class Gameover{
static GameFrame frame;
public static class MyFrame extends JFrame { //创建新类,继承自JFrame
private static final long serialVersionUID = 1L;
public MyFrame() {
Container container = getContentPane(); //创建一个容器
container.setLayout(null);
//JLabel jl = new JLabel("这是一个JFrame窗体"); //在窗体中设置标签
//jl.setHorizontalAlignment(SwingConstants.CENTER); //将标签的文字至于标签中间的位置
//container.add(jl); //将标签添加到容器中
JButton jb = new JButton(""); //定义一个按钮
ImageIcon img = new ImageIcon("G:\\Users\\Administrator\\eclipse-workspace\\test\\src\\VacationTest\\PlaneFight\\Image\\Gameover.jpg");
img.setImage(img.getImage().getScaledInstance(350,150,0));
jb.setBounds(0, 0, 350, 150); //设置按钮的大小
jb.setIcon(img);
jb.addActionListener(new ActionListener() { //为按钮添加点击事件
@Override
public void actionPerformed(ActionEvent e){
try {
Thread.sleep(1500);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.exit(0);
}
});
container.add(jb);
setSize(360,180);
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);//设置窗口 相对于屏幕的位置,null 指:处于屏幕中央
}
}
}
在代码中使用的图片,以及素材,已经放在百度网盘里了。
注意: 使用的图片路径 要改。
https://pan.baidu.com/s/1L70YtfqrBg8iWqFxmxB1jA
提取码:p6o2