这是我的坦克游戏大战的最后一版,里面添加很多新的功能。这个坦克大战的有很多不足之处,但是对于初学者来说依然是一个很好的练习项目,从中我们可以学习Java基础知识,将知识与项目结合,学习面向对象编程思想,最主要的是编程的逻辑练习,代码往往不像是写文章从上到下一气呵成完成,中间很可能为增加一个功能来添加一个类一个方法等,中间有很多细节需要我们考虑。文章最后会附加该坦克大战涉及的相关素材。

功能:

1.防止坦克重叠

2.可以分关(闪烁效果)

3.记录成绩(小型游戏都是写在文件中)

4.存盘退出,可以记录但是敌人坦克的坐标

5.可以恢复上局,继续玩

6.坦克的声音

坦克大战最终版本(2.1)

MyTanKGame类:

/*
* 功能:
* 1.防止坦克重叠
* 2.可以分关(闪烁效果)
* 3.记录成绩(小型游戏都是写在文件中)
* 4.存盘退出,可以记录但是敌人坦克的坐标
* 5.可以恢复上局,继续玩
* 6.坦克的声音
*/
package com.fanghua6; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Vector;
import javax.swing.*; public class MyTankGame1_7 extends JFrame implements ActionListener { Mypanel1_2 mp = null; // 定义开始面板
MyStartPanel msp = null;
// 做出菜单
JMenuBar jmb = null;
// 开始游戏
JMenu jm1 = null;
JMenuItem jmi1 = null;
// 退出游戏
JMenuItem jmi2 = null;
// 存盘退出
JMenuItem jmi3 = null;
JMenuItem jmi4 = null; public static void main(String[] args) {
new MyTankGame1_7();
} // 构造函数
public MyTankGame1_7() {
// mp = new Mypanel1_2(); // 启动mp线程
// Thread t = new Thread(mp);
// t.start(); // this.add(mp);
// 注册监听
// this.addKeyListener(mp);
// 创建菜单及菜单选项
jmb = new JMenuBar();
jm1 = new JMenu("游戏(G)");
jm1.setMnemonic('G');
jmi1 = new JMenuItem("开始新游戏(N)");
jmi1.setMnemonic('N'); jmi2 = new JMenuItem("退出游戏(E)");
jmi2.setMnemonic('E'); jmi3 = new JMenuItem("存盘退出(C)");
jmi3.setMnemonic('C'); jmi4 = new JMenuItem("继续上局(S)");
jmi4.setMnemonic('S'); // jmi1相应
jmi1.addActionListener(this);
jmi1.setActionCommand("newgame");
jmi2.addActionListener(this);
jmi2.setActionCommand("exit");
jmi3.addActionListener(this);
jmi3.setActionCommand("saveExit");
jmi4.addActionListener(this);
jmi4.setActionCommand("conGame"); jm1.add(jmi1);
jm1.add(jmi2);
jm1.add(jmi3);
jm1.add(jmi4); jmb.add(jm1); // 加了开始面板(上面的全注释掉)
msp = new MyStartPanel();
this.add(msp);
// 启动msp面板
Thread t = new Thread(msp);
t.start(); this.setJMenuBar(jmb); this.setSize(600, 500);
this.setTitle("我的坦克大战");
ImageIcon icon = new ImageIcon("images\\tanke.png");
this.setIconImage(icon.getImage());
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true); } @Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
// 对用户不同的点击做出不同的处理
if (e.getActionCommand().equals("newgame")) { // 创建战场面板
mp = new Mypanel1_2("newGame");
// 启动mp线程
Thread t = new Thread(mp);
t.start();
// 先把msp删掉,再加mp
this.remove(msp);
this.add(mp);
// 注册监听
this.addKeyListener(mp);
// 显示,刷新JFrame(这个很重要)
this.setVisible(true);
} else if (e.getActionCommand().equals("exit")) { // 用户退出系统的菜单(保存击毁敌人数量)
Recorder.keepRecording(); System.exit(0);
}// 对存盘退出的处理
else if (e.getActionCommand().equals("saveExit")) { /*
* 当时这里这样处理的,大错特错。new两次 工作 new Recorder().setEts(mp.ets);new
* 保存击毁敌人的数量和敌人的坐标 Recorder().keepRecAndEnemyTank();
*/ // 工作
Recorder rd = new Recorder();
rd.setEts(mp.ets);
// 保存击毁敌人的数量和敌人的坐标
rd.keepRecAndEnemyTank(); // 退出
System.exit(0);
} else if (e.getActionCommand().equals("conGame")) {
// 继续游戏
// 创建战场面板
mp = new Mypanel1_2("con"); // 不在这:mp.nodes = new Recorder().getNodesAndEnNums();
// 启动mp线程
Thread t = new Thread(mp);
t.start();
// 先把msp删掉,再加mp
this.remove(msp);
this.add(mp);
// 注册监听
this.addKeyListener(mp);
// 显示,刷新JFrame(这个很重要)
this.setVisible(true); }
}
} // 提示面板(用线程来实现闪烁效果)
class MyStartPanel extends JPanel implements Runnable {
// 做一个开关
int times = 0; public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 400, 300);
// 提示信息
if (times % 2 == 0) {
g.setColor(Color.yellow);
// 开关信息的字体
Font myFont = new Font("华文新魏", Font.BOLD, 30);
g.setFont(myFont);// 别忘了
g.drawString("stage: 1", 140, 200);
}
} @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
// 休眠
try {
Thread.sleep(500);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
times++;
// 重画
this.repaint();
}
} } // 我的面板,拓宽思路:Panel本身就是一个刷新体
class Mypanel1_2 extends JPanel implements java.awt.event.KeyListener, Runnable { // 定义我的坦克
Hero1_2 hero = null; // 判断是续上局,还是新游戏 // 定义敌人的坦克(不止一辆,线程安全,集合)
Vector<EnemyTank> ets = new Vector<EnemyTank>();
Vector<Node> nodes = new Vector<Node>(); // 定义炸弹集合
Vector<Bomb> bombs = new Vector<Bomb>(); int enSize = 4;// 敌人坦克保持4个 // 定义三张图片(三张图片才能组成一颗炸弹)
Image image1 = null;
Image image2 = null;
Image image3 = null; // 构造函数(续局,变成含参的构造函数)
public Mypanel1_2(String flag) { // 恢复记录(写在这里)
Recorder.getRecoring(); hero = new Hero1_2(70, 100); if (flag.equals("newGame")) {
// 初始化敌人的坦克
for (int i = 0; i < enSize; i++) {
// 创建一辆敌人的坦克
EnemyTank et = new EnemyTank((i + 1) * 50, 0);
et.setColor(0);
// 坦克默认反向是0(向上),这里改一下
et.setDirect(2); // 将MyPanel的敌人坦克向量交给该敌人坦克
et.setEts(ets); // 启动敌人的坦克
Thread t = new Thread(et);
t.start();
// 给敌人坦克添加一颗子弹
Shot s = new Shot(et.x + 10, et.y + 30, 2);
// 加入给敌人的坦克
et.ss.add(s); Thread t2 = new Thread(s);
t2.start();
ets.add(et);
}
} else { nodes = new Recorder().getNodesAndEnNums(); for (int i = 0; i < nodes.size(); i++) { Node node = nodes.get(i);
// 创建一辆敌人的坦克
EnemyTank et = new EnemyTank(node.x, node.y);
et.setColor(0);
et.setDirect(node.direct); // 将MyPanel的敌人坦克向量交给该敌人坦克
et.setEts(ets); // 启动敌人的坦克
Thread t = new Thread(et);
t.start();
// 给敌人坦克添加一颗子弹
Shot s = new Shot(et.x + 10, et.y + 30, 2);
// 加入给敌人的坦克
et.ss.add(s); Thread t2 = new Thread(s);
t2.start();
ets.add(et);
}
}
// 初始话图片,这样做击中第一个坦克,爆炸的效果不明显。下面优化
image1 = Toolkit.getDefaultToolkit().getImage(
Panel.class.getResource("/bomb_1.gif"));
image2 = Toolkit.getDefaultToolkit().getImage(
Panel.class.getResource("/bomb_2.gif"));
image3 = Toolkit.getDefaultToolkit().getImage(
Panel.class.getResource("/bomb_3.gif")); // 引包:import javax.imageio.ImagesssIO;
// try {
// image1=ImageIO.read(new File("/bomsb_1.gif"));
// image2=ImageIO.read(new File("/bomb_2.gif"));
// image3=ImageIO.read(new File("/bomb_3.gif"));
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } // 在Mypanel1_2初始化里,放置开战音效,加载即播放
// 以后再添加爆炸效果的声音也是这样做的思路
AePlayWave apw = new AePlayWave("E:\\111.wav");
apw.start(); } // 提示信息(只需要画笔即可)
public void showInfo(Graphics g) {
// 画出提示信息坦克
this.drawTank(70, 310, g, 0, 0);
g.setColor(Color.black);
g.drawString(Recorder.getEnNum() + "", 100, 330); this.drawTank(70, 360, g, 0, 1);
g.setColor(Color.black);
g.drawString(Recorder.getMyLife() + "", 100, 380); // 画出玩家的总成绩
g.setColor(Color.black);
Font f = new Font("宋体", Font.BOLD, 20);
g.setFont(f);
g.drawString("你的总成绩", 410, 30);
this.drawTank(410, 60, g, 0, 0); g.setColor(Color.black);
g.drawString(Recorder.getAllEnNum() + "", 440, 80);
} // 重写paint函数
public void paint(Graphics g) {
// 一定要调用
super.paint(g);
g.fillRect(0, 0, 400, 300);
// 画出提示信息
this.showInfo(g); // 画出自己的坦克(将方向填进去)
if (hero.isLive == true) {
this.drawTank(hero.getX(), hero.getY(), g, this.hero.direct, 1);
}
// 从ss中取出每一颗子弹,并画出
for (int i = 0; i < hero.ss.size(); i++) { Shot myShot = hero.ss.get(i);
if (myShot != null && myShot.isLive == true) {
g.draw3DRect(myShot.x, myShot.y, 1, 1, false); /*
* 画出一颗子弹(后添加 hero.s.isLive==true,节省资源) if (hero.s != null
* &&hero.s.isLive == true) { g.draw3DRect(hero.s.x, hero.s.y,
* 1, 1,false); }
*/
}
if (myShot.isLive == false) {
// 从ss(向量)中删除该子弹
// hero.ss.remove(i);会报异常。
hero.ss.remove(myShot);
}
} // 画出炸弹
for (int i = 0; i < bombs.size(); i++) {
// 取出炸弹
Bomb b = bombs.get(i);
if (b.life > 6) {
g.drawImage(image1, b.x, b.y, 30, 30, this);
} else if (b.life > 4) {
g.drawImage(image2, b.x, b.y, 30, 30, this);
} else {
g.drawImage(image3, b.x, b.y, 30, 30, this);
}
// 让b的生命值减小
b.lifeDown();
// 如果炸弹生命值为零,就把该炸弹从bombs向量中去掉
if (b.life == 0) {
bombs.remove(b);
} } // 画出敌人的坦克
for (int i = 0; i < ets.size(); i++) {
EnemyTank et = ets.get(i);
if (et.isLive) {
this.drawTank(et.getX(), et.getY(), g, et.getDirect(), 0); // 画出敌人的子弹
for (int j = 0; j < et.ss.size(); j++) {
// 取出子弹
Shot enemyShot = et.ss.get(j);
if (enemyShot != null && enemyShot.isLive == true) {
g.draw3DRect(enemyShot.x, enemyShot.y, 1, 1, false);
}
if (enemyShot.isLive == false) {
// 如果敌人的坦克死亡了,就从Vector中删除
et.ss.remove(enemyShot);
}
}
}
}
} // 敌人的坦克是否击中我
public void hitMe() {
// 取出每一个敌人的坦克
for (int i = 0; i < this.ets.size(); i++) {
// 取出坦克
EnemyTank et = ets.get(i); // 取出每一颗子弹
for (int j = 0; j < et.ss.size(); j++) {
// 取出子弹
Shot enemyShot = et.ss.get(j);
if (hero.isLive) {
if (this.hitTank(enemyShot, hero)) { }
}
}
}
} // 判断我的子弹是否击中敌人的坦克
public void hitEnemyTank() {
// 判断是否击中敌人的坦克
for (int i = 0; i < hero.ss.size(); i++) {
// 取出子弹
Shot myShot = hero.ss.get(i);
// 判断子弹是否有效
if (myShot.isLive) {
// 取出每个坦克,与它判断
for (int j = 0; j < ets.size(); j++) {
// 取出坦克
EnemyTank et = ets.get(j); if (et.isLive) {
if (this.hitTank(myShot, et)) { // 减少敌人数量
Recorder.reduceEnNum();
// 增加我的记录
Recorder.addEnNumRec();
}
} }
}
}
} // 写一个函数 专门判断是否击中敌人坦克(原来第二参数: EnemyTank et)
// void改成boolean 判断击中的目标是谁 public boolean hitTank(Shot s, Tank1_2 et) {
boolean b2 = false; // 判断该坦克的方向
switch (et.direct) {
// 如果敌人坦克的方向是上或者是下
case 0:
case 2:
if (s.x > et.x && s.x < et.x + 20 && s.y > et.y && s.y < et.y + 30) {
// 击中
// 子弹死亡
s.isLive = false;
// 敌人坦克死亡
et.isLive = false;
b2 = true;
// 创建一颗炸弹,放入Vector
Bomb b = new Bomb(et.x, et.y);
// 放入Vector
bombs.add(b); } break;
case 1:
case 3:
if (s.x > et.x && s.x < et.x + 30 && s.y > et.y && s.y < et.y + 20) {
// 击中
// 子弹死亡
s.isLive = false;
// 敌人坦克死亡
et.isLive = false;
b2 = true;
// 创建一颗炸弹,放入Vector
Bomb b = new Bomb(et.x, et.y);
// 放入Vector
bombs.add(b); }
break;
} return b2; } // 画出坦克的函数
public void drawTank(int x, int y, Graphics g, int direct, int type) {
// 坦克类型
switch (type) {
case 0:
g.setColor(Color.green);
break;
case 1:
g.setColor(Color.yellow);
break;
}
// 方向设置
switch (direct) {
// 向上
case 0:
g.fill3DRect(x, y, 5, 30, false);
g.fill3DRect(x + 15, y, 5, 30, false);
g.fill3DRect(x + 5, y + 5, 10, 20, false);
g.fillOval(x + 5, y + 10, 10, 10);
g.drawLine(x + 10, y + 15, x + 10, y);
break;
// 向右
case 1:
g.fill3DRect(x, y, 30, 5, false);
g.fill3DRect(x, y + 15, 30, 5, false);
g.fill3DRect(x + 5, y + 5, 20, 10, false);
g.fillOval(x + 10, y + 5, 10, 10);
g.drawLine(x + 15, y + 10, x + 30, y + 10);
break;
// 向下
case 2:
g.fill3DRect(x, y, 5, 30, false);
g.fill3DRect(x + 15, y, 5, 30, false);
g.fill3DRect(x + 5, y + 5, 10, 20, false);
g.fillOval(x + 5, y + 10, 10, 10);
g.drawLine(x + 10, y + 15, x + 10, y + 30);
break;
// 向左
case 3:
g.fill3DRect(x, y, 30, 5, false);
g.fill3DRect(x, y + 15, 30, 5, false);
g.fill3DRect(x + 5, y + 5, 20, 10, false);
g.fillOval(x + 10, y + 5, 10, 10);
g.drawLine(x + 15, y + 10, x, y + 10);
break;
} } @Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
// 已更正为顺时针
if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W) {
this.hero.moveUp();
this.hero.setDirect(0);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT
|| e.getKeyCode() == KeyEvent.VK_D) {
this.hero.setDirect(1);
this.hero.moveRight();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN
|| e.getKeyCode() == KeyEvent.VK_S) {
this.hero.moveDown();
this.hero.setDirect(2);
} else if (e.getKeyCode() == KeyEvent.VK_LEFT
|| e.getKeyCode() == KeyEvent.VK_A) {
this.hero.moveLeft();
this.hero.setDirect(3);
} else if (e.getKeyCode() == KeyEvent.VK_J) {
// 将J键设置为发出子弹
// 子弹连发,J被按几下,发几颗:this.hero.shotEnemy();
// this.repaint();在run函数里,不应该设计在这里
if (this.hero.ss.size() <= 4) {
this.hero.shotEnemy();
}
}
// 必须重新绘制Panel
this.repaint();
} @Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub } @Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub } @Override
public void run() {
// TODO Auto-generated method stub
// 每隔100毫秒去重绘
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // 谨记这里:由于这里多写了这些,线程每次都被重新调用
// showInfo的信息不会动态改变 调了好久好久。苦笑脸
// 判断是否击中(写在这里,虽然在每次重绘的时候都要调用,但是没办法)
// 每一颗子弹都要和每个坦克进行匹配
// for (int i = 0; i < hero.ss.size(); i++) {
// // 取出子弹
// Shot myShot = hero.ss.get(i);
// // 判断子弹是否有效
// if (myShot.isLive) {
// // 取出每个坦克,与它判断
// for (int j = 0; j < ets.size(); j++) {
// // 取出坦克
// EnemyTank et = ets.get(j);
//
// if (et.isLive) {
// this.hitTank(myShot, et);
// }
// }
// }
// }
this.hitEnemyTank(); this.hitMe(); // 重绘
this.repaint();
}
}
}

Menbers类:

package com.fanghua6;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Vector;
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();
}
}
} // 继续上一局的记记录点的类
class Node {
int x;
int y;
int direct; public Node(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
} // 记录类,同时也可以保存玩家的设置(一般都是事先写在缓存里面的)
class Recorder {
// 记录每关有多少敌人
private static int enNum = 20;
// 设置我有多少可以用的坦克
private static int myLife = 3;
// 记录一共消灭多少敌人
private static int allEnNum = 0; // 从文件中恢复记录点
static Vector<Node> nodes = new Vector<Node>(); private static FileWriter fw = null;
private static BufferedWriter bw = null;
private static FileReader fr = null;
private static BufferedReader br = null; private Vector<EnemyTank> ets = new Vector<EnemyTank>(); // 完成读取的函数(记录点和敌人的数量)
public Vector<Node> getNodesAndEnNums() {
try {
fr = new FileReader("E:/Tanke.txt");
br = new BufferedReader(fr);
String n = "";
n = br.readLine();// 设置先读第一行
allEnNum = Integer.parseInt(n);
// 接着读
while ((n = br.readLine()) != null) {
String[] xyz = n.split(" ");
// 在知道只有三条数据的情况下,用了这个方法。否则要用for循环
Node node = new Node(Integer.parseInt(xyz[0]),
Integer.parseInt(xyz[1]), Integer.parseInt(xyz[2]));
nodes.add(node);
} } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
br.close();
fr.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return nodes;
} public Vector<EnemyTank> getEts() {
return ets;
} public void setEts(Vector<EnemyTank> ets) {
this.ets = ets;
} // 保存击毁敌人的数量和击毁敌人坦克的坐标、方向
public void keepRecAndEnemyTank() {
try {
fw = new FileWriter("E:/Tanke.txt");
bw = new BufferedWriter(fw); bw.write(allEnNum + "\r\n"); // 保存当前的敌人坦克的坐标和方向
for (int i = 0; i < ets.size(); i++) {
// 取出第一个坦克
EnemyTank et = ets.get(i);
if (et.isLive) {
// 活的就保存
String recode = et.x + " " + et.y + " " + et.direct;
// 写入
bw.write(recode + "\r\n"); }
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} // 从文件中读取,记录
public static void getRecoring() {
try {
fr = new FileReader("E:/Tanke.txt");
br = new BufferedReader(fr);
String n = br.readLine();
allEnNum = Integer.parseInt(n);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
br.close();
fr.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} } // 把玩家击毁敌人坦克数量保存到文件中
public static void keepRecording() {
try {
fw = new FileWriter("E:/Tanke.txt");
bw = new BufferedWriter(fw); bw.write(allEnNum + "\r\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} } } public static int getAllEnNum() {
return allEnNum;
} public static void setAllEnNum(int allEnNum) {
Recorder.allEnNum = allEnNum;
} public static int getEnNum() {
return enNum;
} public static void setEnNum(int enNum) {
Recorder.enNum = enNum;
} public static int getMyLife() {
return myLife;
} public void setMyLife(int myLife) {
Recorder.myLife = myLife;
} // 减少敌人数
public static void reduceEnNum() {
enNum--;
} // 消灭敌人
public static void addEnNumRec() {
allEnNum++;
} } // 炸弹类(没必要定义为线程,因为它不会移动,没有坐标改变)
class Bomb {
// 定义炸弹的坐标
int x, y;
int life = 9;// 炸弹的生命(三张图片)
// 可以看出isLive很有用,它可以决定类(或者对象)要不要展现在面板上
boolean isLive = true; public Bomb(int x, int y) {
this.x = x;
this.y = y;
} // 炸弹减少生命值
public void lifeDown() {
if (life > 0) {
life--;
} else {
this.isLive = false;
}
} } // 子弹类
class Shot implements Runnable {
int x;
int y;
int direct;
// 设置子弹的消亡(默认活着的)
boolean isLive = true;
// speed要给个初始值1,之前给0,按J键,子弹没有动
int speed = 1; public Shot(int x, int y, int direct) {
super();
this.x = x;
this.y = y;
this.direct = direct; } @Override
public void run() {
// TODO Auto-generated method stub while (true) {
// 设置子弹休息50毫秒 try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
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 > 400 || y < 0 || y > 300) {
this.isLive = false;
break;
}
}
}
} // 坦克类
class Tank1_2 { int x = 0;
int y = 0;
boolean isLive = true; // 坦克方向:0表示上,1表示右,2表示下,3表示左
int direct = 0;
int speed = 1;
// 坦克的颜色
int color; public int getColor() {
return color;
} public void setColor(int color) {
this.color = color;
} 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;
} // 构造函数
public Tank1_2(int x, int y) {
this.x = x;
this.y = y; } } // 敌人的坦克(做成线程,会移动)
class EnemyTank extends Tank1_2 implements Runnable { int times = 0;
// 定义一个向量,可以访问MyPanel上所有敌人的坦克
Vector<EnemyTank> ets = new Vector<EnemyTank>();
// 定义向量,可以存放敌人的子弹
Vector<Shot> ss = new Vector<Shot>(); // 敌人添加子弹,应该刚刚创建坦克和敌人子弹死亡后 public EnemyTank(int x, int y) {
super(x, y);
} // 得到MyPaneld的敌人坦克向量
public void setEts(Vector<EnemyTank> vv) {
// 此时拥有一种能力,可以拿到yPan el上所有敌人的坦克
this.ets = vv;
} // 判断是否碰到别的敌人的坦克
public boolean isTouchOtherEnemy() {
boolean b = false; switch (this.direct) {
case 0:
// 我的坦克向上
for (int i = 0; i < ets.size(); i++) {
// 取出第一个坦克(很有可能是自己哦)
EnemyTank et = ets.get(i);
// 如果不是自己
if (et != this) {
// 如果敌人的方向是向下或者向下
if (et.direct == 0 || et.direct == 2) {
if (this.x >= et.x && this.x <= et.x + 20
&& this.y >= et.y && this.y <= et.y + 30) {
return true;
}
if (this.x + 20 >= et.x && this.x + 20 <= et.x + 20
&& this.y >= et.y && this.y <= et.y + 30) {
return true;
}
}
if (et.direct == 1 || et.direct == 3) {
if (this.x >= et.x && this.x <= et.x + 30
&& this.y >= et.y && this.y <= et.y + 20) {
return true;
}
if (this.x + 20 >= et.x && this.x + 20 <= et.x + 30
&& this.y >= et.y && this.y <= et.y + 20) {
return true;
}
}
}
}
break;
case 1:
for (int i = 0; i < ets.size(); i++) {
EnemyTank et = ets.get(i);
if (et != this) {
if (et.direct == 0 || et.direct == 2) {
if (this.x + 30 >= et.x && this.x + 30 <= et.x + 20
&& this.y >= et.y && this.y <= et.y + 30) {
return true;
}
if (this.x + 30 >= et.x && this.x + 30 <= et.x + 20
&& this.y + 20 >= et.y
&& this.y + 20 <= et.y + 30) {
return true;
}
}
if (et.direct == 1 || et.direct == 3) {
if (this.x + 30 >= et.x && this.x + 30 <= et.x + 30
&& this.y >= et.y && this.y <= et.y + 20) {
return true;
}
if (this.x + 30 >= et.x && this.x + 30 <= et.x + 30
&& this.y + 20 >= et.y
&& this.y + 20 <= et.y + 20) {
return true;
}
}
}
}
break;
case 2:
for (int i = 0; i < ets.size(); i++) {
EnemyTank et = ets.get(i);
if (et != this) {
if (et.direct == 0 || et.direct == 2) {
if (this.x >= et.x && this.x <= et.x + 20
&& this.y + 30 >= et.y
&& this.y + 30 <= et.y + 30) {
return true;
}
if (this.x + 20 >= et.x && this.x + 20 <= et.x + 20
&& this.y + 30 >= et.y
&& this.y + 30 <= et.y + 30) {
return true;
}
}
if (et.direct == 1 || et.direct == 3) {
if (this.x >= et.x && this.x <= et.x + 30
&& this.y + 30 >= et.y
&& this.y + 30 <= et.y + 20) {
return true;
}
if (this.x + 20 >= et.x && this.x + 20 <= et.x + 30
&& this.y + 30 >= et.y
&& this.y + 30 <= et.y + 20) {
return true;
}
}
}
}
break;
case 3:
for (int i = 0; i < ets.size(); i++) {
EnemyTank et = ets.get(i);
if (et != this) {
if (et.direct == 0 || et.direct == 2) {
if (this.x >= et.x && this.x <= et.x + 20
&& this.y >= et.y && this.y <= et.y + 30) {
return true;
}
if (this.x >= et.x && this.x <= et.x + 20
&& this.y + 20 >= et.y
&& this.y + 20 <= et.y + 30) {
return true;
}
}
if (et.direct == 1 || et.direct == 3) {
if (this.x >= et.x && this.x <= et.x + 30
&& this.y >= et.y && this.y <= et.y + 20) {
return true;
}
if (this.x >= et.x && this.x <= et.x + 30
&& this.y + 20 >= et.y
&& this.y + 20 <= et.y + 20) {
return true;
}
}
}
}
break;
}
return b;
} @Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
// 设置坦克休息一会
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
switch (this.direct) {
case 0:
// 说明坦克正在向上运动(继续往上走,符合实际)
// y -= speed;设置坦克平滑移动的效果
for (int i = 0; i < 30; i++) {
if (y > 0 && !this.isTouchOtherEnemy()) {
y -= speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
case 1:
for (int i = 0; i < 30; i++) {
// 这里注意坐标起点问题不是(400x300)
if (x < 400 && !this.isTouchOtherEnemy()) {
x += speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
case 2:
for (int i = 0; i < 30; i++) {
if (y < 300 && !this.isTouchOtherEnemy()) {
y += speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
case 3:
for (int i = 0; i < 30; i++) {
if (x > 0 && !this.isTouchOtherEnemy()) {
x -= speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
break;
}
this.times++;
// 设置3秒发一颗子弹
if (times % 2 == 0) {
if (isLive) {
if (ss.size() < 5) {
Shot s = null;
// 没有子弹,添加
switch (direct) {
case 0:
s = new Shot(x + 10, y, 0);
ss.add(s);
break;
case 1:
s = new Shot(x + 30, y + 10, 1);
ss.add(s);
break;
case 2:
s = new Shot(x + 10, y + 30, 2);
ss.add(s);
break;
case 3:
s = new Shot(x, y + 10, 3);
ss.add(s);
break;
}
// 启动子弹线程
Thread t = new Thread(s);
t.start();
}
}
}
// 让坦克随机产生一个新的方向
this.direct = (int) (Math.random() * 4);
// 判断敌人坦克是否死亡了(双等号)
if (this.isLive == false) {
// 让坦克死亡,后退出线程
return;
} }
}
} // 我的坦克
class Hero1_2 extends Tank1_2 {
// 多个子弹,用向量创建
Vector<Shot> ss = new Vector<Shot>();
// 子弹
Shot s = null; public Hero1_2(int x, int y) {
super(x, y);
} // 坦克开火
public void shotEnemy() { switch (this.direct) {
case 0:
s = new Shot(x + 10, y, 0);
// 把子弹加入向量
ss.add(s);
break; case 1:
s = new Shot(x + 30, y + 10, 1);
// 把子弹加入向量
ss.add(s);
break; case 2:
s = new Shot(x + 10, y + 30, 2);
// 把子弹加入向量
ss.add(s);
break; case 3:
s = new Shot(x, y + 10, 3);
// 把子弹加入向量
ss.add(s);
break;
}
// 启动子弹线程(创建线程,赶紧传参,我在这里吃了大亏!)
Thread t = new Thread(s);
t.start();
} public void moveUp() {
y -= speed;
} public void moveRight() {
x += speed;
} public void moveDown() {
y += speed;
} public void moveLeft() {
x -= speed;
}
}

实现图片:

相关素材:

1.爆炸效果的图片(3张)

2.游戏声音素材

这个自己下载一个好啦(我的是上面命名为111.wav的文件,这里不支持上传)

Java坦克大战(四)的更多相关文章

  1. Java坦克大战 (四) 之子弹的产生

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  2. Java坦克大战(三)

    关于这个坦克大战的项目是在学习Java基础的时候,拿来练习的最近看到这些代码,感觉很亲切,就把他们都复制下来,编辑成博客.回首看去,Java基础的学习确实应该建立在找项目练习上,这样才能将学到的基础知 ...

  3. Java坦克大战(二)

    本文紧接上一篇讲解坦克大战这个项目,因为当初在学习的时候,是以这个案例逐步学习Java基础的,过程是先讲知识点,再将知识点逐步的融入到项目中,即给坦克添加新的功能.之前的Demo练习,想都记录下来.这 ...

  4. Java坦克大战(一)

    接下来的几篇博客,想记录一下通过学习坦克大战项目来循序渐进的学习Java基础.主要是为了巩固基础知识,当然学习编程重要的还是多敲,问题通常是在敲代码的过程中发现的,积累也是在敲代码中寻求的经验.这个坦 ...

  5. Java坦克大战 (七) 之图片版

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  6. Java坦克大战 (六) 之增加可玩性

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  7. Java坦克大战 (五) 之产生敌方坦克和爆炸效果

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  8. Java坦克大战 (三) 之可完全控制坦克朝八个方向运动

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  9. Java坦克大战 (二) 之画一个能动的圆圈代表坦克

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

随机推荐

  1. winform改变启动界面

    我们知道,有时做个小项目什么的,一般从登录开始,再到主页,再到其他业务,如果做到其他页面功能,调试时还要从登录页面一个个点进去,明显的降低开发进度. 这时,我们可以直接将目标界面改为启动页面即可. u ...

  2. Python数字与字符之间的转换

    Python数字与字符之间的转换 命令 意义 int(x [,base ]) 将x转换为一个整数 long(x [,base ]) 将x转换为一个长整数 float(x ) 将x转换到一个浮点数 co ...

  3. 利用IP核设计高性能的计数器

    利用Quartus II的LPM_counter IP核进行设计(利用IP核设计可以迅速高效的完成产品的设计) 新建工程 调用IP核 创建一个新的IP核 选择LMP_COUNTER,语言类型,输出路径 ...

  4. 推荐使用OpenLiveWriter在cnblogs上写的Blog

    这是我第一个使用OpenLiveWriter在cnblogs上写的Blog.不知道效果如何,但又很多功能我可以采用! 如表格功能:   Open Live Writer Write  on Web 优 ...

  5. Android Studio中的大量findViewById

    一. 分析 在Android Studio中开发时,findViewById是用的最多的函数之一.经常需要对返回的view进行类型转换,输入麻烦.代码丑陋. 本文提供两种方案来解决这个问题: 1.安装 ...

  6. Request参数值自动去空格

    /// <summary> /// TypeTrimHelper /// </summary> public static class TypeTrimHelper { /// ...

  7. 基于ε-NFA的正则表达式引擎

    正则表达式几乎每个程序员都会用到,对于这么常见的一个语言,有没有想过怎么去实现一个呢?乍一想,也许觉得困难,实际上实现一个正则表达式的引擎并没有想像中的复杂,<编译原理>一书中有一章专门讲 ...

  8. linux下的初始化系统systemd简科普

    systemd是什么?名字很奇怪,不认识. 扒一扒wiki,你就会知道我是谁了? 在下有眼不识泰山,原来你就是盘古老爷爷用的开天辟地大斧头啊. 贫穷不可怕,可怕的是贫穷限制了你的想象--------- ...

  9. 1-VScode格式化ESlint-方法(最全最好用方法!)

    1-VScode格式化ESlint-方法(最全最好用方法!)   ESlint:是用来统一JavaScript代码风格的工具,不包含css.html等. 背景: 近来研究前端,然后一直在百度上找VSc ...

  10. 阿里P9架构师讲解从单机至亿级流量大型网站系统架构的演进过程

    阶段一.单机构建网站 网站的初期,我们经常会在单机上跑我们所有的程序和软件.此时我们使用一个容器,如tomcat.jetty.jboos,然后直接使用JSP/servlet技术,或者使用一些开源的框架 ...