package ui;
import javax.swing.*;
/**
-
游戏窗体 java窗体类JFrame
*/
public class GameFrame extends JFrame {public GameFrame() {
setTitle(“飞机大战”);setSize(500,700); setLocationRelativeTo(null); setResizable(false); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
GameFrame frame = new GameFrame();
GamePanel panel = new GamePanel(frame);//方便面板中添加键盘监听器
//启动游戏的方法
panel.action();
//报错是因为GamePanel构造器没有添加GameFrame frame
frame.add(panel);
frame.setVisible(true);
}
}
package ui;
import com.sun.xml.internal.ws.api.addressing.AddressingVersion;
import jdk.nashorn.internal.ir.IfNode;
import javax.swing.;
import java.awt.;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.StreamCorruptedException;
import java.nio.file.Path;
import java.security.spec.ECParameterSpec;
import java.time.Year;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
-
java 游戏面板 JPanel
*/
public class GamePanel extends JPanel{
BufferedImage bg;//背景图Hero hero = new Hero();//创建英雄机
//Ep ep = new Ep();//创建单个敌机
//创建敌机大本营(所有敌机都在这里出现)
List eps = new ArrayList();//集合//创建英雄机子弹仓库 也可以在hero.java里面创建
List fs = new ArrayList();//在去下面action里面 控制子弹活动//定义分数
int score;//游戏的开关
boolean gameover;//游戏开始时,gameover为false;游戏结束时gameover为true//设置英雄机的火力 1为一排子弹
int power = 1;//*****用(*)表示后加的程序/**
-
开始游戏的方法
*/
public void action(){//这个最后要到JFrame里被调用
//创建并启动一个线程 控制游戏场景中物体活动
//固定格式
//new Threed(){public void run(){…线程需要做的事情}}.start();
new Thread(){
public void run(){
//写个死循环 让游戏一直运行
while (true){//这里关闭游戏时 加入的if程序
if(!gameover){
//调用敌机入场的方法
epEnter();//在下面创建一个方法就不会报错,创建快捷键,点epEnter()在alt+回车,提示里点!,在选择GamePanel 下面成功创建
//调用让敌机动起来的方法
epMove();//在下面创建一个方法就不会报错
//发射子弹
shoot();//在下面创建一个方法就不会报错 创建快捷键,点shoot()在alt+回车,提示里点!,在选择GamePanel 下面成功创建//子弹移动的方法 fireMove();//在下面创建一个方法就不会报错 //判断子弹是否打到敌机 shootEp();//在下面创建一个方法就不会报错,创建快捷键,点shootEp()在alt+回车,提示里点!,在选择GamePanel 下面成功创建 //判断敌机是否撞到英雄机 hit打击 hit();//创建方法同上 } //每执行一次 休息一会 即让线程休眠 毫秒数 //捕获异常 try { Thread.sleep(30); } catch (InterruptedException e) { e.printStackTrace(); } //刷新页面 repaint(); } }
}.start();
}
//检测敌机是否撞到英雄机
private void hit() {
//遍历敌机
for (int i=0;i<eps.size();i++){
//获取敌机
Ep e = eps.get(i);
//如果撞到敌机 hitBy()会报错 把它创建在Ep敌机类里面
if (e.hitBy(hero)){
//1:删除敌机 remove删除
eps.remove(e);
//2:英雄机的血量减少
hero.hp --;
//英雄机被撞 英雄机火力为1;
power = 1;//*****用(*)表示后加的程序
//3:增加分数
score += 200;
//4:当英雄机的血量为0时,游戏结束
if (hero.hp<=0){
gameover = true;//游戏结束 在下面画出游戏结束画面
}}
}
}
//检测子弹是否打到敌机 shoot射击
private void shootEp() {
//遍历所以呢子弹
for (int i = 0; i<fs.size();i++){
//获取每一颗子弹
Fire f = fs.get(i);
//每拿一颗子弹就判断一下这颗子弹是否击中了敌机,bang重击
bang(f);//出现报错,点 bang(f);在alt+回车 点!,下面创建成功}
}
//判断一下这颗子弹是否击中了敌机
private void bang(Fire f) {
//遍历所有的敌机
for (int i = 0;i<eps.size();i++){
//获取每一个敌机
Ep e = eps.get(i);
//判断这个子弹是否击中敌机 ; e 表示敌机 f表示子弹;shootBy通过射击
出现报错,点shootBy(f),alt+回车,选!,再选EP类
if (e.shootBy(f)){
if (e.type !=15){//*****用()表示后加的程序
//如果敌机被子弹击中
//1:让敌机的血量减少
e.hp --;
//当敌机血量减少到0时
if (e.hp<=0){
//判断敌机是否是道具机//*****用()表示后加的程序
if (e.type==12){//*****用()表示后加的程序
//让英雄机子弹变双排
power++;//*****用()表示后加的程序
//如果power值>3 的时候 增加心理
if (power>3){
hero.hp++;//*****用()表示后加的程序
power = 3;//*****用()表示后加的程序, 在去shoot()判断
}} //2:敌机消失即从敌机大本营删除;remove删除 eps.remove(e); //3:增加打掉敌机的分数 score += 100; } //4:删除子弹即从子弹库中删除子弹 fs.remove(f); //3:增加打掉敌机的分数 //score += 100;//把这个放到前面if里 } }
/* //如果敌机被子弹击中
//1:让敌机的血量减少
e.hp --;
//当敌机血量减少到0时
if (e.hp<=0){
//判断敌机是否是道具机//*****用()表示后加的程序
if (e.type==12){//*****用()表示后加的程序
//让英雄机子弹变双排
power++;//*****用()表示后加的程序
//如果power值>3 的时候 增加心理
if (power>3){
hero.hp++;//*****用()表示后加的程序
power = 3;//*****用(*)表示后加的程序, 在去shoot()判断
}} //2:敌机消失即从敌机大本营删除;remove删除 eps.remove(e); //3:增加打掉敌机的分数 score += 100; } //4:删除子弹即从子弹库中删除子弹 fs.remove(f); //3:增加打掉敌机的分数 //score += 100;//把这个放到前面if里 }*/ }
}
//子弹动移动方法
protected void fireMove(){
//每颗子弹移动起来 用遍历
for (int i = 0;i<fs.size();i++){
Fire fire = fs.get(i);//获取每一课子弹
//让子弹移动
fire.move();//在子弹Fire类里面创建一个方法就不会报错
}
}
//定义一个计数器 控制子弹速度 shoot执行次数
int findex = 0;
//发射子弹的方法
protected void shoot(){
findex++;
if (findex>=20){//如果感觉打不死就把数据调小,发射子弹的速度越快
//1表示单排子弹 2为双排子弹 3为3排子弹
if (power1){//*****用(*)表示后加的程序
Fire fire1 = new Fire(hero.x+25,hero.y-20,1);//中间子弹突出一些
fs.add(fire1);//然后在下面画出子弹
}else if (power2){//*****用(*)表示后加的程序
Fire fire = new Fire(hero.x,hero.y,1);
//将子弹加入弹药库
fs.add(fire);//然后在下面画出子弹
Fire fire2 = new Fire(hero.x+50,hero.y,1);
//将子弹加入弹药库
fs.add(fire2);//然后在下面画出子弹}else {//*****用(*)表示后加的程序 Fire fire = new Fire(hero.x,hero.y,0); //将子弹加入弹药库 fs.add(fire);//然后在下面画出子弹 //发射多颗子弹 Fire fire1 = new Fire(hero.x+25,hero.y-20,1);//中间子弹突出一些 //将子弹加入弹药库 fs.add(fire1);//然后在下面画出子弹 Fire fire2 = new Fire(hero.x+50,hero.y,2); //将子弹加入弹药库 fs.add(fire2);//然后在下面画出子弹 }
/* //创建子弹
//Fire fire = new Fire();//这里报错是因为Fire.java里面已经有有参构造器即(int hx,int hy),方法1:在Fire.java
//里面创建无参构造器 2:或者在Fire()里面加参数 如下:
Fire fire = new Fire(hero.x,hero.y,0);
//将子弹加入弹药库
fs.add(fire);//然后在下面画出子弹
//发射多颗子弹
Fire fire1 = new Fire(hero.x+25,hero.y-20,1);//中间子弹突出一些
//将子弹加入弹药库
fs.add(fire1);//然后在下面画出子弹
Fire fire2 = new Fire(hero.x+50,hero.y,2);
//将子弹加入弹药库
fs.add(fire2);//然后在下面画出子弹*/findex = 0;//重新开始记录 }
}
/**
-
敌机动起来的方法
*/
protected void epMove(){
//遍历所有敌机
for (int i = 0;i<eps.size();i++){
//获取每一个敌机
Ep e = eps.get(i);
//让敌机动起来
e.move();//在敌机EP类里面创建一个方法就不会报错}
}
//敌机入场的方法;此方法在死循环中调用,所以该方法会一直重复执行
//解决敌机出现频率太快问题
int index = 0;//记录方法执行的次数即每隔一段时间释放一个敌机protected void epEnter(){
//记录执行的次数
index ++;
if (index>=20){
//1:创建敌机
Ep e = new Ep();
//2:将敌机加入到敌机大本营即上面的集合里
eps.add(e);
//将index重新归零
index = 0;
}
}public GamePanel(GameFrame frame) {
setBackground(Color.cyan);
bg = App.getImg("/img/bg2.jpg");
//使用鼠标监听器的格式(固定格式)
//1:创建鼠标适配器
MouseAdapter adapter = new MouseAdapter(){
//2:确定需要监听的鼠标事件
//鼠标的事件
// 1:mouseMoved() 监听鼠标移动事件
// 2:mousecliked() 监听鼠标单击事件
// 3:mousePressed() 监听鼠标按下去事件
// 4:mouseEntered() 监听鼠标移入游戏界面事件
// 5:mouseExited() 监听鼠标移出游戏界面事件//游戏结束 重新开始点击事件;打出mousecliked(),按提示操作 @Override public void mouseClicked(MouseEvent e) { //super.mouseClicked(e); //点击鼠标时,会执行的代码, //如果游戏结束了,点击屏幕时,重新开始游戏 if (gameover){ //重新开始游戏,需要做些什么事情 //1:重新创建英雄机,即重新调用英雄机 hero = new Hero(); //2:重置游戏开关 gameover = false; //分数归零 score = 0; //清空敌机集合 防止重新开始页面有上次游戏敌机存在 eps.clear();//*****用(*)表示后加的程序 //在清空子弹集合 fs.clear();//*****用(*)表示后加的程序 //随机背景图 Random rd = new Random();//*****用(*)表示后加的程序 //index 指标 img里面有5张背景图片 int index = rd.nextInt(5)+1;//*****用(*)表示后加的程序 //读取图片 bg = App.getImg("/img/bg"+index+".jpg");//*****用(*)表示后加的程序 //刷新页面 repaint(); } } //mouseMoved用法 打出mouseMoved在按alt+回车.再提示里面找 //MouseEvent e 监视鼠标事件 @Override public void mouseMoved(MouseEvent e) { //super.mouseMoved(e);这个可以不用 //当鼠标在游戏界面移动时会触发的方法 // System.out.println("移动");测试用的 //让英雄机的横纵坐标等于鼠标的横纵坐标 int mx = e.getX(); int my = e.getY(); //如果游戏没有结束 if (!gameover){ //让英雄机移动到鼠标的位置上,调用英雄机 hero.moveToMouse(mx,my); } //刷新界面 否则飞机表面上看不出移动 repaint(); } }; //3:将鼠标适配器加入到监听器中 addMouseListener(adapter); addMouseMotionListener(adapter);//镜像 //使用键盘监听器(固定格式) //1:创建键盘适配器 KeyAdapter kd = new KeyAdapter(){ //2:确定需要监听的键盘事件 //先打keyPressed 再alt+回车 在提示里面找 @Override public void keyPressed(KeyEvent e) { super.keyPressed(e); //当按下键盘的按键时,会触发的方法 //System.out.println("键盘运行了");//测试键盘是否运行 //监听键盘;每一个按键都对应有一个数字 //获取键盘上按键的数字 int keyCode = e.getKeyCode(); //System.out.println(keyCode);//可以测试按键按下的反应 //如果按上键 向上移动 if (keyCode==KeyEvent.VK_UP){ hero.moveUP(); //如果按下键 向下移动 }else if (keyCode==KeyEvent.VK_DOWN){ hero.moveDown(); }else if(keyCode==KeyEvent.VK_LEFT){ hero.moveLeft(); }else if(keyCode==KeyEvent.VK_RIGHT){ hero.moveRight(); } repaint();//重绘即刷新 } }; //3:将键盘适配器加到窗体的键盘监听器中,现在需要在框架中加入frame frame.addKeyListener(kd);//加frame调用
}
/**
-
专用画图方法
-
Graphics g 画笔
-
paint方法:先打出paint出来 按alt+回车
-
@param g
*/
@Override
public void paint(Graphics g) {
super.paint(g);
//画面板图片
g.drawImage(bg,0,0,500,700,null);
//画英雄机s
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(“分数:”+score,10,30);//score得分 字体位置
//画英雄就的血量
for (int i=0;i<hero.hp;i++){
//画血量图 hero.img英雄机图片 100+i35英雄机3个飞机每一个的位置(3是在Hero()预定的)
//5是英雄机的y轴起始位置 30是飞机的大小
g.drawImage(hero.img,150+i35,5,30,30,null);
}
//当游戏结束时 画出GameOver(游戏结束)
if (gameover){
g.setColor(Color.red);
g.setFont(new Font(“楷体”,Font.BOLD,50));//楷体,字体加粗,大小50像素
g.drawString(“游戏结束! 菜鸟!”,60,300);//游戏结束 返回界面设置 g.setColor(Color.green); g.setFont(new Font("楷体",Font.BOLD,50)); g.drawString("点击屏幕重新开始",40,350);
}
}
} -
package ui;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
- 处理图片的工具类 App
*/
public class App {
public static BufferedImage getImg(String path){
try {
BufferedImage img = ImageIO.read(App.class.getResource(path));
return img;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
package ui;
import java.awt.image.BufferedImage;
//英雄机
public class Hero extends FlyObject{
//Hero继承了FlyObject
int hp;//英雄机血量
public Hero() {
img = App.getImg("/img/hero.png");
x = 200;
y = 550;
w = img.getHeight()/2;
h = img.getWidth()/2;
hp = 3;
}
//英雄机移动到鼠标位置上的方法
public void moveToMouse(int mx,int my){
x = mx-w/2;
y = my-h/2;
}
//向上移动
public void moveUP(){
y -= 10;
}
//向下移动
public void moveDown(){
y += 10;
}
//向左移动
public void moveLeft(){
x -= 10;
}
//向右移动
public void moveRight(){
x += 10;
}
}
package ui;
//敌机
import java.awt.image.BufferedImage;
import java.util.Random;
/**
*敌机
*1:敌机数量很多 java中存放多个数据,1)使用数组,缺点数组必须确定长度,2)使用集合 这里用集合好些
*2:敌机的数量是未知的
*/
public class Ep extends FlyObject {
int hp;//敌机的血量
//Ep 继承了FlyObject
int sp ;//敌机出现的速度
int type;//敌机的类型//*****用(*)表示后加的程序
//构造器:给敌机定型
public Ep() {
//定义一个随机类
Random rd = new Random();
//img = App.getImg("/img/ep02.png");//确定敌机显示的固定图片
//随机产生敌机图片 如果读取图片报错显示input==null 表示图片路径错误
int index = rd.nextInt(15)+1;
//System.out.println("/img/ep0"+index+".png");//检查原因
//设置敌机的类型
type = index;//*****用(*)表示后加的程序 在去GameePanel里找到bang(Fire f),判断敌机是否是道具机
//三目运算符
//格式:条件表达式?值1:值2;表示如果条件表达式成立为true就取值1,否则取值2
//如果index的值小于10就取0,否则就取空;
//注意(index<10?"0":"")这个括号必须有 否则类型不同会报错
String path = "/img/ep"+(index<10?"0":"")+index+".png";
System.out.println(path);
img = App.getImg(path);
// x = 100;//位置固定
// y = 50;//位置固定
w = img.getWidth();
h = img.getHeight();
//定义随机 nextlnt(num) 表示会在[0,num]之间随机生成一个整数
x = rd.nextInt(500-w);//减去敌机宽度
y = -h;//减去敌机高度
//设置速度
//sp = index;//这个相等 代表飞机越大速度越大
//采取相反的动作 飞机越小速度越快
sp = 17-index;//因为图片是1到16个
}
/**
* 敌机移动的方法
* 敌机向下移动
*/
public void move(){
//改变敌机的飞行方向 如5号敌机
if (type==5){//*****用(*)表示后加的程序
x -= 5;
y += sp;
}else if (type==6) {
x += 5;
y += sp;
}else if (type==15){
//如果导弹 先去bang(Fire f)判断是否是导弹
y += 20;
}else {
/* //y += 10;//敌机出现的速度都一样
//改变敌机出现的速度 变成随机
Random rd = new Random();
y += rd.nextInt(10)+1;//随机速度最少是1,出现敌机停顿现象,是因为每随机一次
//就会移动一次,速度在变化 ,重新在上面定义一个敌机的速度
*/
} y += sp;
}
//判断敌机是否被子弹击中
public boolean shootBy(Fire f) {//出现报错 里面加 return false;
boolean hit = x<=f.x+f.w && x>=f.x-w &&
y<=f.y+f.h && y>=f.y-h;
return hit;//把false换成hit 即返回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;
}
}
package ui;
/**游戏场景中飞行物类(英雄机 ,敌机,子弹等)
- 在开发中,常会将具有相同特点的类放在一起,将这些相同的特点抽离出来,形成一个父类
*/
import java.awt.image.BufferedImage;
//飞行物父类
public class FlyObject {
//飞行物的图片
BufferedImage img;
//飞行物的横纵坐标及大小
int x;
int y;
int w;
int h;
}
package ui;
/**
- 子弹类 属于飞行物
*/
public class Fire extends FlyObject{
//子弹当前移动的方向
int dir;//dir=0,就往左上角 dir=1 就往中间,dir =2 就往右上角
//构造方法:初始化子弹 hx,hy英雄机的横纵坐标
/*public Fire() {
}*/
public Fire(int hx, int hy,int dir) {//加入dir子弹移动方法 前面GamePanel里shoot()
//会报错 添加0,1,2
//确定子弹的图片
img = App.getImg("/img/fire.png");
//确定子弹的大小
w = img.getWidth()/4;//改变子弹形状大小
h = img.getHeight()/4;
//确定子弹的位置(初始位置在英雄机上)
x = hx+10;//加10 是校正子弹在飞机出发的位置
y = hy;
//上面创建好后去GamePanel里面创建子弹库
this.dir = dir;//如果参数和构造器的名字一样时,要加this区分开
}
public void move(){
//y -= 10;//这是统一一个方向
//改变子弹发射方向
if (dir==0){
x -= 2;//用10 子弹发射角度太大 改成2
y -= 10;
}else if (dir==1){
//x = 0;//这个不用写
y -= 10;
}else {
x += 2;
y -= 10;
}
}
}