1. 坦克大战08版
1.1 增加功能 654
1.游戏开始时,播放经典的坦克大战音乐, [思路,使用一个播放音乐的类,即可] 654
2.修正下文件存储位置 654
3.处理文件相关异常=》提示代码的健壮性 654
代码在com.stulzl.tankgame08.包中
测试TankGame08
package com.stulzl.tankgame08;
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Scanner;
//主方法 572
public class TankGame08 extends JFrame {
//定义我们的MyPanel
MyPanel mp = null;
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
TankGame08 tankGame01 = new TankGame08();//调用显示结果
}
//构造器设置窗口信息
public TankGame08(){
System.out.println("请输入选择 1:新游戏 2:继续上局游戏");
String key = scanner.next();
mp = new MyPanel(key);//初始化面板,这个key是把用户选择传进MyPanel进行判断选择
//将mp放入到Thread 并启动
Thread thread = new Thread(mp);
thread.start();//启动MyPanel线程
this.add(mp);//将面板添加到窗口
this.setSize(1300,950);//设置窗口大小
this.addKeyListener(mp);//让JFrame监听mp
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭程序
this.setVisible(true);//显示窗口
//在JFrame中增加相应方法监听关闭窗口的处理 650
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Recorder.keepRecord();//调用保存记录方法
System.exit(0);//这个System的方法不必纠结,可以理解成就是起到关闭左右
}
});
}
}
父类坦克Tank
package com.stulzl.tankgame08;
public class Tank {//父类坦克
private int x;//坦克的横坐标
private int y;//坦克的纵坐标
private int direct;//坦克方向0上 1右 2下 3左 577
private int speed = 1;//控制坦克速度
boolean isLive = true;//我们坦克存活标志
//坦克上右下左移动方法 577
public void moveUp(){//向上移动
y-=speed;
}
public void moveRight(){//向右移动
x+=speed;
}
public void moveDown(){//向下移动
y+=speed;
}
public void moveLeft(){//向左移动
x-=speed;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public int getDirect() {
return direct;
}
public void setDirect(int direct) {
this.direct = direct;
}
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
我们的坦克Hero
package com.stulzl.tankgame08;
import java.util.Vector;
public class Hero extends Tank {//我们的坦克,设置坦克坐标并且开启Shot射击线程
//我们坦克存活标志isLive直接写到父类中,方便
//定义一个Shot对象,表示射击(线程)
Shot shot = null;
//可以发射多颗子弹 607
Vector shots = new Vector<>();
//构造器
public Hero(int x, int y) {
super(x, y);
}
//射击方法 600
public void shortEnemyTank(){
//最多发射5颗子弹 607
// if(shots.size()==5){
// return;
// }
//创建一个Shot对象,根据Hero坦克的位置 方向
switch(getDirect()){
case 0://向上
shot = new Shot(getX()+20,getY(),0);//因为射击Shot线程有有参构造器,所以要传值
break;
case 1://向右
shot = new Shot(getX()+60,getY()+20,1);
break;
case 2://向下
shot = new Shot(getX()+20,getY()+60,2);
break;
case 3://向左
shot = new Shot(getX(),getY()+20,3);
break;
}
//把新创建的shot放入到shots的Vector集合中
shots.add(shot);
//启动Shot线程,因为Shot实现了Runnable接口,所以直接调用即可
Thread thread = new Thread(shot);
thread.start();//启动
}
}
敌人的坦克EnemyTank
package com.stulzl.tankgame08;
import java.util.Vector;
public class EnemyTank extends Tank implements Runnable{//敌人的坦克
//在敌人坦克类,使用Vector 保存多个Shot子弹
Vector shots = new Vector<>();
boolean isLive = true;//敌人坦克存活
//增加成员EnemyTank 可以得到敌人坦克的Vector 646
//分析
//1. Vector 在
Vector enemyTanks = new Vector<>();
//这里提供一个方法,可以将MyPanel 的成员(就是我们在MyPanel定义的敌人坦克集合) 646
// Vector enemyTanks = new Vector<>();
//设置到 EnemyTank中得(就是我们在EnemyTank中定义的集合) 的成员 enemyTanks
//这里解释为什么这么做,因为我们目的是防止敌人坦克重叠,就必须得到每一两敌人坦克的坐标,但是呢,敌人坦克
//的定义没在EnemyTank类中定义,而是在MyPanel中定义的敌人坦克集合,所以我们就要通过一个方法来得
//到MyPanel中的定义的敌人坦克集合,得到了集合后还要接受,所以就在EnemyTank中有定义了一个同类型得集合
//用来接收得到的敌人坦克集合
public void setEnemyTanks(Vector enemyTanks) {
this.enemyTanks = enemyTanks;
}
//编写方法,判断当前的这个敌人坦克,是否和 enemyTanks 中的其他坦克发生的重叠或者碰撞 646-647
public boolean isTouchEnemyTak(){
//判断当前敌人坦克(this)方向
switch(this.getDirect()){//这里获取的方向是当前去和其他敌人坦克比较的敌人坦克方向
//经过笔记分析,我们 分为8中情况(4大类)分别比较
case 0://向上
//遍历敌人坦克集合,让当前这个敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
//从Vector中取出一辆敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
//不和自己比较
if(enemyTank!=this){//不是自己则比较
//如果敌人坦克向上/下
//分析
//1. 如果敌人坦克是上/下 x的范围 [enemyTank.getX(), enemyTank.getX() + 40]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 60]
if(enemyTank.getDirect() == 0 || enemyTank.getDirect()==2){
//注意要判断左右两个点
//2. 当前坦克 左上角的坐标 [this.getX(), this.getY()]
//在这个范围内就代表碰撞了
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+40
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+60){
return true;
}
//3. 当前坦克 右上角的坐标 [this.getX() + 40, this.getY()]
if(this.getX()+40> enemyTank.getX()
&& this.getX()+40< enemyTank.getX()+40
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+60){
return true;
}
}
//如果敌人坦克向右/左
//分析
//1. 如果敌人坦克是右/左 x的范围 [enemyTank.getX(), enemyTank.getX() + 60]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 40]
if(enemyTank.getDirect() == 1 || enemyTank.getDirect()==3){
//2. 当前坦克 左上角的坐标 [this.getX(), this.getY()]
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+60
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+40){
return true;
}
//3. 当前坦克 右上角的坐标 [this.getX() + 40, this.getY()]
if(this.getX()+40> enemyTank.getX()
&& this.getX()+40< enemyTank.getX()+60
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+40){
return true;
}
}
}
}
break;
case 1://向右
//遍历敌人坦克集合,让当前这个敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
//从Vector中取出一辆敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
//不和自己比较
if(enemyTank!=this){//不是自己则比较
//如果敌人坦克向上/下
//分析
//1. 如果敌人坦克是上/下 x的范围 [enemyTank.getX(), enemyTank.getX() + 40]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 60]
if(enemyTank.getDirect() == 0 || enemyTank.getDirect()==2){
//注意要判断左右两个点
//2. 当前坦克 右上角的坐标 [this.getX()+60, this.getY()]
//在这个范围内就代表碰撞了
if(this.getX()+60> enemyTank.getX()
&& this.getX()+60< enemyTank.getX()+40
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+60){
return true;
}
//3. 当前坦克 右下角的坐标 [this.getX() + 60, this.getY()+40]
if(this.getX()+60> enemyTank.getX()
&& this.getX()+60< enemyTank.getX()+40
&& this.getY()+40> enemyTank.getY()
&& this.getY()+40< enemyTank.getY()+60){
return true;
}
}
//如果敌人坦克向右/左
//分析
//1. 如果敌人坦克是右/左 x的范围 [enemyTank.getX(), enemyTank.getX() + 60]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 40]
if(enemyTank.getDirect() == 1 || enemyTank.getDirect()==3){
//2. 当前坦克 右上角的坐标 [this.getX()+60, this.getY()]
if(this.getX()+60> enemyTank.getX()
&& this.getX()+60< enemyTank.getX()+60
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+40){
return true;
}
//3. 当前坦克 右下角的坐标 [this.getX() + 60, this.getY()+40]
if(this.getX()+60> enemyTank.getX()
&& this.getX()+60< enemyTank.getX()+60
&& this.getY()+40> enemyTank.getY()
&& this.getY()+40< enemyTank.getY()+40){
return true;
}
}
}
}
break;
case 2://向下
//遍历敌人坦克集合,让当前这个敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
//从Vector中取出一辆敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
//不和自己比较
if(enemyTank!=this){//不是自己则比较
//如果敌人坦克向上/下
//分析
//1. 如果敌人坦克是上/下 x的范围 [enemyTank.getX(), enemyTank.getX() + 40]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 60]
if(enemyTank.getDirect() == 0 || enemyTank.getDirect()==2){
//注意要判断左右两个点
//2. 当前坦克 左下角的坐标 [this.getX(), this.getY()+60]
//在这个范围内就代表碰撞了
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+40
&& this.getY()+60> enemyTank.getY()
&& this.getY()+60< enemyTank.getY()+60){
return true;
}
//3. 当前坦克 右下角的坐标 [this.getX() + 40, this.getY()+60]
if(this.getX()+40> enemyTank.getX()
&& this.getX()+40< enemyTank.getX()+40
&& this.getY()+60> enemyTank.getY()
&& this.getY()+60< enemyTank.getY()+60){
return true;
}
}
//如果敌人坦克向右/左
//分析
//1. 如果敌人坦克是右/左 x的范围 [enemyTank.getX(), enemyTank.getX() + 60]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 40]
if(enemyTank.getDirect() == 1 || enemyTank.getDirect()==3){
//2. 当前坦克 左下角的坐标 [this.getX(), this.getY()+60]
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+60
&& this.getY()+60> enemyTank.getY()
&& this.getY()+60< enemyTank.getY()+40){
return true;
}
//3. 当前坦克 右下角的坐标 [this.getX() + 40, this.getY()+60]
if(this.getX()+40> enemyTank.getX()
&& this.getX()+40< enemyTank.getX()+60
&& this.getY()+60> enemyTank.getY()
&& this.getY()+60< enemyTank.getY()+40){
return true;
}
}
}
}
break;
case 3://向左
//遍历敌人坦克集合,让当前这个敌人坦克和其他所有的敌人坦克进行比较
for (int i = 0; i < enemyTanks.size(); i++) {
//从Vector中取出一辆敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
//不和自己比较
if(enemyTank!=this){//不是自己则比较
//如果敌人坦克向上/下
//分析
//1. 如果敌人坦克是上/下 x的范围 [enemyTank.getX(), enemyTank.getX() + 40]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 60]
if(enemyTank.getDirect() == 0 || enemyTank.getDirect()==2){
//注意要判断左右两个点
//2. 当前坦克 左上角的坐标 [this.getX(), this.getY()]
//在这个范围内就代表碰撞了
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+40
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+60){
return true;
}
//3. 当前坦克 左下角的坐标 [this.getX(), this.getY()+40]
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+40
&& this.getY()+40> enemyTank.getY()
&& this.getY()+40< enemyTank.getY()+60){
return true;
}
}
//如果敌人坦克向右/左
//分析
//1. 如果敌人坦克是右/左 x的范围 [enemyTank.getX(), enemyTank.getX() + 60]
// y的范围 [enemyTank.getY(), enemyTank.getY() + 40]
if(enemyTank.getDirect() == 1 || enemyTank.getDirect()==3){
//2. 当前坦克 左上角的坐标 [this.getX(), this.getY()]
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+60
&& this.getY()> enemyTank.getY()
&& this.getY()< enemyTank.getY()+40){
return true;
}
//3. 当前坦克 左下角的坐标 [this.getX(), this.getY()+40]
if(this.getX()> enemyTank.getX()
&& this.getX()< enemyTank.getX()+60
&& this.getY()+40> enemyTank.getY()
&& this.getY()+40< enemyTank.getY()+40){
return true;
}
}
}
}
break;
}
return false;//表示没有碰撞
}
//构造器
public EnemyTank(int x, int y) {
super(x, y);
}
@Override
public void run() {
while(true){
//这里我们判断如果shots size() = 0就创建一颗子弹,放入到shots集合中,并启动 608
if(isLive==true && shots.size()==0){//敌人的坦克存活且敌人子弹集合Vector为空
Shot s = null;//临时子弹变量
//判断坦克方向,创建对应子弹
switch(getDirect()){
case 0://向上
s = new Shot(getX()+20,getY(),0);
break;
case 1://向右
s = new Shot(getX()+60,getY()+20,1);
break;
case 2://向下
s = new Shot(getX()+20,getY()+60,2);
break;
case 3://向左
s = new Shot(getX(),getY()+20,3);
break;
}
shots.add(s);//将子弹加入shots的Vector集合
Thread thread = new Thread(s);
thread.start();//启动子弹线程
}
//根据坦克的方向来继续移动 605
switch(getDirect()){
case 0://向上
//让坦克保持一个方向走30步
for(int i=0;i<30;i++){
//控制坦克移动范围不能超过边界,只能在边界内移动且满足不能和别的敌人坦克发生碰撞 606
if(getY()>0 && !isTouchEnemyTak()) {
moveUp();
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 1://向右
for(int i=0;i<30;i++){
if(getX()+60<1000 && !isTouchEnemyTak()){
moveRight();
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 2://向下
for(int i=0;i<30;i++){
if(getY()+60<750 && !isTouchEnemyTak()){
moveDown();
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 3://向左
for(int i=0;i<30;i++){
if(getX()>0 && !isTouchEnemyTak()){
moveLeft();
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
//然后判断随机的改变坦克方向0-3
setDirect((int)(Math.random()*4));//Math.random() [0-1)
//Math.random()*4) [0-4)
//结束线程
if(isLive==false){
break;
}
}
}
}
射击Shot
package com.stulzl.tankgame08;
//发射子弹线程 600
public class Shot implements Runnable{
int x;//子弹x坐标
int y;//子弹y坐标
int direct=0;//子弹的方向
int speed = 2;//子弹的速度
boolean isLive = true;//子弹是否存活
//构造器,接收传来的值
public Shot(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
@Override
public void run() {//射击行为
while(true){
//休息50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
switch(direct){//根据方向值选择相应的子弹方向路线
case 0://向上
y-=speed;
break;
case 1://向右
x+=speed;
break;
case 2://向下
y+=speed;
break;
case 3://向左
x-=speed;
break;
}
//System.out.println("子弹x= "+x+" y= "+y);
//当子弹移动到面板的边界时,就应该销毁(把启动的子弹的线程销毁)
//当子弹击中敌人坦克时也应该结束次线程
if(!(x>=0 && x<=1000 && y>=0 && y<=750 && isLive)){//子弹越界出窗口范围
isLive = false;//子弹死了
break;
}
}
}
}
爆炸Bomb
package com.stulzl.tankgame08;
//炸弹 604
public class Bomb {
int x,y;//炸弹坐标
int life = 9;//炸弹生命周期,用来控制后面炸弹爆炸瞬间的大图还是小图
boolean isLive = true;//是否存活
public Bomb(int x, int y) {
this.x = x;
this.y = y;
}
//减少生命值
public void lifeDown(){
if(life>0){
life--;
}else{
isLive = false;
}
}
}
Node类恢复数据用到的
package com.stulzl.tankgame08;
//一个Node对象就表示一个敌人坦克信息
public class Node {
private int x;
private int y;
private int direct;
public Node(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getDirect() {
return direct;
}
public void setDirect(int direct) {
this.direct = direct;
}
}
Recorder类记录数据到文件或读取
package com.stulzl.tankgame08;
import java.io.*;
import java.util.Vector;
//用来记录相关信息和文件的交互 650
@SuppressWarnings("all")
public class Recorder {
//定义变量,记录我方击毁敌人坦克数量
private static int allEnemyTankNum = 0;
//定义IO对象,准备写数据到文件
private static BufferedWriter bw = null;
private static BufferedReader br = null;
//注意这个文件位置我们是保存在E盘的但涉及文件改动或者复制时,这里的路径不会修改
//所以我们将文件路径保存到src下 654
// private static String recordFile = "e:\\java学习\\初级\\course138\\myRecord.txt";
private static String recordFile = "src\\myRecord.txt";
//定义Vector,用于接收MyPanel类中定义的EnemyTank的Vector集合,和防止敌人坦克碰撞定义的Vector
//一个意思
private static Vector enemyTanks = null;
//定义一个Node的Vector集合,用于保存敌人的信息node
private static Vector nodes = new Vector<>();
//返回记录文件的目录 654
public static String getRecordFile() {
return recordFile;
}
//用来得到MyPanel类中定义的EnemyTank的Vector集合
public static void setEnemyTanks(Vector enemyTanks) {
Recorder.enemyTanks = enemyTanks;
}
//增加一个方法,用于读取recordFile文件信息,回复相关数据 653
//该方法,在继续上局的时候调用即可
public static Vector getNodesAndEnemyTankrec(){
try {
//创建一个BufferedReader对象
//这里做个小提示,如果玩家第一次上来就选择继续游戏,但会我们的recordFile文件还没创建为空
//就会抛出异常,所以更新一下 654
br = new BufferedReader(new FileReader(recordFile));
//读取文件信息
//这个读取的时击毙敌人的坦克数量值,因为readLine()返回的时String类型所以需要类型转换
allEnemyTankNum = Integer.parseInt(br.readLine());
//继续循环读取剩余的敌人坦克信息 x,y,direct
String line = "";
while((line = br.readLine())!=null){
//因为读到的时字符串,但是我们需要数组,因为数组到左更方便
String syd[] = line.split(" ");//依据" "(空格)分割字符串
//这里new一个Node对象,将读到的信息传给Node类
//同样需要类型转换
Node node = new Node(Integer.parseInt(syd[0]), Integer.parseInt(syd[1])
, Integer.parseInt(syd[2]));
//为了更好的管理,将node对象放入我们创建的nodes的Vector集合中,因为后面
//返回值直接返回nodes这个Vector集合就行了
nodes.add(node);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if(br!=null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//返回nodes这个Vector集合(其实返回了所有我们读到的敌人坦克数据),我们
//把信息放入了Node类中,而node又放在了nodes的Vector集合中//就是谁调用这个方法并接收
//就拥有了所有读到的数据
return nodes;
}
//增加一个方法,当游戏退出时,我们将allEnemyTankNum值保 650
// 存进"E:\\java学习\\初级\\course137\\myRecord.txt"文件
//因为是个方法不会自己自动执行,需要我们调用
//对这个方法升级保存敌人坦克的坐标和方向 651
public static void keepRecord(){
try {
bw = new BufferedWriter(new FileWriter(recordFile));
//保存销毁敌人的坦克数
bw.write(allEnemyTankNum+"\r\n");//"\r\n"换行的意思
//bw.newLine();//这样写换行也行
//遍历敌人坦克的Vector ,然后根据情况保存即可. 651
//OOP, 定义一个属性 ,然后通过setXxx得到 敌人坦克的Vector
for (int i = 0; i < enemyTanks.size(); i++) {
//取出一辆敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
//保存敌人坦克信息
if(enemyTank.isLive){//如果敌人坦克存活
//将信息写入变量
String record = enemyTank.getX()+" "+enemyTank.getY()+" "+enemyTank.getDirect();
//写入文件
bw.write(record+"\r\n");
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if(bw!=null){
bw.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static int getAllEnemyTankNum() {
return allEnemyTankNum;
}
public static void setAllEnemyTankNum(int allEnemyTankNum) {
Recorder.allEnemyTankNum = allEnemyTankNum;
}
//当我方坦克击毁一辆敌人坦克,就应当allEnemyTankNum++
public static void addAllEnemyTankNun(){
Recorder.allEnemyTankNum++;
}
}
背景音乐AePlayWave类
package com.stulzl.tankgame08;
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
//播放游戏bgm,这个类是老师写的,这里不做重点 654
public class AePlayWave extends Thread {
private String filename;
public AePlayWave(String wavfile) {//构造器,作用是接收文件路径
filename = wavfile;
}
public void run() {
File soundFile = new File(filename);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (Exception e1) {
e1.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat();
SourceDataLine auline = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (Exception e) {
e.printStackTrace();
return;
}
auline.start();
int nBytesRead = 0;
//这是缓冲
byte[] abData = new byte[512];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0)
auline.write(abData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
auline.drain();
auline.close();
}
}
}
画板MyPanel
package com.stulzl.tankgame08;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.Vector;
//坦克的绘图区
//为了响应键盘事件,实现KeyListener
//为了让Panel 不停的重绘子弹,需要将 MyPanel 实现Runnable ,当做一个线程使用 601
public class MyPanel extends JPanel implements KeyListener,Runnable{
//定义我们的坦克
Hero hero = null;
//定义敌人坦克,放入到Vector中 578
Vector enemyTanks = new Vector<>();
int enemyTanksSize = 3;//敌人坦克数量
//定义一个存放Node对象的Vector集合,用于恢复敌人坦克的坐标和方向
//目的时接收调用getNodesAndEnemyTankrec()方法后返回的nodes的Vector集合
Vector nodes = new Vector<>();
//定义一个Vector,用于存放炸弹
Vector bombs = new Vector<>();
//定义三张炸弹图片,用于显示爆炸效果
//说明,当子弹击中坦克时,加入一个Bomb对象到bombs
Image image1 = null;
Image image2 = null;
Image image3 = null;
//构造器设置坦克信息
public MyPanel(String key){
//调用getNodesAndEnemyTankrec()方法恢复数据,并用我们在MyPanel中创建的nodes集合接收
//这里做个小提示,如果玩家第一次上来就选择继续游戏,但会我们的recordFile文件还没创建为空
//就会抛出异常,所以更新一下 654
//先判断记录的文件是否存在
//如果存在,就正常执行,如果文件不存在,提示,只能开启新游戏,key = "1"
//根据getRecordFile()方法得到的文件路径,判断文件是否存在
File file = new File(Recorder.getRecordFile());
if(file.exists()){//存在
nodes = Recorder.getNodesAndEnemyTankrec();
}else{//不存在
System.out.println("文件不存在,只能开启新的游戏");
key="1";//将key置为1,强制开启新游戏
}
//我们的坦克设置
hero = new Hero(500, 100);//创建并初始化自己的坦克坐标
hero.setSpeed(5);//改变坦克速度
//让玩家选择新游戏还是继续上局游戏 653
switch(key){
case "1"://新游戏
//遍历 初始化敌人坦克
for (int i = 0; i <enemyTanksSize; i++) {
//创建并设置敌人坦克坐标,每隔100像素放一辆坦克
//(100 * (i + 1)), 0敌人坦克坐标
EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);
//将MyPanel中定义的enemyTanks的Vector集合设置给
// EnemyTank中定义的enemyTanks的Vector集合
enemyTank.setEnemyTanks(enemyTanks);
//将MyPanel中定义的enemyTanks的Vector集合设置给Recorder中enemyTanks 651
//的Vector集合
Recorder.setEnemyTanks(enemyTanks);
//更改敌人坦克方向
enemyTank.setDirect(2);
//启动敌人坦克随机动线程
Thread thread1 = new Thread(enemyTank);
thread1.start();
//给该enemyTank 加入一颗子弹shot 602
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//加入enemyTank的Vector 成员
enemyTank.shots.add(shot);
//启动 shot 子弹对象
Thread thread = new Thread(shot);
thread.start();
//添加敌人坦克到Vector
enemyTanks.add(enemyTank);
}
break;
case "2"://继续上局游戏
//遍历 初始化敌人坦克
for (int i = 0; i <nodes.size(); i++) {
//取出一个node敌人坦克信息
Node node = nodes.get(i);
//设置敌人坦克坐标
EnemyTank enemyTank = new EnemyTank(node.getX(),node.getY());
//将MyPanel中定义的enemyTanks的Vector集合设置给
// EnemyTank中定义的enemyTanks的Vector集合
enemyTank.setEnemyTanks(enemyTanks);
//将MyPanel中定义的enemyTanks的Vector集合设置给Recorder中enemyTanks 651
//的Vector集合
Recorder.setEnemyTanks(enemyTanks);
//更改敌人坦克方向
enemyTank.setDirect(node.getDirect());
//启动敌人坦克随机动线程
Thread thread1 = new Thread(enemyTank);
thread1.start();
//给该enemyTank 加入一颗子弹shot 602
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//加入enemyTank的Vector 成员
enemyTank.shots.add(shot);
//启动 shot 子弹对象
Thread thread = new Thread(shot);
thread.start();
//添加敌人坦克到Vector
enemyTanks.add(enemyTank);
}
break;
default:
System.out.println("您的输入有误~");
break;
}
//初始化图片对象
image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));
image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));
image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));
//这里播放音乐 654
AePlayWave aePlayWave = new AePlayWave("src\\111.wav");
aePlayWave.start();//启动线程
}
//编写方法记录战绩,显示我方击毁敌方坦克信息 650
public void showInfo(Graphics g){
//画出玩家总成绩
g.setColor(Color.black);//设置画笔颜色
Font font = new Font("宋体", Font.BOLD, 25);//设置字体,加粗,字号
g.setFont(font);//将上两句设置的信息添加给画笔
g.drawString("您累计击毁敌方坦克",1020,30);//将这行字放置在1020,30坐标
//画出敌方坦克作为小标识
drawTank(1020,60,g,0,0);//后面俩0一个代表方向,一个代表坦克颜色
//战绩
g.setColor(Color.black);//之所以重新设置画笔颜色,是因为在画坦克标识时改变了画笔颜色了
//Recorder.getAllEnemyTankNum()+""类型转换
g.drawString(Recorder.getAllEnemyTankNum()+"",1080,100);
}
//绘图方法
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0,0,1000,750);//填充矩形,默认黑色,这个是坦克的游戏区域
//这里调用记录战绩的方法
showInfo(g);
//画出自己坦克,我们把方法封装到一个方法,调用即可 573
if(hero !=null && hero.isLive==true) {//我们的坦克不为空且存活
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);//这个1控制的是坦克颜色类型
}
//画出hero射击的子弹
// if(hero.shot!=null && hero.shot.isLive==true){//如果子弹存在且存活就画,如果子弹运动到边界就不画了
// g.draw3DRect(hero.shot.x,hero.shot.y,1,1,false);//子弹大小 长1 高1
// //this.repaint();//重绘
// }
//画出hero的子弹集合Vector 607
//遍历绘制
for (int i = 0; i < hero.shots.size(); i++) {
Shot shot = hero.shots.get(i);//取出一颗子弹
//如果子弹存在且存活就画,如果子弹运动到边界就不画了
if(shot!=null && shot.isLive==true) {
g.draw3DRect(shot.x,shot.y, 1, 1, false);//子弹大小 长1 高1
}else{//如果子弹为空或者已经销毁,就从shots的Vector集合中去掉
hero.shots.remove(shot);
}
}
//如果bombs集合中有对象,就画出 604
for (int i = 0; i < bombs.size(); i++) {
//取出一个炸弹
Bomb bomb = bombs.get(i);
//根据当前这个bomb对象的life值去判断对应的图片
if(bomb.life>6){
g.drawImage(image1,bomb.x, bomb.y, 60,60,this);//画出大图片
}else if(bomb.life>3){
g.drawImage(image2,bomb.x, bomb.y, 60,60,this);//画出中图片
}else{
g.drawImage(image3,bomb.x, bomb.y, 60,60,this);//画出小图片
}
//让炸弹的生命值减少
bomb.lifeDown();
//如果bomb的life为0,就从bombs的集合中删除
if(bomb.life==0){
bombs.remove(bomb);
}
}
//画出敌人坦克,遍历Vector集合
for (int i = 0; i <enemyTanks.size(); i++) {
//从Vector中 取出敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
if(enemyTank.isLive) {//当前敌人坦克是活的,我们才画
//画敌人坦克
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
//画出 enemyTank 所有子弹 602
for (int j = 0; j < enemyTank.shots.size(); j++) {
//取出敌人子弹
Shot shot = enemyTank.shots.get(j);
//绘制
if (shot.isLive == true) { //子弹存活
g.draw3DRect(shot.x, shot.y, 1, 1, false);//子弹大小 长1 高1
} else {//敌人子弹不存活
//从Vector 移除
enemyTank.shots.remove(shot);
}
}
}
}
}
/**
* @param x 坦克的左上角 x 坐标
* @param y 坦克的左上角 y 坐标
* @param g 画笔
* @param direct 坦克方向(上下左右)
* @param type 坦克类型
*/
//编写方法画出坦克
public void drawTank(int x,int y,Graphics g,int direct,int type){
//根基不同的类型,设置不同的颜色
switch(type){
case 0://敌人的坦克
g.setColor(Color.cyan);
break;
case 1://我们的坦克
g.setColor(Color.yellow);
break;
}
//根据坦克方向,来绘制对应方向的坦克 577
//direct 表示方向(0: 向上 1 向右 2 向下 3 向左 )
switch(direct){
case 0://表示坦克向上
g.fill3DRect(x,y,10,60,false);//左边轮子//绘制3D的矩形看起来更像 false使图像更像
g.fill3DRect(x+30,y,10,60,false);//右边的轮子
g.fill3DRect(x+10,y+10,20,40,false);//中间身体
g.fillOval(x+10,y+20,20,20);//中间炮塔
g.drawLine(x+20,y+30,x+20,y);//炮筒
break;
case 1://向右
g.fill3DRect(x,y,60,10,false);//左边轮子//绘制3D的矩形看起来更像 false使图像更像
g.fill3DRect(x,y+30,60,10,false);//右边的轮子
g.fill3DRect(x+10,y+10,40,20,false);//中间身体
g.fillOval(x+20,y+10,20,20);//中间炮塔
g.drawLine(x+30,y+20,x+60,y+20);//炮筒
break;
case 2://向下
g.fill3DRect(x,y,10,60,false);//左边轮子//绘制3D的矩形看起来更像 false使图像更像
g.fill3DRect(x+30,y,10,60,false);//右边的轮子
g.fill3DRect(x+10,y+10,20,40,false);//中间身体
g.fillOval(x+10,y+20,20,20);//中间炮塔
g.drawLine(x+20,y+30,x+20,y+60);//炮筒
break;
case 3://向左
g.fill3DRect(x,y,60,10,false);//左边轮子//绘制3D的矩形看起来更像 false使图像更像
g.fill3DRect(x,y+30,60,10,false);//右边的轮子
g.fill3DRect(x+10,y+10,40,20,false);//中间身体
g.fillOval(x+20,y+10,20,20);//中间炮塔
g.drawLine(x+30,y+20,x,y+20);//炮筒
break;
default:
System.out.println("暂时没有别的操作");
}
}
//编写一个方法判断子弹击中坦克 方法 603
public void hitTank(Shot s, Tank tank){//Tank tank这里直接用父类Tank接收涉及多态,更方便
//判断子弹s是否击中坦克
switch(tank.getDirect()){
case 0://向上
case 2://向下//如果子弹在坦克区域内,说明击中坦克
if(s.x>tank.getX() && s.x<tank.getX()+40
&& s.y>tank.getY() && s.y<tank.getY()+60){
s.isLive = false;
tank.isLive = false;
//当我们的子弹击中敌人坦克后,就将enemyTank从Vector中去掉
enemyTanks.remove(tank);
//当我方击毁一辆敌方坦克,就对数据allEnemyTankNum++ 650
//解读, 因为 enemyTank 可以是 Hero 也可以是 EnemyTank
if(tank instanceof EnemyTank){//判断是不是敌方坦克
Recorder.addAllEnemyTankNun();
}
//创建Bomb对象,加入到bombs集合
Bomb bomb = new Bomb(tank.getX(),tank.getY());
bombs.add(bomb);//将炸弹添加进bombs的Vector集合
}
break;
case 1://向右
case 3://向左
if(s.x>tank.getX() && s.x<tank.getX()+60
&& s.y>tank.getY() && s.y<tank.getY()+40){
s.isLive = false;
tank.isLive = false;
//当我们的子弹击中敌人坦克后,就将enemyTank从Vector中去掉 605
enemyTanks.remove(tank);
//当我方击毁一辆敌方坦克,就对数据allEnemyTankNum++
//解读, 因为 enemyTank 可以是 Hero 也可以是 EnemyTank
if(tank instanceof EnemyTank){//判断是不是敌方坦克
Recorder.addAllEnemyTankNun();
}
//创建Bomb对象,加入到bombs集合
Bomb bomb = new Bomb(tank.getX(),tank.getY());
bombs.add(bomb);//将炸弹添加金bombs的Vector集合
}
break;
}
}
//三个方法
//有字符输出,触发
public void keyTyped(KeyEvent e) {
}
//按下键盘,触发
//处理wdsa键按下的情况 577
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_W){//方向向上
//改变坦克的方向
hero.setDirect(0);
if(hero.getY()>0){//判断我方坦克,在边界范围内移动 606
hero.moveUp();
}
}else if(e.getKeyCode()==KeyEvent.VK_D){//方向向右
hero.setDirect(1);
if(hero.getX()+60<1000){
hero.moveRight();
}
}else if(e.getKeyCode()==KeyEvent.VK_S){//方向向下
hero.setDirect(2);
if(hero.getY()+60<750){
hero.moveDown();
}
}else if(e.getKeyCode()==KeyEvent.VK_A){//方向向左
hero.setDirect(3);
if(hero.getX()>0){
hero.moveLeft();
}
}
//如果用户按下J键,就发射子弹
if(e.getKeyCode()==KeyEvent.VK_J){
//如果子弹为空或者子弹销毁才发射子弹 ,一次只能发射一颗子弹 607
// if(hero.shot==null || hero.shot.isLive==false){
// hero.shortEnemyTank();//调用射击方法
// }
//发射多颗子弹
if(hero.shots.size()<5){//最多发射5颗子弹 607
hero.shortEnemyTank();//调用射击方法
}
}
this.repaint();//面板重绘
}
//松开键盘,触发
@Override
public void keyReleased(KeyEvent e) {
}
//当我们的坦克可以连续发射子弹后,判断每一颗子弹是否击中敌人坦克 607
public void hitEnemyTank(){
for (int j = 0; j < hero.shots.size(); j++) {//遍历子弹集合
Shot shot = hero.shots.get(j);//取出集合中的子弹
if(shot!=null && shot.isLive){//当我们的子弹射出(即不为空)且 存活
//遍历敌人所有坦克集合,看看哪一辆敌人坦克被击中
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);//取出一辆敌人坦克
hitTank(shot,enemyTank);//调用判断是否击中
}
}
}
}
//编写方法判断敌人坦克子弹是否击中我方坦克 609
public void hitHero(){
//遍历所有的敌人坦克enemyTanks的Vector集合
for (int i = 0; i < enemyTanks.size(); i++) {
//取出一辆敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
//遍历这辆敌人坦克的所有子弹shots的集合Vector
for(int j=0;j<enemyTank.shots.size();j++){
//取出子弹
Shot shot = enemyTank.shots.get(j);
//判断敌人坦克子弹shot是否击中我们的Hero坦克
if(hero.isLive && shot.isLive){//我们的坦克存活且敌人子弹也存活
hitTank(shot,hero);
}
}
}
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//判断是否击中了敌人坦克 603
//这个方法只能判断当前射出的子弹击中敌人,适用于单颗子弹
// if(hero.shot!=null && hero.shot.isLive){//当我的子弹射出(即不为空)且 存活
// //遍历敌人所有坦克,看看哪一辆敌人坦克被击中
// for (int i = 0; i < enemyTanks.size(); i++) {
// EnemyTank enemyTank = enemyTanks.get(i);//取出一辆敌人坦克
// hitTank(hero.shot,enemyTank);
// }
// }
//调用多颗子弹击中敌人坦克方法
hitEnemyTank();
//调用敌人坦克击中我们的Hero坦克方法
hitHero();
this.repaint();//面板重绘
}
}
}