package com.shuai.plane;
import java.awt.Color;
import java.awt.Graphics;
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.lang.ref.PhantomReference;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
//JAVA中游戏面板:JPanel
/*
- 自定义游戏面板
- 1.写一个类继承JPanel
- 2.创建构造方法,初始化游戏面板的属性
- 画图的步骤
- 1.在类中定义图片,并取名
- 2.在构造方法中调用工具类(Photo)初始化图片
- 3.在画图方法paint中,画图片(paint是专门的画图方法)
- */
public class GamePanel extends JPanel{
//创建一个集合,用来存放敌机(敌机的大本营)
List<EnemyPlane> arrays = new ArrayList<>();
//创建一个集合,用来放英雄机的弹药
List<HeroFire> HFire = new ArrayList<>();
//创建一个集合,用来放敌机的弹药
List<EnemyFire> EnFire = new ArrayList<>();
//创建英雄机的对象
HeroPlane heroPlane = new HeroPlane();
//创建敌机的对象
EnemyPlane enemyPlane = new EnemyPlane();
//写一个线程,让程序跑起来
//写一个方法,启动线程
public void action() {
new Thread() {
public void run() {
//定义一个变量控制敌机出现的数量
int a = 0;
int b = 20;
//写一个死循环,让程序一直运行
while(true) {
//让敌机出现在面板上
if(a == b) {
MakeEP();
a = 0;
}
//让敌机移动
epMove();
//让英雄机子弹出现在面板上
if(a == 0) {
MakeHeroFire();
}
//让英雄机的子弹动起来
HeroFireMove();
//让敌机的子弹出现在面板上
//MakeEnemyFire();
//让敌机的子弹动起来
//EnemyFireMove();
//重绘布局(刷新页面)
repaint();
try {
this.sleep(30);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
a++;
}
}
}.start();
}
//写一个方法,让敌机移动
public void epMove() {
for(int i = 0; i < arrays.size(); i++) {
EnemyPlane enemyPlane = arrays.get(i);
enemyPlane.y += 10;
if(enemyPlane.y > 800) {
arrays.remove(enemyPlane);
}
//EnemyFire enemyFire = EnFire.get(i);
//enemyFire.y += 15;
}
}
//写一个方法,用来生产敌机,并把它放进集合中
void MakeEP() {
//创建敌机
EnemyPlane enemyPlane = new EnemyPlane();
//创建敌机的子弹
//EnemyFire enemyFire = new EnemyFire(enemyPlane.x, enemyPlane.y);
//把敌机加到大本营中(集合)
arrays.add(enemyPlane);
//EnFire.add(enemyFire);
}
//写一个方法,生产英雄机的弹药,并把它放进英雄机的弹药库
void MakeHeroFire() {
for(int i = 0; i < arrays.size(); i++) {
HeroFire heroFire = new HeroFire(heroPlane.x + 15, heroPlane.y, 0);
HFire.add(heroFire);
HeroFire heroFire2 = new HeroFire(heroPlane.x + 78, heroPlane.y, 2);
HFire.add(heroFire2);
HeroFire heroFire3 = new HeroFire(heroPlane.x + 48, heroPlane.y - 20, 1);
HFire.add(heroFire3);
}
}
//写一个方法让英雄机子弹移动
public void HeroFireMove() {
for(int i = 0; i < HFire.size(); i++) {
HeroFire heroFire = HFire.get(i);
if(heroFire.dir == 0) {
heroFire.x -= 1;
}
if(heroFire.dir == 2) {
heroFire.x += 1;
}
heroFire.y -= 10;
// if(heroFire.y < -heroFire.h) {
// HFire.remove(heroFire);
// }
}
}
//写一个方法生产敌机的弹药,并放进敌机的弹药库
void MakeEnemyFire() {
EnemyFire enemyFire = new EnemyFire(enemyPlane.x, enemyPlane.y);
EnFire.add(enemyFire);
}
//让敌机的子弹动起来
void EnemyFireMove() {
for(int i = 0; i < EnFire.size(); i++) {
EnFire.get(i).y += 15;
}
}
//定义背景图
BufferedImage bgp;
//创建构造方法,初始化面板
public GamePanel(GameFrame gameFrame) {
//设置面板的背景
//setBackground(Color.cyan);
//通过工具类(Photo)获取图片
bgp = Photo.getImage("/Img/bg1.jpg");
/*
* 使用鼠标监听器(固定格式)
*/
//1.创建鼠标适配器
//这是一个匿名内部类
MouseAdapter adapter = new MouseAdapter() {
//2.确定需要的鼠标监听事件
/*
* 鼠标监听事件:
* 1.mouseMoved();监听鼠标移动事件
* 2.mouseCliked();监听鼠标单击事件(鼠标按下后松开才会被监听,按下不会被监听)
* 3.mousePressed();监听鼠标按下去的事件(只要鼠标按下就会被监听)
* 4.mouseEntered();监听鼠标移入游戏界面的事件
* 5.mouseExited();监听鼠标移出游戏界面的事件
*
* MouseEvevt:鼠标的事件,记录鼠标做的事情
* */
@Override
//当鼠标在游戏界面移动时,会触发此方法
public void mouseMoved(MouseEvent e) {
//让英雄机跟着鼠标移动,就让英雄机的横纵坐标等于鼠标的横纵坐标
//获取鼠标的坐标(getX();获取鼠标的横坐标, getY();获取鼠标的纵坐标)
int mx = e.getX();
int my = e.getY();
//让英雄机移动到鼠标的位置(英雄机移动到鼠标的位置,是英雄机的行为,可以在英雄机中定义方法)
//此时英雄机依然不会移动,其实英雄机的坐标已经改变,但是没有把英雄机绘制到新的坐标,需要刷新页面
//heroPlane.move(mx, my);
heroPlane.x = mx - heroPlane.w/2;
heroPlane.y = my - heroPlane.h/2;
//刷新页面,将英雄机绘制到新的页面
//重新调用paint方法(re 表示重新调用)
repaint();
}
@Override
//当鼠标在游戏界面单击时,会触发此方法
public void mouseClicked(MouseEvent e) {
System.out.println("~~~~~~~鼠标单击了");
}
@Override
//当鼠标在游戏界面按下时,会触发此方法
public void mousePressed(MouseEvent e) {
System.out.println("!!!!!!!!鼠标被按下了");
}
@Override
//当鼠标移入游戏界面时,会触发此方法
public void mouseEntered(MouseEvent e) {
System.out.println("**************鼠标被移出");
}
@Override
//当鼠标移出游戏界面时,会触发此方法
public void mouseExited(MouseEvent e) {
System.out.println("**************鼠标被移入");
}
};
//3.将适配器加入到监听器中(固定格式)
addMouseListener(adapter);
addMouseMotionListener(adapter);
/*
* 使用键盘适配器(固定模式)
*/
// 1.创建键盘适配器
KeyAdapter key = new KeyAdapter() {
// 2.确定需要监听的键盘事件
@Override
//当键盘按下时,会触发此方法
public void keyPressed(KeyEvent e) {
//监听键盘的按键(每一个按键都对应有一个数字)
//获取按键的数字
int a = e.getKeyCode();
//KeyEvent.VK_?;是表示指定的按键对应的数字
if(a == KeyEvent.VK_A || a == KeyEvent.VK_LEFT) {
if((heroPlane.x -= 10) < -heroPlane.w/2) {
heroPlane.x = 0 - heroPlane.w/2;
}
}else if(a == KeyEvent.VK_D || a == KeyEvent.VK_RIGHT) {
if((heroPlane.x += 10) > 600 - heroPlane.w/2) {
heroPlane.x = 600 - heroPlane.w/2;
}
}else if(a == KeyEvent.VK_W || a == KeyEvent.VK_UP) {
if((heroPlane.y -= 10) < 0) {
heroPlane.y = 0;
}
}else if(a == KeyEvent.VK_S || a == KeyEvent.VK_DOWN) {
if((heroPlane.y += 10) > 800 - heroPlane.h) {
heroPlane.y = 800 - heroPlane.h;
}
}
//重绘,将英雄机画到新的位置上
repaint();
/*
if(e.getKeyChar() == 'a') {
if((heroPlane.x -= 10) < -heroPlane.w/2) {
heroPlane.x = 0 - heroPlane.w/2;
}
}
if(e.getKeyChar() == 'd') {
if((heroPlane.x += 10) > 600 - heroPlane.w/2) {
heroPlane.x = 600 - heroPlane.w/2;
}
}
if(e.getKeyChar() == 'w') {
if((heroPlane.y -= 10) < 0) {
heroPlane.y = 0;
}
}
if(e.getKeyChar() == 's') {
if((heroPlane.y += 10) > 800 - heroPlane.h) {
heroPlane.y = 800 - heroPlane.h;
}
}
//重绘,将英雄机画到新的位置上
repaint();
*/
}
};
// 3.将适配器加入到窗体的监听器中(加到面板的键盘监听器中是没有用的)
//可以在创建面板对象时,把窗体对象传过来,然后把键盘适配器添加到窗体的键盘监听器中
//需要面板的构造器添加参数,并指定类型
gameFrame.addKeyListener(key);
}
//专门画图的方法,重写于JComponent类(JPanel的父类)
//画图的方法:(画图时一定要注意图片的尺寸)
// 格式: g.drawImage(图片, 横坐标, 纵坐标, 宽度, 高度, 当转换了更多图片是要通知的对象)
// 画图时如果不设定图片的大小,图片显示就是原图的大小
@Override
public void paint(Graphics g) {
//g.drawImage(img, x, y, observer);
//横纵坐标是设置图片左上角的坐标,设置图片的大小
g.drawImage(bgp, 0, 0, 600, 800, null);
//在paint中画图是有顺序的,先画的在下面一层,先画的会被后画的覆盖
//给敌机画图
//已经把敌机放入集合中了,使用遍历绘制敌机
for(int i = 0; i < arrays.size(); i++) {
EnemyPlane enemyPlane = arrays.get(i);
g.drawImage(enemyPlane.img, enemyPlane.x, enemyPlane.y, enemyPlane.w, enemyPlane.h, null);
}
//给英雄机画图
g.drawImage(heroPlane.img, heroPlane.x, heroPlane.y, heroPlane.w, heroPlane.h, null);
//把英雄机的弹药画进面板
for(int i = 0; i < HFire.size(); i++) {
HeroFire heroFire = HFire.get(i);
g.drawImage(heroFire.img, heroFire.x, heroFire.y, heroFire.w, heroFire.h, null);
}
//把敌机的子弹画进面板中
// for(int i = 0; i < EnFire.size(); i++) {
// EnemyFire enemyFire = EnFire.get(i);
// g.drawImage(enemyFire.img, enemyFire.x, enemyFire.y, enemyFire.w, enemyFire.h, null);
// }
}
}
图一是运行时操作的画面,如果移动鼠标或者按着键盘不放(应该是飞机移动太快的时候),就会出现下面的画面,会一次出现多个子弹(我就的是鼠标移动时刷新的太快,但是线程里面是限制了时间的,难道是鼠标或者键盘和县线程里面重复刷新了???不会啊,求救啊!!!)。还有,截图代码中,如果把圈出来的判断代码放出来,游戏界面就会出现圈起来的样子(最左边的子弹向上走到一定高度就会变成两个,每次都是在那个高度,并且只有最左边的子弹会变化。)(我就的可能是集合删除后子弹的位置改变了,如果用LinkedList会不会好点?求救!!!)