在第一版的基础上实现了发射多颗子弹,坦克爆炸,敌方坦克移动,限制边界的效果
父类坦克
package TankGeme;
/**
* @author Lenovo
* 坦克模型
*/
public class Tank {
//坦克的横坐标,纵坐标
private int x;
private int y;
//坦克方向0123上右下左
private int direction;
//速度
private int speed=7;
boolean exist=true;
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;
}
public int getDirection() {
return direction;
}
public void setDirection(int direction) {
this.direction = direction;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
//坦克移动方法
//上
public void moveUp(){ y-=speed;}
//下
public void moveDown(){
y+=speed;
}
//左
public void moveLeft(){
x-=speed;
}
//右
public void moveRight(){
x+=speed;
}
}
敌方坦克
package TankGeme;
import java.util.Vector;
/**
* @author 小小小白白白白
* 敌人的坦克
*/
@SuppressWarnings({"all"})
public class EnemyTank extends Tank implements Runnable{
//在敌人坦克类中,使用Vector保存多个Shot
Vector<Shot> shots=new Vector<>();
public EnemyTank(int x, int y) {super(x, y);}
@Override
public void run() {
while (true){
//这里我们判断如果Shot中的没有超过7个就生成
if (exist&&shots.size()<7){
Shot shot=null;
//判断坦克方向创建对应的子弹
switch(getDirection()){
case 0:
shot=new Shot(getX()+15,getY()-5,0);
break;
case 1:
shot=new Shot(getX()+60,getY()+15,1);
break;
case 2:
shot=new Shot(getX()+15,getY()+60,2);
break;
case 3:
shot=new Shot(getX(),getY()+15,3);
break;
}
shots.add(shot);
new Thread(shot).start();
}
//随机产生一个方向
setDirection((int)(Math.random()*4));
switch(getDirection()) {
case 0:
//for是为了不让他瞬间变向
//if是为了防止他出界
for (int i = 0; i < 20; i++) {
if (getY()>0){
moveUp();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 1: for (int i = 0; i < 20; i++) {
if (getX()+60<1000){
moveRight();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}break;
case 2: for (int i = 0; i < 20; i++) {
if (getY()+60<750){
moveDown();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}break;
case 3: for (int i = 0; i < 20; i++) {
if (getX()>0){
moveLeft();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}break;
}
if (!exist){
break;
}
}
}
}
我方坦克
package TankGeme;
import java.util.Vector;
/**
* @author 小小小白白白白
* 我的坦克
*/
@SuppressWarnings({"all"})
public class MyTank extends Tank{
//定义一个shot对象,表示一个射击线程
Shot shot=null;
//创建一个Vector保存多个子弹
Vector<Shot> shots=new Vector<>();
public MyTank(int x, int y) {
super(x, y);
}
//射击
public void fire() {
//控制子弹的数量
if(shots.size()==7){
return;
}
//创建shot对象,根据自己坦克的位置和方向来创建子弹
switch(getDirection()){
case 0:
shot=new Shot(getX()+15,getY()-5,0);
break;
case 1:
shot=new Shot(getX()+60,getY()+15,1);
break;
case 2:
shot=new Shot(getX()+15,getY()+60,2);
break;
case 3:
shot=new Shot(getX(),getY()+15,3);
break;
}
//把新创建的shot加入集合
shots.add(shot);
//启动射击线程
new Thread(shot).start();
}
}
绘图区域
package TankGeme;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
/**
* @author 小小小白白白白
* 坦克大战的绘图区域
* 为了监听键盘事件,实现keyListner
* 为了让panel不停地重绘子弹,需要将Mypanel实现Runnable接口,当做一个线程来使用
*/
@SuppressWarnings({"all"})
public class MyPanel extends JPanel implements KeyListener ,Runnable{
//定义我的坦克
MyTank mt=null;
/**
* 定义敌方坦克,存入Vector集合
* 之所以使用Vector是因为它是线程安全的
* 生成敌方子弹存入Vector集合,并取出绘制
*/
Vector<EnemyTank> wt=new Vector<>();
//定义敌人的数量
int amount=3;
//定义一个Vector用于存放爆炸
//当子弹击中坦克时加入爆炸到集合中
Vector<Bang> bangs = new Vector<>();
int eSise=3;
//定义三张炸弹图片,用于产生爆炸效果
Image image1=null;
Image image2=null;
Image image3=null;
public MyPanel() {
//初始化我的坦克
mt=new MyTank(400,400);
//初始化速度
mt.setSpeed(10);
//使用for来初始化敌方坦克
for (int i = 0; i < amount; i++) {
//创建一个敌人坦克
EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 100);
//设置方向,将每一个坦克都初始化朝下
enemyTank.setDirection(2);
//启动敌方坦克让他动起来
new Thread(enemyTank).start();
//给敌人坦克生成子弹
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
//将生成的子弹存入集合
enemyTank.shots.add(shot);
//启动
new Thread(shot).start();
//将创建的坦克加入Vector
wt.add(enemyTank);
}
//初始化图片对象
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"));
}
/**
* 绘制模块
* @param g
*/
@Override
public void paint(Graphics g) {
super.paint(g);
//填充矩形,默认黑色
g.fillRect(0,0,1000,750);
//画出自己坦克射击的子弹,遍历Vector集合依次画出
for (int i = 0; i < mt.shots.size(); i++) {
Shot shot=mt.shots.get(i);
if(shot!=null&& shot.exist){
g.setColor(Color.WHITE);
g.fillOval(shot.x,shot.y,10,10);
}else{
mt.shots.remove(shot);
}
}
//如果爆炸集合中有对象就画出
for (int i = 0; i < bangs.size(); i++) {
Bang bang = bangs.get(i);
//根据当前对象的生命值去画对应的图片
if(bang.life>5){
g.drawImage(image3, bang.x, bang.y,70,70,this);
}else if(bang.life>3){
g.drawImage(image2, bang.x, bang.y,70,70,this);
}else {
g.drawImage(image1, bang.x, bang.y,70,70,this);
}
//让这个炸弹的生命值减少
bang.lifeDown();
//如果生命值为0就把它从集合中去除
if(bang.life<=0){
bangs.remove(bang);
}
}
//调用画坦克方法
if(mt!=null&&mt.exist){
drawTank(mt.getX(),mt.getY(),g,mt.getDirection(),0);
}
//画出敌方坦克和子弹,遍历Vector
for (int i = 0; i < wt.size(); i++) {
//取出的每一个都是一个坦克对象
EnemyTank enemyTank = wt.get(i);
//判断取出的敌方坦克是否活着,绘制/移除
if (enemyTank.exist) {
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
for (int j = 0; j < enemyTank.shots.size(); j++) {
//取出子弹
Shot shot = enemyTank.shots.get(j);
//绘制子弹,当子弹活着就画出来,否则移除
if (shot.exist) {
g.setColor(Color.red);
g.fillOval(shot.x, shot.y, 10, 10);
} else {
enemyTank.shots.remove(shot);
}
}
}else{
wt.remove(enemyTank);
}
}
}
/**
* 编写坦克方法
* x 坦克的左上角x坐标
* y 坦克的左上角y坐标
* g 画笔
* direction 坦克方向(上下左右)
* type 坦克类型
*/
public void drawTank(int x,int y,Graphics g,int direction,int type) {
//根据不同类型的坦克设置不同的颜色
switch(type) {
//我们的坦克
case 0:g.setColor(Color.CYAN);break;
//敌人的坦克
case 1:g.setColor(Color.yellow);break;
}
//根据坦克方向来绘制坦克
//0123上右下左
switch(direction) {
//上
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;
}
}
/**
* 按下键或松开键的对应操作
* @param e
*/
@Override
public void keyTyped(KeyEvent e) {
}
//某个键被按下时,该方法就会被触发
@Override
public void keyPressed(KeyEvent e) {
//按下W键
if(e.getKeyCode() == KeyEvent.VK_W){
mt.setDirection(0);
if (mt.getY()>0) {
mt.moveUp();
}
}
//按下D键
else if(e.getKeyCode() == KeyEvent.VK_D){
mt.setDirection(1);
if (mt.getX()+60<980) {
mt.moveRight();
}
}
//按下S键
else if(e.getKeyCode() == KeyEvent.VK_S){
mt.setDirection(2);
if (mt.getY()+60<700) {
mt.moveDown();
}
}
//按下A键
else if(e.getKeyCode() == KeyEvent.VK_A){
mt.setDirection(3);
if (mt.getX()>0) {
mt.moveLeft();
}
}
//按下J键
else if(e.getKeyCode() == KeyEvent.VK_J){
mt.fire();
}
//重绘面板
this.repaint();
}
//某个键被松开时,该方法就会被触发
@Override
public void keyReleased(KeyEvent e) {
}
/**
* 判断我的子弹是否打中了敌方坦克
* 每隔0.1秒重绘地图
*/
public void hitwt(){
//判断我的子弹是否击中了敌方坦克,遍历集合中的所有子弹
for (int j = 0; j <mt.shots.size(); j++) {
Shot shot =mt.shots.get(j);
//首先判断我的子弹是否还存在
//我没有输入J的时候为空所以要判断一下
if(shot!=null&&shot.exist){
//遍历所有的坦克
for (int i = 0; i < wt.size(); i++) {
//取出每一个坦克看他是否被击中
EnemyTank enemyTank = wt.get(i);
hit(shot,enemyTank);
}
}
}
}
//判断敌方的子弹是否击中了我方坦克,遍历集合中的所有子弹
public void hitmt(){
for (int i = 0; i < wt.size(); i++) {
//依次取出敌人坦克
EnemyTank enemyTank = wt.get(i);
//遍历所有子弹
for (int j = 0; j < enemyTank.shots.size(); j++) {
//取出子弹
Shot shot = enemyTank.shots.get(j);
//判断是否被击中
if(mt.exist&&shot.exist){
hit(shot,mt);
}
}
}
}
public void hit(Shot s,Tank tank){
//根据坦克的方向进行判断
switch(tank.getDirection()){
//坦克向上和向下的形状是相同的
case 0:
case 2:
//如果子弹击中了坦克,那么该坦克就会消失,子弹也会消失
if(s.x> tank.getX()&&s.x<tank.getX()+40&&s.y>tank.getY()&&s.y<tank.getY()+60){
s.exist=false;
tank.exist=false;
//创建爆炸加入到集合中
Bang bang = new Bang(tank.getX(), tank.getY());
bangs.add(bang);
}
break;
case 1:
case 3:
if(s.x> tank.getX()&&s.x<tank.getX()+60&&s.y>tank.getY()&&s.y<tank.getY()+40){
s.exist=false;
tank.exist=false;
//创建爆炸加入到集合中
Bang bang = new Bang(tank.getX(), tank.getY());
bangs.add(bang);
}
break;
}
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
hitmt();
hitwt();
this.repaint();
}
}
}
子弹
package TankGeme;
/**
* @author 小小小白白白白
*/
@SuppressWarnings({"all"})
public class Shot implements Runnable{
int x;
int y;
int direct;
int speed=20;
//子弹是否存活
boolean exist=true;
public Shot(int x, int y, int direct) {
this.x = x;
this.y = y;
this.direct = direct;
}
@Override
public void run() {
while(true) {
//休眠0.1秒
try {
Thread.sleep(100);
} 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&&exist)){
//确认子弹销毁
exist=false;
break;
}
}
}
}
爆炸
package TankGeme;
/**
* @author 小小小白白白白
*/
public class Bang {
int x;
int y;
//生命周期
int life=8;
boolean exsit=true;
public Bang(int x, int y) {
this.x = x;
this.y = y;
}
//减少生命值
public void lifeDown(){
if(life>0){
life--;
}else {
exsit=false;
}
}
}
主界面
package TankGeme;
import javax.swing.*;
/**
* @author 小小小白白白白
* 框架及入口
*/
public class Game01 extends JFrame {
//定义MyPanel
MyPanel mp;
public static void main(String[] args) {
Game01 game01 = new Game01();
}
public Game01(){
mp=new MyPanel();
//将游戏的绘图区域放入画框
this.add(mp);
//设置画框大小
this.setSize(1000,750);
//监听键盘事件
this.addKeyListener(mp);
//将游戏区域放入Thread并启动该线程,自动重绘游戏区域
Thread thread = new Thread(mp);
thread.start();
//可视化
this.setVisible(true);
//退出
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}