坦克大战(Java)
本文通过坦克大战的项目开发,来学习Java的绘图技术、事件监听、事件处理、线程、IO流等知识,项目功能还未完善,日后再补:
- 绘制我的坦克和敌人坦克
- 我的坦克能自由移动
- 我的坦克能发射子弹
- 敌人坦克中弹爆炸
- 敌人坦克也能自由移动
- 敌人坦克也能发射子弹
- 我的坦克中弹也爆炸
代码块
建立一个坦克类:
class Tank {
// 坦克的横坐标
int x = 0;
// 坦克的纵坐标 修改x、y的值就可以使坦克移动
int y = 0;
// 坦克的方向
int direct;
// 坦克的速度
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 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;
}
}
我的坦克
class Hero extends Tank {
// 子弹是坦克发射出来的,认为它是坦克的一个属性
Shot myShot = null;
Vector<Shot> myShots = new Vector<Shot>();
public Hero(int x, int y) {
// 利用父类的构造函数来传递子类的变量
super(x, y);
}
// 开火 (坦克的一种动作)
public void shotEnemy() {
// 子弹的坐标取决于坦克的位置、方向
switch (direct) {
case 0:
// 创建一颗子弹
myShot = new Shot(x + 12, y - 7, 0);
// 把子弹加入到向量中
myShots.add(myShot);
break;
case 1:
myShot = new Shot(x + 45, y + 18, 1);
myShots.add(myShot);
break;
case 2:
myShot = new Shot(x + 12, y + 45, 2);
myShots.add(myShot);
break;
case 3:
myShot = new Shot(x - 10, y + 18, 3);
myShots.add(myShot);
break;
}
// 启动子弹线程
Thread t = new Thread(myShot);
t.start();
}
//子弹向上移动
public void moveUp() {
y -= speed * 10;
}
//向右
public void moveRight() {
x += speed * 10;
}
//向下
public void moveDown() {
y += speed * 10;
}
//向左
public void moveLeft() {
x -= speed * 10;
}
}
敌人坦克
class EnemyTank extends Tank implements Runnable {
//坦克存活
boolean islive = true;
//敌人子弹
Shot enemyShot = null;
//敌人子弹组
Vector<Shot> enemyShots = new Vector<Shot>();
public EnemyTank(int x, int y) {
super(x, y);
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 坦克往哪个方向,根据惯性就让它往该方向再走几步
switch (this.direct) {
case 0:
//for循环是为了让坦克方向不要变化太快
for (int i = 0; i < 30; i++) {
if (y > 0) {
y -= speed;
}
try {
//休眠50ms
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 1:
for (int i = 0; i < 30; i++) {
if (x < 600) {
x += speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 2:
for (int i = 0; i < 30; i++) {
if (y < 500) {
y += speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 3:
for (int i = 0; i < 30; i++) {
if (x > 0) {
x -= speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
// 让坦克随机产生一个新的方向
this.direct = (int) (Math.random() * 4);
// 让坦克死亡后,退出线程
if (this.islive == false) {
break;
}
}
}
}
子弹
class Shot implements Runnable {
int x;
int y;
int direct;
int speed = 20;
// 是否还活着
boolean islive = true;
public Shot(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
// run函数是构造函数调用的
@Override
public void run() {
while (true) {
// 让子弹休息一下,避免内存消耗太大
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;
}
// 判断子弹是否碰到边缘
if (x < 0 || x > 600 || y < 0 || y > 500) {
islive = false;
break;
}
}
}
}
炸弹
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;
}
}
}
功能实现类
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
import javax.swing.*;
public class MyTankGame_2 extends JFrame {
MyTankPanel mtp = null;
public int x = 100;
public int y = 100;
public MyTankGame_2() {
mtp = new MyTankPanel(x, y);
// 启动mtp线程
Thread thread = new Thread(mtp);
thread.start();
this.add(mtp);
this.addKeyListener(mtp);
this.setBounds(300, 200, 600, 500);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
}
public static void main(String[] args) {
MyTankGame_2 mtg = new MyTankGame_2();
}
/**
* 我的面板
*
* @author PC
*
*/
class MyTankPanel extends JPanel implements KeyListener, Runnable {
// 定义一个我的坦克
Hero hero = null;
// 定义敌人的坦克组(采用Vector集合,线程安全)
Vector<EnemyTank> enemys = new Vector<EnemyTank>();
// 定义炸弹组
Vector<Bomb> bombs = new Vector<Bomb>();
// 敌人的坦克数量
int enemySize = 3;
// 定义3张图片,3张图片瞬间切换组成一颗炸弹
Image image1 = null;
Image image2 = null;
Image image3 = null;
// 构造方法各种初始化
public MyTankPanel(int x, int y) {
hero = new Hero(x, y);
// 初始化敌人的坦克
for (int i = 0; i < enemySize; i++) {
// 创建一个敌人坦克对象
EnemyTank et = new EnemyTank((i + 1) * 50, 0);
// 启动敌人坦克
Thread thread = new Thread(et);
thread.start();
// 创建敌人的子弹
Shot enemyShot = new Shot(et.x + 15, et.y + 40, 2);
Thread threadShot = new Thread(enemyShot);
threadShot.start();
// 敌人的坦克颜色
et.setColor(0);
// 敌人的坦克方向
et.setDirect(2);
// 将敌人对象添加到Vector集合中
enemys.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"));
}
/**
* 重写paint
*/
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0, 0, 600, 500);
// 画出自己的坦克
this.drawTank(hero.getX(), hero.getY(), g, this.hero.getDirect(), 0);
// 从向量中取出每颗子弹,并画出
for (int i = 0; i < hero.myShots.size(); i++) {
Shot myShot = hero.myShots.get(i);
// 画出一颗子弹
if (myShot != null && myShot.islive == true) {
g.draw3DRect(myShot.x, myShot.y, 1, 1, false);
}
if (myShot.islive == false) {
hero.myShots.remove(myShot);
}
}
// 画出敌人的坦克
for (int i = 0; i < enemys.size(); i++) {
EnemyTank et = enemys.get(i);
if (et.islive) {
this.drawTank(et.getX(), et.getY(), g, et.getDirect(), 1);
// 再画出敌人的子弹
for (int j = 0; j < et.enemyShots.size(); j++) {
// 取出子弹
Shot es = et.enemyShots.get(j);
// 判断子弹有没有效
if (es.islive) {
g.draw3DRect(es.x, es.y, 1, 1, false);
} else {
et.enemyShots.remove(es);
}
}
} else {
enemys.remove(et);
}
}
// 画出炸弹
for (int i = 0; i < bombs.size(); i++) {
System.out.println("击中了");
Bomb b = bombs.get(i);
if (b.life > 6) {
g.drawImage(image1, b.x, b.y, 40, 40, this);
} else if (b.life > 3) {
g.drawImage(image2, b.x, b.y, 40, 40, this);
} else {
g.drawImage(image3, b.x, b.y, 40, 40, this);
}
// 让b的生命值减小
b.lifeDown();
if (b.life == 0) {
bombs.remove(b);
}
}
}
// 判断子弹是否击中敌人坦克
public void isHitTank(Shot s, EnemyTank et) {
switch (et.direct) {
// 向上、向下
case 0:
case 2:
// 击中
if (s.x > et.x && s.x < et.x + 30 && s.y > et.y && s.y < et.y + 40) {
// 子弹死亡
s.islive = false;
// 敌人坦克死亡
et.islive = false;
// 创建一颗炸弹,放入Vector
Bomb b = new Bomb(et.x, et.y);
bombs.add(b);
}
case 1:
case 3:
// 击中
if (s.x > et.x && s.x < et.x + 40 && s.y > et.y && s.y < et.y + 30) {
s.islive = false;
et.islive = false;
// 创建一颗炸弹,放入Vector
Bomb b = new Bomb(et.x, et.y);
bombs.add(b);
}
}
}
/**
* 画出坦克的函数 (坦克的位置,画笔,坦克的方向,坦克的类型)
*
* @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.RED);
break;
}
// 判断方向
switch (direct) {
// 向上
case 0:
// 画出左边的矩形
g.fillRect(x, y, 5, 40);
// 画出右边的矩形
g.fillRect(x + 20, y, 5, 40);
// 画出中间的矩形
g.fillRect(x + 5, y + 5, 20, 30);
// 画出圆形
g.setColor(Color.YELLOW);
g.fillOval(x + 2, y + 9, 19, 19);
// 画出中间直线
g.drawLine(x + 12, y - 5, x + 12, y + 20);
break;
// 向右
case 1:
// 画出上面的矩形
g.fillRect(x, y + 7, 40, 5);
// 画出下面的矩形
g.fillRect(x, y + 32, 40, 5);
// 画出中间的矩形
g.fillRect(x + 5, y + 12, 30, 20);
// 画出圆形
g.setColor(Color.YELLOW);
g.fillOval(x + 10, y + 10, 19, 19);
// 画出中间直线
g.drawLine(x + 20, y + 20, x + 45, y + 20);
break;
// 向下
case 2:
// 画出左边的矩形
g.fillRect(x, y, 5, 40);
// 画出右边的矩形
g.fillRect(x + 20, y, 5, 40);
// 画出中间的矩形
g.fillRect(x + 5, y + 5, 20, 30);
// 画出圆形
g.setColor(Color.YELLOW);
g.fillOval(x + 2, y + 9, 19, 19);
// 画出中间直线
g.drawLine(x + 12, y + 20, x + 12, y + 45);
break;
// 向左
case 3:
// 画出上面的矩形
g.fillRect(x, y + 7, 40, 5);
// 画出下面的矩形
g.fillRect(x, y + 32, 40, 5);
// 画出中间的矩形
g.fillRect(x + 5, y + 12, 30, 20);
// 画出圆形
g.setColor(Color.YELLOW);
g.fillOval(x + 10, y + 10, 19, 19);
// 画出中间直线
g.drawLine(x - 10, y + 20, x + 20, y + 20);
break;
}
}
/**
* 键盘按下事件
*/
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_UP) {
this.hero.setDirect(0);
this.hero.moveUp();
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
this.hero.setDirect(1);
this.hero.moveRight();
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
this.hero.setDirect(2);
this.hero.moveDown();
} else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
this.hero.setDirect(3);
this.hero.moveLeft();
}
// 判断玩家是否按下J键
if (e.getKeyCode() == KeyEvent.VK_J) {
// 开火
if (hero.myShots.size() < 5) {
this.hero.shotEnemy();
}
}
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void run() {
// 每隔100ms去重绘
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 判断是否击中
for (int i = 0; i < hero.myShots.size(); i++) {
// 取出子弹
Shot shot = hero.myShots.get(i);
// 子弹存在
if (shot.islive) {
// 取出坦克
for (int j = 0; j < enemys.size(); j++) {
EnemyTank et = enemys.get(j);
// 判断坦克是否被子弹击中
if (et.islive) {
this.isHitTank(shot, et);
}
}
}
}
// 判断是否需要为敌人坦克添加子弹
for (int i = 0; i < enemys.size(); i++) {
EnemyTank et = enemys.get(i);
if (et.islive) {
for (int j = 0; j < et.enemyShots.size(); j++) {
Shot es = et.enemyShots.get(j);
// 没有子弹了
if (et.enemyShots.size() < 1) {
// 根据坦克方向添加子弹
Shot enemyShot = null;
switch (et.direct) {
case 0:
// 创建一颗子弹
enemyShot = new Shot(et.x + 12, et.y - 7, 0);
// 把子弹加入到向量中
et.enemyShots.add(enemyShot);
break;
case 1:
enemyShot = new Shot(et.x + 45, et.y + 18, 1);
et.enemyShots.add(enemyShot);
break;
case 2:
enemyShot = new Shot(et.x + 12, et.y + 45, 2);
et.enemyShots.add(enemyShot);
break;
case 3:
enemyShot = new Shot(et.x - 10, et.y + 18, 3);
et.enemyShots.add(enemyShot);
break;
}
// 启动子弹线程
Thread t = new Thread(enemyShot);
t.start();
}
}
}
}
// 重绘
this.repaint();
}
}
}
}