文章目录
坦克大战0.5版
增加功能
防止敌人坦克重叠运动
因为考虑到坦克各个边的碰撞,正方形考虑两两碰撞应该有4种可能
而老韩故意设计为长方形的坦克,要考虑的更多一点,比如敌方坦克到底是上下方向,还是左右方向,在宽为40,长为60的坦克形状下,要考虑敌我坦克8种面对方向
为了让敌方坦克相交后不再移动,我们要考虑昨天分析的8种情况,这个判断条件有200多行,非常冗余,暂时没有优化方法,因为这是个长方形坦克,而非正方形。
我们把判断条件封装在一个方法中,大致可以描述为:通过switch接收对方坦克director,我们让敌方坦克的每个坦克每次刷新屏幕都在与其他所有敌方坦克相比较他们的坐标位置,这个计算量应该是非常大的,这里可以体会到CPU核心的运算次数有多么强大,每秒50亿次的运算量,在我的10个敌方坦克集合的遍历集合下依然是游刃有余
就如同我想的那样,我的坦克是线程集合,我的子弹也是线程集合,我每次的屏幕刷新(50ms),这些复杂的数据加起来同时计算,我的CPU每秒应该也有上百万次的计算量了,但是我认为还是没我想的那么简单
enemyTank类中添加集合
//增加成员,EnemyTank 可以得到敌人坦克的Vector
Vector<EnemyTank> enemyTanks = new Vector<>();
EnemyTank类中写入isTouchEnemyTank()方法
public boolean isTouchEnemyTank() {
//判断当前敌人坦克(this)方向
switch (this.getDirect()) {
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() &&
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 (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
//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() &&
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() + 60 &&
this.getY() + 40 >= enemyTank.getY() &&
this.getY() + 40 <= enemyTank.getY() + 40) {
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() + 40 &&
this.getX() <= enemyTank.getX() &&
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() + 40 &&
this.getY() + 60 >= enemyTank.getY() &&
this.getY() + 60 <= enemyTank.getY() + 60) {
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() + 40 &&
this.getX() <= enemyTank.getX() &&
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() + 40 &&
this.getY() + 40 >= enemyTank.getY() &&
this.getY() + 40 <= enemyTank.getY() + 60) {
return true;
}
}
}
}
break;
}
return false;
}
EnemyTank类run()线程判断
if (getY() > 0 && (!isTouchEnemyTank()))
MyPanal类,MyPanal()设置集合
//将 enemyTanks 设置给 enemyTank
enemyTank.setEnemyTanks(enemyTanks);
记录玩家的成绩
统计击毁数据实现,比较容易理解,重新划出一块内容,里面画出文字与static变量,当敌方坦克每次remove,static自增即可,我们需要IO流Out数据,写入文件中
咱们可以记住一个单词,recorder,把这个单词做成一个类,写入IO流内容即可,我们的矩形扩展方法写在Mypanel中,,在paint调用,注意,调用时注意顺序
编写方法,显示我方击毁坦克信息,并在paint中调用
MyPanel类showInfo()方法
public void showInfo(Graphics g){
//画出玩家的总成绩
g.setColor(Color.black);
Font font = new Font("宋体",Font.BOLD,25);
g.setFont(font);
g.drawString("你击毁的敌方坦克",1000,30);
drawTank(1000,60,g,0,0); //画出一个敌方坦克
g.setColor(Color.BLACK); //这里需要重新设置成黑色,不然会保持青色
g.drawString(Recorder.getAllEnemyTankNum() + "",1080,100); //注意转字符串
}
Recorder类
package tankgame5;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
* Create By 刘鸿涛
* 2022/2/5 12:58
* 该类用于记录相关信息的,和文件交互
*/
public class Recorder {
//定义变量,记录我方击毁敌人坦克数
private static int allEnemyTankNum = 0;
//定义IO对象
private static FileWriter fw = null;
private static BufferedWriter bw = null;
private static String recordFile = "d:\\myRecord.txt";
//增加一个方法,当游戏退出时,我们将allEnemyTankNum保存到 recordFile
public static void keepRecord() throws IOException {
bw = new BufferedWriter(new FileWriter(recordFile));
// bw.newLine();
bw.write(allEnemyTankNum + "\r\n");
if (bw != null){
bw.close();
}
}
public static int getAllEnemyTankNum(){
return allEnemyTankNum;
}
public static void setAllEnemyTankNum(int allEnemyTankNum){
Recorder.allEnemyTankNum = allEnemyTankNum;
}
//当我方坦克击毁一个敌人坦克时,就应当allEnemyTankNum++
public static void addAllEnemyTankNum(){
Recorder.allEnemyTankNum++;
}
}
hitTank()方法加入判断
enemyTanks.remove(tank);
//当我方击毁一个地方坦克时,就对数据allEnemyTankNum++
//注意:此时的坦克可以是hero也可以是enemyTank
if (tank instanceof EnemyTank){
Recorder.addAllEnemyTankNum();
}
我们main方法加入监听器
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.out.println("监听到关闭窗口");
try {
Recorder.keepRecord();
} catch (IOException ex) {
ex.printStackTrace();
}
//退出
System.exit(0);
}
});
记录当时的敌人坦克坐标/方向,存盘退出
给Recorder类keepRecord()方法添加功能
//遍历敌人坦克Vector,然后根据情况保存即可
//OOP,定义一个属性,然后通过setXxx得到敌人Vector
for (int i = 0; i < enemyTanks.size(); i++){
//取出敌人坦克
EnemyTank enemyTank = enemyTanks.get(i);
//建议下面判断下(可以不判断)
if (enemyTank.isLive){
//保存该enemyTank信息
String record = enemyTank.getX() + " " + enemyTank.getY() + " " + enemyTank.getDirect();
//写入到文件
bw.write(record + "\r\n");
}
}
将MyPanel对象的 enemyTanks 设置给 Recorder 的 enemyTanks
Mypanel类加入
Recorder.setEnemyTanks(enemyTanks);
玩游戏时,可以选择是开新游戏还是继续上局游戏
将每个敌人信息,恢复成Node对象 => Vector
通过Node的Vector去恢复敌人坦克的位置和方向
Node类
package tankgame5;
/**
* Create By 刘鸿涛
* 2022/2/5 16:28
* 一个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类getNodesAndEnemyTankRec()方法
当继续游戏时调用此方法
//增加一个方法,用于读取recordFile,恢复相关信息
//该方法,在继续上局游戏时调用即可
public static Vector<Node> getNodesAndEnemyTankRec() throws IOException {
br = new BufferedReader(new FileReader(recordFile));
allEnemyTankNum = Integer.parseInt(br.readLine());
//循环读取文件,生成nodes集合
String line = "";
while ((line = br.readLine()) != null){
String []xyd = line.split(" ");
Node node = new Node(Integer.parseInt(xyd[0]),
Integer.parseInt(xyd[1]), Integer.parseInt(xyd[2]));
nodes.add(node); //nodes Vector
}
if(br != null){
br.close();
}
return nodes;
}
MyPanel类加入swich语句输入key值判断是否继续游戏
switch (key){
case "1": //新游戏
//初始化敌人坦克
for (int i = 0; i < enemyTanksSize; i++) {
//创建一个敌人的坦克
EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);
//将 enemyTanks 设置给 enemyTank
enemyTank.setEnemyTanks(enemyTanks);
//设置方向
enemyTank.setDirect(2);
//启动敌方坦克线程
new Thread(enemyTank).start();
//给该enemyTank 加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//加入enemyTank的Vector 成员
enemyTank.shots.add(shot);
//启动 shot 对象
new Thread(shot).start();
//加入坦克
enemyTanks.add(enemyTank);
}
break;
case "2": //继续游戏
//初始化敌人坦克
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
//创建一个敌人的坦克
EnemyTank enemyTank = new EnemyTank(node.getX(),node.getY());
//将 enemyTanks 设置给 enemyTank
enemyTank.setEnemyTanks(enemyTanks);
//设置方向
enemyTank.setDirect(node.getDirect());
//启动敌方坦克线程
new Thread(enemyTank).start();
//给该enemyTank 加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//加入enemyTank的Vector 成员
enemyTank.shots.add(shot);
//启动 shot 对象
new Thread(shot).start();
//加入坦克
enemyTanks.add(enemyTank);
}
break;
default:
System.out.println("你的输入有误");
}
坦克大战0.6版
增加功能
游戏开始时,播放背景音乐
我们需要一个类
AePlayWave
package tankgame6;
/**
* Create By 刘鸿涛
* 2022/2/5 18:06
*/
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
//public class AePlayWave {
// public static void main(String[] args){
// AePlayWave apw = new AePlayWave("D:\\start.wav");
// apw.start();
// }
//}
class AePlayWave extends Thread{
private String filename;
public AePlayWave(String wavefile){
filename = wavefile;
}
public void run(){
File SoundFile = new File(filename);
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(SoundFile);
} catch (Exception e) {
e.printStackTrace();
return;
}
AudioFormat Format = audioInputStream.getFormat();
SourceDataLine sdl = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,Format);
try {
sdl = (SourceDataLine)AudioSystem.getLine(info);
sdl.open(Format);
} catch (Exception e) {
e.printStackTrace();
return;
}
sdl.start();
int nBytesRead = 0;
//缓冲
byte[] abData = new byte [1024];
try {
while(nBytesRead!=-1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0) {
sdl.write(abData, 0, nBytesRead);
}
}
} catch (Exception e) {
e.printStackTrace();
return;
} finally {
sdl.drain();
sdl.close();
}
}
}
MyPanel类中新建AePlayWave对象,通过里面的run方法,直接调用即可
new AePlayWave("src\\start.wav").start();
遇到问题
无声音,未解决
处理文件相关异常
第一次运行游戏读取文件时异常
MyPanel方法中判断
//判断记录的文件是否存在
//不存在,只能开启新游戏
File file = new File(Recorder.getRecordFile());
if (file.exists()){
try {
nodes = Recorder.getNodesAndEnemyTankRec();
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("文件不存在,只能开启新的游戏");
key = "1";
}
recorder类
//返回记录文件路径
public static String getRecordFile(){
return recordFile;
}