初学者必学的一个小项目,通过编写坦克大战能体会面向对象(oop)的精妙之处,同时也能理解集合,多线程,io流,GUI编程等知识的运用,是一个很好的一个学习体验。
这里先放图给大家看看实际运行出来的效果,先开始游戏
然后我们击败了两个坦克,并记录在游戏中,下次可以继续上局游戏
这里把源码分享给大家,可以自由修改坦克属性,变成地狱难度,挑战自己!
首先是 主方法 实现画框等设置和游戏的响应
package com.tankgame;
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Scanner;
public class TankGameShot extends JFrame {
MyPanel mp = null;
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) { //主方法
new TankGameShot();
//调用构造器
}
//构造器
public TankGameShot(){ //设置画框
System.out.println("请输入选择 1:新游戏 2:继续上局");
String key = scanner.next();
mp=new MyPanel(key);
//将mp放入到Thread中实现代理模式启动线程
Thread thread = new Thread(mp);
thread.start(); //启动重汇界面线程
this.add(mp); //将画板加到画框里
// 窗口JFrame,可以把对象(画板)传入监听器中实现监听面板事件
this.addKeyListener(mp);
this.setSize(1300,750); //定义画框的大小
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置窗口关闭后退出程序
this.setVisible(true); //设置能显示
//添加键盘响应事件
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
Recorder.keepRecorder(); //将数据保存并退出
System.exit(0);
}
});
}
}
画笔类 用于实现坦克和子弹的绘制
package com.tankgame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.Vector;
public class MyPanel extends JPanel implements KeyListener,Runnable { //这里实现线程接口启动第二个线程repaint(一直刷新界面)
//定义我的坦克
Hero hero = null;
//将敌人坦克加入集合中,vector可以避免多线程问题
Vector<EnemyTank> enemyTanks = new Vector<>();
//定义一个存放node 对象的vector,用于恢复敌人坦克的坐标和方向
Vector<Node> nodes=null;
//敌人坦克数量
int enemyTankSize = 6;
//定义vector用于存放炸弹(当子弹击中坦克时就加入一个Bomb对象)
Vector<Bomb> bombs = new Vector<>();
//定义这三张图片,显示爆炸效果
Image image1 = null;
Image image2 = null;
Image image3 = null;
//初始化自己的坦克
public MyPanel(String key) { //构造器
//将MyPanel的enemyTanks集合传给 Recorder的enemyTanks
Recorder.setEnemyTanks(enemyTanks);
hero = new Hero(100, 500);
hero.setSpeed(5); //定义坦克移动速度
//判断记录文件是否存在
File file = new File(Recorder.getRecorderFile());
if (!file.exists()){
key="1";
System.out.println("文件不存在,只能开启新游戏");
}else { //如果存在则可以继续游戏
nodes = Recorder.getNodeAndEnemyTankRec(); //将敌人剩余坦克信息传给nodes集合
}
switch (key) {
case "1":
//初始化敌人坦克
Recorder.setAllEnemyTankNum(0);
for (int i = 0; i < enemyTankSize; i++) {
EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
//将vector集合传给enemyTank对象
enemyTank.setEnemyTanks(enemyTanks);
enemyTank.setDirect(2); //设置坦克的炮筒向下
//启动坦克线程让它动起来
new Thread(enemyTank).start();
//给坦克加入子弹
shot shot = new shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//加入shot成员
enemyTank.shots.add(shot);
//启动shot线程
new Thread(shot).start();
//加入集合里
enemyTanks.add(enemyTank);
}
break;
case "2":
//初始化敌人坦克
for (Node node : nodes) {
EnemyTank enemyTank = new EnemyTank(node.getX(), node.getY());
//将vector集合传给enemyTank对象
enemyTank.setEnemyTanks(enemyTanks);
enemyTank.setDirect(node.getDirect()); //设置坦克的炮筒向下
//启动坦克线程让它动起来
new Thread(enemyTank).start();
//给坦克加入子弹
shot shot = new shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirect());
//加入shot成员
enemyTank.shots.add(shot);
//启动shot线程
new Thread(shot).start();
//加入集合里
enemyTanks.add(enemyTank);
}
break;
default:
System.out.println("输入有误");
}
//在构造器中初始化图片对象
image1 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/bg1.png"));
image2 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/bg2.png"));
image3 = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/bg3.png"));
//播放指定的音乐
new AePlayWave("d://坦克大战//src//good.wav").start();
}
//记录击败坦克信息
public void showInfo(Graphics g){
//画出玩家总成绩
g.setColor(Color.black);
Font font = new Font("宋体",Font.BOLD,25);
g.setFont(font);
g.drawString("累计击败坦克",1020,30);
drawTank(1020,50,g,0,0); //画出敌人的一个坦克
g.setColor(Color.black); //重置画笔颜色
g.drawString(Recorder.getAllEnemyTankNum()+"",1080,100);
}
@Override
public void paint(Graphics g) { //生成画笔(画板)
super.paint(g);
g.fillRect(0, 0, 1000, 750); //填充矩形画板(当背景,默认黑色)
showInfo(g); //调用击败坦克界面
//画出自己的坦克
if (hero.alive) {
drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
}
//画出hero坦克射击的子弹
for (int i = 0; i < hero.shots.size(); i++) { //遍历子弹集合
shot shot = hero.shots.get(i); //取出子弹
if (shot != null && shot.alive) {
g.draw3DRect(shot.x, shot.y, 2, 2, false);
} else { //如果子弹以及销毁就要把它从集合拿掉
hero.shots.remove(shot);
}
}
//画出爆炸效果,取出集合的对象
for (int i = 0; i < bombs.size(); i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//取出炸弹
Bomb bomb = bombs.get(i);
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为0则将其从集合移除
if (bomb.life == 0) {
bombs.remove(bomb);
}
}
//画出敌人坦克
for (int i = 0; i < enemyTanks.size(); i++) { //遍历敌人坦克、
//取出坦克
EnemyTank enemyTank = enemyTanks.get(i);
//判断坦克是否存活
if (enemyTank.alive) { //如果true则画
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
//画出所有坦克的子弹(遍历子弹的vector)
for (int j = 0; j < enemyTank.shots.size(); j++) {
//取出子弹
shot shot = enemyTank.shots.get(j);
//绘制
if (shot.alive) {
g.draw3DRect(shot.x, shot.y, 1, 1, false);
} else {
//从vector移除
enemyTank.shots.remove(shot);
}
}
}
}
}
//有字符输出会触发
@Override
public void keyTyped(KeyEvent e) {
}
//当某个键被按下,会触发
@Override
public void keyPressed(KeyEvent e) {
//根据用户不同的键来使坦克移动
if (e.getKeyCode() == KeyEvent.VK_W) { //向上
//改变坦克方向
hero.setDirect(0);
//调用hero的移动方法
hero.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_S) {
hero.setDirect(2);
hero.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_A) {
hero.setDirect(3);
hero.moveLift();
} else if (e.getKeyCode() == KeyEvent.VK_D) {
hero.setDirect(1);
hero.moveRight();
}
//如果按下J就发射子弹即调用shotEnemyTank方法
if (e.getKeyCode() == KeyEvent.VK_J) {
//发射一颗子弹情况
//判断子弹是否销毁
//if (hero.shot==null || !hero.shot.alive) {
//这里null为第一次射击为空则创建对象发射,但随着线程销毁后,无法继续发射了,因为对象已经创建,而且不会随着线程结束而销毁
//因此需要另一个判断条件判断子弹的生命是否为假(即线程是否被销毁)这样就可以继续发子弹,并且保证子弹销毁前只能发射一个
//hero.showEnemyTank();
//发射多颗子弹情况
hero.showEnemyTank();
}
//让面板重绘
this.repaint();
}
//当某个键松开会触发
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void run() {
while (true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通过线程来同时判断子弹是否击中坦克
hitEnemyTank();
hitHero();
//线程重绘实现动态
this.repaint();
}
}
//将子弹和坦克从集合拿出来依次比较是否击中
public void hitEnemyTank() {
//遍历我们的子弹
for (int j = 0; j < hero.shots.size(); j++) {
shot shot = hero.shots.get(j); //取出子弹
if (shot != null && shot.alive) { //当我的子弹还存活且不为空
//遍历敌人所有的坦克
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
hitTank(shot, enemyTank); //遍历坦克后调用方法
}
}
}
}
//判断我方坦克是否被击中
public void hitHero() {
//遍历敌人坦克和所有的子弹
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);//取出敌人坦克
for (int j = 0; j < enemyTank.shots.size(); j++) {
shot shot = enemyTank.shots.get(j); //取出每个坦克的子弹
//判断敌人子弹是否击中我的坦克
if (hero.alive && shot.alive) {
hitTank(shot,hero);
}
}
}
}
public void hitTank(shot s,Tank enemyTank){
//判断s击中坦克
switch (enemyTank.getDirect()){
//上和下坦克xy范围一致
case 0: //上
case 2: //下
if (s.x>enemyTank.getX()&&s.x<enemyTank.getX()+40
&&s.y>enemyTank.getY()&&s.y<enemyTank.getY()+60){
s.alive=false;
enemyTank.alive=false;
//如果击中将其从集合里移除
enemyTanks.remove(enemyTank);
//如果我方坦克击毁敌方坦克就将allEnemyTankNum加1,但此时要向下转型来判断
if (enemyTank instanceof EnemyTank){
Recorder.addallEnemyTankNum(); //调用静态方法
}
//爆炸效果是随坦克被击中时产生,因此在这里new一个Bomb对象将其加入到集合里
Bomb bomb = new Bomb(enemyTank.getX(),enemyTank.getY()); //获取此时坦克坐标
bombs.add(bomb); //把对象加入集合里
}
break;
case 1: //右
case 3: //左
if (s.x>enemyTank.getX()&&s.x<enemyTank.getX()+60
&&s.y>enemyTank.getY()&&s.y<enemyTank.getY()+40){
s.alive=false;
enemyTank.alive=false;
//如果击中将其从集合里移除
enemyTanks.remove(enemyTank);
//如果我方坦克击毁敌方坦克就将allEnemyTankNum加1,但此时要向下转型来判断
if (enemyTank instanceof EnemyTank){
Recorder.addallEnemyTankNum(); //调用静态方法
}
Bomb bomb = new Bomb(enemyTank.getX(),enemyTank.getY()); //获取此时坦克坐标
bombs.add(bomb); //把对象加入集合里
}
break;
}
}
public void peng(Tank tank){
switch (tank.getDirect()){
//获取坦克方向
}
}
/**
* 坦克属性
* @param x 横坐标
* @param 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;
}
//通过改变direct来控制坦克方向,我们规定 (0:向上 1:向右 2:向下 3:向左)
switch (direct){//根据坦克方向来绘制坦克
case 0: //表示向上
g.fill3DRect(x,y,10,60,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); //左轮子
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); //左轮子
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); //左轮子
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("没有处理");
}
}
}
坦克父类 定义基本的属性,速度,方向,移动等
package com.tankgame;
public class Tank {
//私有变量需要提供getter和setter方法
private int x;
private int y;
private int direct; //0:向上 1:向右 2:向下 3:向左\
private int speed=1; //坦克速度初始化为1
boolean alive =true;
//让坦克移动的方法(通过改变x,y的值来实现)
public void moveUp(){ //上
if (y>0) { //如果y大于0则继续移动
y -= speed;
}
}
public void moveRight(){
if (x+80<1000) { //这里加60是算入坦克自身长度因为坐标的定义是在左上角,小于1000是车头(x+60)小于右边边界
x += speed;
}
}
public void moveDown(){
if (y+105<750) {
y += speed;
}
}
public void moveLift(){
if (x>0) {
x -= speed;
}
}
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
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 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;
}
}
我的坦克类 继承父类,并实现射击方法
package com.tankgame;
import java.util.Vector;
public class Hero extends Tank { //此类继承了Tank类可以使用其所有的公有方法
shot shot=null; //定义子弹对象(线程)
//将子弹加入集合里实现连续发射
Vector<shot> shots = new Vector<>();
public Hero(int x, int y) {
super(x, y);
}
//提供一个子弹射击的方法
public void showEnemyTank(){
//规定子弹发射数量直到销毁为止
if (shots.size()==10){
return;
}
//根据hero对象的位置和方向来创建shot对象
switch (getDirect()){
case 0: //向上
shot=new shot(getX()+20,getY(),0);
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.add(shot);
//启动shot线程
Thread thread = new Thread(shot);
thread.start();
}
}
敌人坦克类 继承父类,实现了坦克射击和自由移动方法
package com.tankgame;
import java.util.Vector;
import static java.lang.Thread.sleep;
public class EnemyTank extends Tank implements Runnable{ //实现线程接口让坦克动起来
//在敌人坦克类中使用vector保存多个shot线程
Vector <shot> shots=new Vector<>();
//新增属性用来获取敌人坦克的vector
Vector<EnemyTank> enemyTanks = new Vector<>();
public boolean alive=true; //生命力
public EnemyTank(int x, int y) {
super(x, y);
}
//把myPanel的vector成员传入到EnemyTank的enemyTanks中
public void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
this.enemyTanks = enemyTanks;
}
//编写方法判断vector里坦克是否接触
public boolean isTouchEnemyTank() {
//根据当前坦克方向来判断
switch (this.getDirect()) {
case 0: //上
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);//将一个坦克对象取出,并依次与其他坦克坐标比较
if (enemyTank != this) {
//如果是比较的敌人坦克是上下
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
//当前坦克的左上坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 40
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 60) {
return true;
}
//当前坦克右上坐标
if (this.getX() + 40 >= enemyTank.getX() &&
this.getX() + 40 <= enemyTank.getX() + 40
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 60) {
return true;
}
}
//如果敌人坦克是左右
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
//当前坦克的左上坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 60
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 40) {
return true;
}
//当前坦克右上坐标
if (this.getX() + 40 >= enemyTank.getX() &&
this.getX() + 40 <= enemyTank.getX() + 60
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 40) {
return true;
}
}
}
}
case 1: //右
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);//将一个坦克对象取出,并依次与其他坦克坐标比较
if (enemyTank != this) {
//如果是比较的敌人坦克是上下
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
//当前坦克的右上坐标
if (this.getX() + 60 >= enemyTank.getX() &&
this.getX() + 60 <= enemyTank.getX() + 40
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 60) {
return true;
}
//当前坦克右下坐标
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;
}
}
//如果敌人坦克是左右
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
//当前坦克的右上坐标
if (this.getX() + 60 >= enemyTank.getX() &&
this.getX() + 60 <= enemyTank.getX() + 60
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 40) {
return true;
}
//当前坦克右下坐标
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;
}
}
}
}
case 2: //下
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);//将一个坦克对象取出,并依次与其他坦克坐标比较
if (enemyTank != this) {
//如果是比较的敌人坦克是上下
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
//当前坦克的左下坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 40
&& this.getY() + 60 >= enemyTank.getY()
&& this.getY() + 60 <= enemyTank.getY() + 60) {
return true;
}
//当前坦克右下坐标
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;
}
}
//如果敌人坦克是左右
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
//当前坦克的左下坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 60
&& this.getY() + 60 >= enemyTank.getY()
&& this.getY() + 60 <= enemyTank.getY() + 40) {
return true;
}
//当前坦克右下坐标
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;
}
}
}
}
case 3: //左
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);//将一个坦克对象取出,并依次与其他坦克坐标比较
if (enemyTank != this) {
//如果是比较的敌人坦克是上下
if (enemyTank.getDirect() == 0 || enemyTank.getDirect() == 2) {
//当前坦克的左上坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 40
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 60) {
return true;
}
//当前坦克左下坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 40
&& this.getY() + 40 >= enemyTank.getY()
&& this.getY() + 40 <= enemyTank.getY() + 60) {
return true;
}
}
//如果敌人坦克是左右
if (enemyTank.getDirect() == 1 || enemyTank.getDirect() == 3) {
//当前坦克的左上坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 60
&& this.getY() >= enemyTank.getY()
&& this.getY() <= enemyTank.getY() + 40) {
return true;
}
//当前坦克左下坐标
if (this.getX() >= enemyTank.getX() &&
this.getX() <= enemyTank.getX() + 60
&& this.getY() + 40 >= enemyTank.getY()
&& this.getY() + 40 <= enemyTank.getY() + 40) {
return true;
}
}
}
}
}
//如果上述都没满足则没碰撞
return false;
}
@Override
public void run() {
while (true) {
//判断敌方坦克子弹是否为零如果没有则创建一个子弹并放入集合中
if (alive&&shots.size()<5){
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);
//启动线程
new Thread(s).start();
}
//根据坦克方向来移动
switch (getDirect()) {
case 0:
//让坦克保持在一个方向时多走几步
for (int i = 0; i < 30; i++) {
//如果坦克没有碰撞则继续走
if (!isTouchEnemyTank()) {
moveUp();
}
try {
Thread.sleep(50); //每次要休眠
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break; //让一个坦克走完防止坦克之间穿透
case 1:
for (int i = 0; i < 30; i++) {
if (!isTouchEnemyTank()) {
moveRight();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 2:
for (int i = 0; i < 30; i++) {
if (!isTouchEnemyTank()){
moveDown();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 3:
for (int i = 0; i < 30; i++) {
if (!isTouchEnemyTank()) {
moveLift();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
//随机移动的改变坦克方向(0~3)
setDirect((int) (Math.random() * 4));
//写多线程要考虑线程结束条件,当坦克被击中
if (alive == false) {
break; //退出线程
//
}
}
}
}
射击类 实现子弹射击线程
package com.tankgame;
public class shot implements Runnable{//坦克射击子弹线程
int x; //横坐标
int y; //纵坐标
int direct =0; //方向
int speed =3; //初始化速度
boolean alive = true; //子弹默认存活
//构造器
public shot(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
@Override
//一颗子弹的线程
public void run() { //射击
while (alive) {
try {
Thread.sleep(50); //让线程休眠50ms
} catch (InterruptedException e) {
e.printStackTrace();
}
//根据方向来改变x,y坐标
switch (direct) {
case 0: //上
y -= speed;
break;
case 1: //右
x += speed;
break;
case 2: //下
y += speed;
break;
case 3: //左
x -= speed;
break;
}
//当子弹到边界时销毁或者碰到坦克时
if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && alive)) {
alive = false; //此子弹的线程终止
break;
}
}
}
}
爆炸类 通过图片来实现坦克爆炸效果
package com.tankgame;
public class Bomb { //定义爆炸效果
int x,y;
int life =9 ; //设置生命周期
boolean alive =true;
public Bomb(int x, int y) {
this.x = x;
this.y = y;
}
//减少生命值
public void LifeDown(){ //配合出现图片爆炸效果
if (life>0){
life--;
}else {
alive=false;
}
}
}
记录类 记录类用于和文件交互,实现游戏的记录
package com.tankgame;
import java.io.*;
import java.util.Vector;
//记录数据和文件交互
public class Recorder {
//定义变量记录击毁坦克数
private static int allEnemyTankNum = 0;
//定义IO对象 读入数据到文件中
private static BufferedWriter bw = null;
private static BufferedReader br = null;
private static String recorderFile = "d://坦克大战//data//data.txt";
//定义vector指向MyPanel对象的敌人坦克vector
private static Vector<EnemyTank> enemyTanks = null;
//定义node的vector记录坦克信息
private static Vector<Node> nodes = new Vector<>();
//提供其set方法
public static void setEnemyTanks(Vector<EnemyTank> enemyTanks) {
Recorder.enemyTanks = enemyTanks;
}
//判断文件是否存在
public static String getRecorderFile() {
return recorderFile;
}
//当游戏退出时,将allEnemyTankNum保存到recorderFile中
public static void keepRecorder () {
try {
bw = new BufferedWriter(new FileWriter(recorderFile));
bw.write(allEnemyTankNum + "\r\n"); //换行
//遍历敌人的坦克的vector,然后根据情况保存即可
//oop设置一个属性,然后通过setXxx得到 敌人坦克的vector
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
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 Vector<Node> getNodeAndEnemyTankRec(){
try {
br = new BufferedReader(new FileReader(recorderFile));
allEnemyTankNum = Integer.parseInt(br.readLine()); //将第一行字符串读取(即坦克击毁数量)
//循环读取文件生成node集合
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);//加入集合中
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (br!=null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return nodes;
}
public static int getAllEnemyTankNum() {
return allEnemyTankNum;
}
public static void setAllEnemyTankNum(int allEnemyTankNum) {
Recorder.allEnemyTankNum = allEnemyTankNum;
}
//当我方坦克击毁坦克将allEnemyTankNum加1
public static void addallEnemyTankNum(){
allEnemyTankNum++;
}
}
结点类 实现记录一个坦克的信息用于文件交互
package com.tankgame;
/**
* 一个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;
}
}
最后是音乐类 实现游戏音乐的播放
package com.tankgame;
import java.io.*;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
//播放声音的类
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();
}
}
}
最后需要把三张爆炸图片和音乐下载,导入到项目中的同级目录中使用,路径格式等都需要修改
声明:坦克大战也是我的跟着老韩学习的成果,十分感谢!