本次实现:
- 让敌方坦克能向随机方向移动
- 敌我双方均能发射子弹且能击毁目标
- 实现击中目标后产生爆炸效果
- 优化相关细节及参数,将部分功能封装成一个(函数)方法
详细代码:
1、
package com.tank;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class TankGame extends JFrame{
MyPanel mp = null;
public static void main(String[] args) {
TankGame test = new TankGame();
}
//构造函数
public TankGame(){
mp = new MyPanel();
Thread t = new Thread(mp);
t.start();//不启动run()方法不会定时重绘
this.add(mp);
this.addKeyListener(mp);
this.setSize(410, 300);
this.setTitle("坦克大战");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
}
//炸弹类
class Bomb{
int x, y;
int life = 13;
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;
}
}
}
class MyPanel extends JPanel implements KeyListener, Runnable{
//定义我的坦克
Hero hero = null;
//定义敌军坦克组,vector tanks 简写为vts
Vector <Enemy> vts= new Vector <Enemy>();
int eSize = 3;
//定义一个炸弹集合
Vector <Bomb> vBombs= new Vector<Bomb>();
//定义三张爆炸效果图
Image image1 = null;
Image image2 = null;
Image image3 = null;
public MyPanel(){
hero = new Hero(186, 235);
hero.setColor(0);
//初始化爆炸效果图
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"));
//初始化敌军坦克
for(int i=0; i<eSize; i++){
Enemy eTank = new Enemy(i*186, 0);
eTank.setColor(1);
eTank.setDirect(1);
vts.add(eTank);
Thread t = new Thread(eTank);
t.start();
}
}
//判断英雄坦克是否击中目标
public void hitEnemy(){
//取出每一颗存活的炮弹与坦克进行对比
for(int i=0; i<hero.vb.size(); i++){
Bullet b = hero.vb.get(i);
if(b.isLive){ //如果炮弹存活,则取出敌方存活的坦克与其匹配
for(int j=0; j<vts.size(); j++){
Enemy e = vts.get(j);
if(e.isLive){
this.isHit(b, e);//对比该炮弹是否击中该敌军坦克(即炮弹进入该坦克坐标区域)
}
}
}
}
}
//判断敌方坦克是否击中hero坦克
public void hitHero(){
//取出敌方坦克
for(int i=0; i<vts.size(); i++){
//取出坦克
Enemy e = vts.get(i);
//取出每一颗敌方炮弹
for(int j=0; j<e.eb.size(); j++){
Bullet b = e.eb.get(j);
this.isHit(b, hero);
}
}
}
//重写paint方法
public void paint(Graphics g){
super.paint(g);
g.fillRect(0, 0, 410, 300);
//调用画坦克函数 画出英雄的坦克
if(hero.isLive){
this.drawTank(this.hero.getX(), this.hero.getY(), g, this.hero.getDirect(), this.hero.getColor());
}
//从vb向量中取出炮弹
for(int i=0; i<hero.vb.size(); i++){
Bullet myBullet = hero.vb.get(i);
//画出炮弹
if(myBullet!=null && myBullet.isLive==true){//如果无isLive条件,子弹虽然不会移动,但是会一直存在
g.drawOval(myBullet.x, myBullet.y, 2, 2);
}
if(myBullet.isLive==false){
hero.vb.remove(myBullet);
}
}
//画出爆炸效果
for(int i=0; i<vBombs.size(); i++){
//取出爆炸对象
Bomb bomb = vBombs.get(i);
if(bomb.life>8){
g.drawImage(image1, bomb.x, bomb.y, 30, 30, this);
}else if(bomb.life>4){
g.drawImage(image2, bomb.x, bomb.y, 30, 30, this);
}else {
g.drawImage(image3, bomb.x, bomb.y, 30, 30, this);
}
bomb.lifeDown();//生命周期减少
if(bomb.isLive==false){
vBombs.remove(bomb);
}
}
//画出敌军坦克
for(int i=0; i<vts.size(); i++){
Enemy e = vts.get(i);
if(e.isLive){ //画出存活的敌军坦克(被炮弹击中后敌方坦克的isLive状态变为false,即死亡)
this.drawTank(e.getX(), e.getY(), g, e.getDirect(), vts.get(i).getColor());
//画出敌方炮弹
for(int j=0; j<e.eb.size(); j++){
Bullet b = e.eb.get(j);
if(b.isLive){
g.drawOval(b.x, b.y, 2, 2);
}else{
e.eb.remove(j);
}
}
}
}
/*附加:上面情况会出现敌方坦克已死亡,已发射的炮弹其实还存在,但是不会执行到绘出那一步
* 这里再重新添加个判断条件,当敌方坦克已死亡,即:!e.isLive时
* 不绘出死亡的坦克,但继续副出已发出且处于存活状态的炮弹
*/
for(int i=0; i<vts.size(); i++){
Enemy e = vts.get(i);
if(!e.isLive){
//只画出敌方坦克死亡前发射的炮弹
for(int j=0; j<e.eb.size(); j++){
Bullet b = e.eb.get(j);
if(b.isLive){
g.drawOval(b.x, b.y, 2, 2);
}else{
e.eb.remove(j);
}
}
}
}
}
//击中后显示目标效果
public void isHit(Bullet b, Tank t){
switch(t.direct){
//敌军坦克方向为上、下时状态
case 0:
case 1:
if(b.x>t.x && b.x<t.x+20 && b.y>t.y && b.y<t.y+30){
b.isLive = false;
t.isLive = false;
Bomb bomb = new Bomb(t.x, t.y); //击中时创建一个爆炸对象
vBombs.add(bomb); //加入Vector中
}
//敌军坦克方向为左、右时状态
case 2:
case 3:
if(b.x>t.x && b.x<t.x+30 && b.y>t.y && b.y<t.y+20){
b.isLive = false;
t.isLive = false;
Bomb bomb = new Bomb(t.x, t.y);
vBombs.add(bomb);
}
}
}
//画坦克函数
public void drawTank(int x, int y, Graphics g, int direct, int color){
//定义坦克颜色
switch (color){
case 0:
g.setColor(Color.GREEN);
break;
case 1:
g.setColor(Color.red);
break;
}
判断坦克方向
switch (direct){
//向上
case 0:
g.fill3DRect(x, 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+10, x+10, y);
g.fill3DRect(x+15, y, 5, 30, false);
break;
//向下
case 1:
g.fill3DRect(x, 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+10, x+10, y+30);
g.fill3DRect(x+15, y, 5, 30, false);
break;
//向左
case 2:
g.fill3DRect(x, y+10, 30, 5, false);
g.fill3DRect(x+5, y+15, 20, 10, false);
g.fillOval(x+10, y+15, 10, 10);
g.drawLine(x+15, y+20, x, y+20);
g.fill3DRect(x, y+25, 30, 5, false);
break;
//向右
case 3:
g.fill3DRect(x, y+10, 30, 5, false);
g.fill3DRect(x+5, y+15, 20, 10, false);
g.fillOval(x+10, y+15, 10, 10);
g.drawLine(x+15, y+20, x+30, y+20);
g.fill3DRect(x, y+25, 30, 5, false);
break;
}
}
@Override
public void keyTyped(KeyEvent e) {
// TODO 自动生成的方法存根
}
@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_DOWN)){
this.hero.setDirect(1);
this.hero.moveDown();
}else if((e.getKeyCode() == KeyEvent.VK_LEFT)){
this.hero.setDirect(2);
this.hero.moveLeft();
}else if((e.getKeyCode() == KeyEvent.VK_RIGHT)){
this.hero.setDirect(3);
this.hero.moveRight();
}
//判断玩家是否按下发射炮弹键(空格键)
if(e.getKeyCode() == KeyEvent.VK_SPACE){
if(hero.vb.size()<5){ //限定每次能发射的最大炮弹数
this.hero.shot();
}
}
this.repaint();/*run()方法中已经定时重绘,这里可以不必重绘,
但是由于延时重绘会导致英雄坦克的运动显得不流畅*/
}
@Override
public void keyReleased(KeyEvent e) {
// TODO 自动生成的方法存根
}
@Override//每隔100毫秒重绘一下炮弹
public void run() {
// TODO 自动生成的方法存根
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
//判断是否击中敌方坦克
this.hitEnemy();
//判断敌方坦克是否击中英雄坦克
this.hitHero();
this.repaint();
}
}
}
2、
package com.tank;
import java.util.Vector;
class Tank{
//坦克横、纵坐标
int x = 0;
int y = 0;
//坦克颜色
int color = 0;
//是否存活
boolean isLive = true;
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
//坦克方向 0 = 上, 1 = 下,2 = 左, 3 = 右
int direct = 0;
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 Tank(int x, int y){
this.x = x;
this.y = y;
}
}
//英雄坦克——我的
class Hero extends Tank{
//创建英雄坦克的炮弹
Bullet b = null;
//创建向量
Vector <Bullet> vb = new Vector <Bullet>();
//移动速度
int speed = 3;
//坦克的初始位置
public Hero (int x, int y){
super(x, y);
}
//我的坦克 移动方法
public void moveUp(){
y-=speed;
}
public void moveDown(){
y+=speed;
}
public void moveLeft(){
x-=speed;
}
public void moveRight(){
x+=speed;
}
//坦克开炮
public void shot(){
switch(this.direct){
case 0:
b = new Bullet(x+10, y, this.direct);//向上
vb.add(b);//将炮弹加入向量
break;
case 1:
b = new Bullet(x+10, y+30, this.direct);//向下
vb.add(b);
break;
case 2:
b = new Bullet(x, y+20, this.direct);//向左
vb.add(b);
break;
case 3:
b = new Bullet(x+30, y+20, this.direct);//向右
vb.add(b);
break;
}
//启动炮弹线程
Thread t = new Thread(b);
t.start();
}
}
//敌方坦克 实现线程接口
class Enemy extends Tank implements Runnable{
//移动速度
int speed = 1;
//定义一个向量存放敌方坦克的炮弹
Vector <Bullet> eb = new Vector <Bullet>();
public Enemy(int x, int y) {
super(x, y);
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
switch(this.direct){
case 0: //上
for(int i=0; i<25; i++){
if(y>0){
y-=speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
break;
case 1: //下
for(int i=0; i<30; i++){
if(y<300-30){ //必须要减去一个y轴的长度,不然坦克y点在边界时,其部分出界了
y+=speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
break;
case 2: //左
for(int i=0; i<35; i++){
if(x>0){
x-=speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
break;
case 3: //右
for(int i=0; i<40; i++){
if(x<400-30){
x+=speed;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
break;
}
if(isLive){
if(eb.size()<5){
Bullet b = null;
switch(direct){
case 0:
b = new Bullet(x+9, y, this.direct);//向上
eb.add(b);//将炮弹加入向量
break;
case 1:
b = new Bullet(x+9, y+30, this.direct);//向下
eb.add(b);
break;
case 2:
b = new Bullet(x, y+19, this.direct);//向左
eb.add(b);
break;
case 3:
b = new Bullet(x+30, y+19, this.direct);//向右
eb.add(b);
break;
}
Thread t = new Thread(b);
t.start();
}
}
//坦克产生一个随机方向
this.direct = (int)(Math.random()*4);
//坦克死亡后退出线程
if(this.isLive == false){
break;
}
}
}
}
//炮弹类
class Bullet implements Runnable{
int x, y;
int direct; //坦克方向决定炮弹方向
int speed = 6;
boolean isLive = true;//用于判定炮弹是否死亡
public Bullet(int x, int y, int direct){
this.x = x;
this.y = y;
this.direct = direct;
}
@Override//炮弹运行
public void run() {
while(isLive){
try {
Thread.sleep(130);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
switch(direct){
case 0:
y-=speed;
break;
case 1:
y+=speed;
break;
case 2:
x-=speed;
break;
case 3:
x+=speed;
}
if(x<0 || x>400|| y<0 || y>300){
isLive = false;
}
}
}
}
演示效果: