代码总体上来说借鉴了尚学堂“手把手教你一小时写出坦克大战”(感谢),也从中加入了一些自己的想法(相对来说较少),子弹碰撞后消失,添加音效,随机生成地形等;话不多说,直接上思维导图(比较粗略)

目录

​编辑

窗口初始化

准备工作

窗口初始化

游戏父类的编写

方向类

游戏父类

添加坦克类

Tank

四个方向的移动——玩家(键盘监听)、人机(随机移动)

射击——玩家(键盘监听)、人机(随机移动)

Player

键盘监听器

人机

初始化子弹类

玩家子弹

子弹射击

碰撞检测

人机子弹

击中玩家

添加围墙、基地

Base

Wall

Grass

Fe

优化

爆炸

音乐

重写GamePanel

游戏最终成品


窗口初始化

准备工作

首先创建一个TankWar项目,在scr相同路径下创建images、music文件夹,将需要使用的图片、音乐分别放在images、music中。创建一个com.tankwar(包),注意命名规范

窗口初始化

在com.tankwar中创建GamePanel类

import javax.swing.JFrame;
public class GamePanel extends JFrame{//窗口大小private int width=1260;private int height=800;//窗口初始化public void launch() {//标题setTitle("坦克大战");//初始化窗口大小setSize(width,height);//使屏幕居中setLocationRelativeTo(null);//添加关闭事件setDefaultCloseOperation(3);//用户不能调整setResizable(false);//使窗口可见setVisible(true);}public static void main(String[]args) {GamePanel gamePanel=new GamePanel();gamePanel.launch();}
}

运行结果

导入窗口图片与屏幕刷新

程序在运行时屏幕的每,对象的位置以及图像的每一次改变都需要屏幕的一次刷新,而屏幕的每一次刷新都需要重新绘制屏幕,所以会出现闪屏现象。如果想要解决这种问题,需要运用双缓存技术

(2条消息) Java双缓冲技术_kkvveeerer的博客-CSDN博客_java双缓冲


import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;import javax.swing.JFrame;
public class GamePanel extends JFrame{//窗口大小private int width=1260;private int height=800;//定义双缓存图片Image offScreenImage=null;//临时变量private int a=1;//游戏状态 0 未开始,1 开始,2 暂停,3 失败,4 胜利private int state=0;//游戏是否开始private boolean start=false;//窗口初始化public void launch() {//标题setTitle("坦克大战");//初始化窗口大小setSize(width,height);//使屏幕居中setLocationRelativeTo(null);//添加关闭事件setDefaultCloseOperation(3);//用户不能调整setResizable(false);//使窗口可见setVisible(true);/** 控制屏幕刷新时间*/while(true) {repaint();try {Thread.sleep(25);//刷新休眠时间} catch (Exception e) {e.printStackTrace();// TODO: handle exception}}}public void paint(Graphics g) {//创建和容器一样大小的imageif(offScreenImage==null) {offScreenImage=this.createImage(width,height);}//获得该图片的画布Graphics gImage=offScreenImage.getGraphics();//填充整个画布gImage.fillRect(0,0,width,height);if(state==0) {//游戏未开始gImage.drawImage(Toolkit.getDefaultToolkit().getImage("images/interface.png"),0,0,this);}else if(state==1) {//游戏开始}else if(state==2) {//游戏暂停}else if(state==3) {//游戏失败}else if(state==4) {//游戏胜利}g.drawImage(offScreenImage,0,0,null);}public static void main(String[]args) {GamePanel gamePanel=new GamePanel();gamePanel.launch();}
}

程序运行结果

添加鼠标监听器

在游戏中点击“PLAYER”方可进入游戏,需要加入鼠标监听器

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;import javax.swing.JFrame;
public class GamePanel extends JFrame{//窗口大小private int width=1260;private int height=800;//定义双缓存图片Image offScreenImage=null;//临时变量private int a=1;//游戏状态 0 未开始,1 开始,2 暂停,3 失败,4 胜利private int state=0;//游戏是否开始private boolean start=false;//窗口初始化public void launch() {//标题setTitle("坦克大战");//初始化窗口大小setSize(width,height);//使屏幕居中setLocationRelativeTo(null);//添加关闭事件setDefaultCloseOperation(3);//用户不能调整setResizable(false);//使窗口可见setVisible(true);/** 控制屏幕刷新时间*/while(true) {repaint();try {Thread.sleep(25);} catch (Exception e) {e.printStackTrace();// TODO: handle exception}}}public void paint(Graphics g) {//创建和容器一样大小的imageif(offScreenImage==null) {offScreenImage=this.createImage(width,height);}//获得该图片的画布Graphics gImage=offScreenImage.getGraphics();//填充整个画布gImage.fillRect(0,0,width,height);if(state==0) {//游戏未开始gImage.drawImage(Toolkit.getDefaultToolkit().getImage("images/interface.png"),0,0,this);//添加鼠标事件this.addMouseListener(new GamePanel.MouseMonitor());}else if(state==1) {//游戏开始}else if(state==2) {//游戏暂停}else if(state==3) {//游戏失败}else if(state==4) {//游戏胜利}g.drawImage(offScreenImage,0,0,null);}/** 添加鼠标监听*/private class MouseMonitor extends MouseAdapter{@Overridepublic void mouseClicked(MouseEvent e) {Point p=new Point(e.getX(),e.getY());if(!start&&state==0) {if(p.x>450&&p.x<750&&p.y>400&&p.y<480) {//“PLAYER  X,Y坐标范围”state=1;start=true;}}else if(state==3||state==4) {}}}public static void main(String[]args) {GamePanel gamePanel=new GamePanel();gamePanel.launch();}
}

游戏父类的编写

方向类

游戏中坦克需要运用到“方向”,可以单独创建一个枚举类,或者在坦克类中定义“方向”成员变量。

/** 首先枚举是一个特殊的class* 这个class相当于final static 修饰,不能被继承* 他的构造方法强制被私有化* 所有的枚举都继承自java.lang.Enum类。由于java不支持多继承,所以枚举对象不能再继承其他类*/
public enum Direction {//每个成员变量都是final static 修饰UP,LEFT,RIGHT,DOWN
}

游戏父类

游戏中的坦克、子弹、基地、墙体、爆炸图片等元素,在绘画到面板上的过程中,需要运用到坐标,大小、移动速度、主界面画板。将其抽取出来,创建游戏父类

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;public abstract class GameObject {//游戏元素图片Image img;//游戏元素的横纵坐标int x;int y;//游戏元素的宽,高int width;int height;//游戏元素的移动速度int speed;//游戏元素的移动方向Direction direction;//引入主界面GamePanel gamePanel;public GameObject() {}public GameObject(String img,int x,int y,GamePanel gamePanel) {this.img=Toolkit.getDefaultToolkit().getImage(img);this.x=x;this.y=y;this.gamePanel=gamePanel;}public Image getImag() {return img;}public void setImg(String img) {this.img=Toolkit.getDefaultToolkit().getImage(img);}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 getWidth() {return width;}public void setWidth(int width) {this.width = width;}public int getHeight() {return height;}public void setHeight(int height) {this.height = height;}public double getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public Direction getDirection() {return direction;}public void setDirection(Direction direction) {this.direction = direction;}public GamePanel getGamePanel() {return gamePanel;}public void setGamePanel(GamePanel gamepanel) {this.gamePanel = gamePanel;}//继承元素绘制自己的方法public abstract void paintSelf(Graphics g);//获取当前游戏元素的矩形,是为碰撞检测而写public abstract Rectangle getRec();
}

添加坦克类

Tank

注:蓝色字体的写在子类中

坦克具有哪些特有属性?

四个方向移动的图片

    //向上移动时的图片private String upImage;//向下移动时的图片private String downImage;//向右移动时的图片private String rightImage;//向左移动时的图片private String leftImage;

四个方向的移动——玩家(键盘监听)、人机(随机移动)

1、墙体碰撞检测

2、边界碰撞检测

3、人机碰撞检测

4、移动


1、墙体碰撞检测

/** 坦克与墙碰撞检测*/public boolean hitWall(int x,int y) {//假设玩家坦克前进,下一个位置形成的矩形Rectangle next=new Rectangle(x,y,width,height);//地图里所有的墙体List<Wall>walls=this.gamePanel.wallList;List<Fe>fes=this.gamePanel.feList;//判断两个矩形是否相交for(Wall w:walls) {if(w.getRec().intersects(next)) {return true;}}for(Fe f:fes) {if(f.getRec().intersects(next)) {return true;}}return false;}

2、边界碰撞检测

/** 边界与坦克碰撞检测*/public boolean moveToBorder(int x,int y) {if(x<10) {return true;}else if(x>this.gamePanel.getWidth()-width) {return true;}if(y<30) {return true;}else if(y>this.gamePanel.getHeight()-height) {return true;}return false;}

3、人机碰撞检测

/** 人机碰撞检测*/public boolean hitTank(int x,int y) {int a=0;//假设人机坦克前进,下一个位置形成的矩形Rectangle next=new Rectangle(x,y,width,height);//地图里所有的人机List<Bot>bots=this.gamePanel.botList;//判断两个矩形是否相交for(Bot b:bots) {if(b.getRec().intersects(next)) {a++;if(a==2) {return true;}}}return false;}

4、移动

     /** 坦克移动*/public void leftWard() {direction=Direction.LEFT;setImg(leftImage);if(!hitWall(x-speed,y)&&!moveToBorder(x-speed,y)&&alive&&!hitTank(x-speed,y)) {this.x-=speed;}}public void rightWard() {direction=Direction.RIGHT;setImg(rightImage);if(!hitWall(x+speed,y)&&!moveToBorder(x+speed,y)&&alive&&!hitTank(x+speed,y)) {this.x+=speed;}}public void upWard() {direction=Direction.UP;setImg(upImage);if(!hitWall(x,y-speed)&&!moveToBorder(x,y-speed)&&alive&&!hitTank(x,y-speed)) {this.y-=speed;}}public void downWard() {direction=Direction.DOWN;setImg(downImage);if(!hitWall(x,y+speed)&&!moveToBorder(x,y+speed)&&alive&&!hitTank(x-speed,y+speed)) {this.y+=speed;}}

射击——玩家(键盘监听)、人机(随机移动)

1、子弹射击方向应与坦克移动方向一致

2、射击子弹CD

3、射击


1、子弹射击方向应与坦克移动方向一致

坦克坐标点为坦克图片左上角,所以子弹初始坐标应该为坦克的坐标加上坦克高度或宽度的一半。

/** 根据方向确定头部位置,x和y是左上角的点*/public Point getHeadPoint() {switch(direction) {case UP:return new Point(x+width/2,y);case LEFT:return new Point(x,y+height/2);case DOWN:return new Point(x+width/2,y+height);case RIGHT:return new Point(x+width,y+height/2);default:return null;}}

2、射击子弹CD

/** 坦克射击cd*/public class AttackCD extends Thread{public void run() {attackCoolDown=false;try {//休眠1秒Thread.sleep(attackCoolDownTime);//attackCoolDownTime为定义的成员变量单位为ms}catch (InterruptedException e) {e.printStackTrace();}//将攻击功能解除冷却状态attackCoolDown=true;this.stop();//不是很理解}}

3、射击

/** 射击*/public void attack() {Point p=getHeadPoint();if(attackCoolDown&&alive) {Bullet bullet=new Bullet("images/bullet/bulletGreen.gif",p.x-10,p.y-10, direction, this.gamePanel);this.gamePanel.bulletList.add(bullet);//将子弹添加至子弹集合attackCoolDown=false;new AttackCD().start();}}

这里由于还未创建其他类所以会报错,在后续会添加上的(博主偷个懒直接把源码粘贴过来了)

import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.List;public class Tank extends GameObject{//向上移动时的图片private String upImage;//向下移动时的图片private String downImage;//向右移动时的图片private String rightImage;//向左移动时的图片private String leftImage;//坦克sizeint width=54;int height=54;//坦克初始方向Direction direction=Direction.UP;//坦克速度private int speed=3;//攻击冷却private boolean attackCoolDown=true;//攻击冷却事件毫秒间隔1000ms发射子弹private int attackCoolDownTime=100;//坦克是否活着public boolean alive=true;/** 坦克坐标,方向,图片,方向,面板*/public Tank(String img,int x,int y,String upImage,String downImage,String leftImage,String rightImage,GamePanel gamePanel) {super(img,x,y,gamePanel);this.upImage=upImage;this.leftImage=leftImage;this.downImage=downImage;this.rightImage=rightImage;   }/** 坦克移动*/public void leftWard() {direction=Direction.LEFT;setImg(leftImage);if(!hitWall(x-speed,y)&&!moveToBorder(x-speed,y)&&alive&&!hitTank(x-speed,y)) {this.x-=speed;}}public void rightWard() {direction=Direction.RIGHT;setImg(rightImage);if(!hitWall(x+speed,y)&&!moveToBorder(x+speed,y)&&alive&&!hitTank(x+speed,y)) {this.x+=speed;}}public void upWard() {direction=Direction.UP;setImg(upImage);if(!hitWall(x,y-speed)&&!moveToBorder(x,y-speed)&&alive&&!hitTank(x,y-speed)) {this.y-=speed;}}public void downWard() {direction=Direction.DOWN;setImg(downImage);if(!hitWall(x,y+speed)&&!moveToBorder(x,y+speed)&&alive&&!hitTank(x-speed,y+speed)) {this.y+=speed;}}/** 射击*/public void attack() {Point p=getHeadPoint();if(attackCoolDown&&alive) {Bullet bullet=new Bullet("images/bullet/bulletGreen.gif",p.x-10,p.y-10, direction, this.gamePanel);this.gamePanel.bulletList.add(bullet);//将子弹添加至子弹集合attackCoolDown=false;new AttackCD().start();}}/** 坦克射击cd*/public class AttackCD extends Thread{public void run() {attackCoolDown=false;try {//休眠1秒Thread.sleep(attackCoolDownTime);}catch (InterruptedException e) {e.printStackTrace();}//将攻击功能解除冷却状态attackCoolDown=true;this.stop();//不是很理解}}/** 根据方向确定头部位置,x和y是左上角的点*/public Point getHeadPoint() {switch(direction) {case UP:return new Point(x+width/2,y);case LEFT:return new Point(x,y+height/2);case DOWN:return new Point(x+width/2,y+height);case RIGHT:return new Point(x+width,y+height/2);default:return null;}}/** 坦克与墙碰撞检测*/public boolean hitWall(int x,int y) {//假设玩家坦克前进,下一个位置形成的矩形Rectangle next=new Rectangle(x,y,width,height);//地图里所有的墙体List<Wall>walls=this.gamePanel.wallList;List<Fe>fes=this.gamePanel.feList;//判断两个矩形是否相交for(Wall w:walls) {if(w.getRec().intersects(next)) {return true;}}for(Fe f:fes) {if(f.getRec().intersects(next)) {return true;}}return false;}/** 人机碰撞检测*/public boolean hitTank(int x,int y) {int a=0;//假设人机坦克前进,下一个位置形成的矩形Rectangle next=new Rectangle(x,y,width,height);//地图里所有的人机List<Bot>bots=this.gamePanel.botList;//判断两个矩形是否相交for(Bot b:bots) {if(b.getRec().intersects(next)) {a++;if(a==2) {return true;}}}return false;}/** 边界与坦克碰撞检测*/public boolean moveToBorder(int x,int y) {if(x<10) {return true;}else if(x>this.gamePanel.getWidth()-width) {return true;}if(y<30) {return true;}else if(y>this.gamePanel.getHeight()-height) {return true;}return false;}@Overridepublic Rectangle getRec(){return new Rectangle(x,y,width,height);}@Overridepublic void paintSelf(Graphics g) {g.drawImage(img, x, y, null);}
}

Player

玩家类继承坦克类,在父类的基础上只需要添加键盘监听器即可


键盘监听器

/** 按下键盘时坦克持续运动*/public void keyPressed(KeyEvent e) {int key=e.getKeyCode();switch(key) {case KeyEvent.VK_A:left=true;Music.playBackground();break;case KeyEvent.VK_D:right=true;Music.playBackground();break;case KeyEvent.VK_W:up=true;Music.playBackground();break;case KeyEvent.VK_S:down=true;Music.playBackground();break;case KeyEvent.VK_SPACE:Music.attackPlay();this.attack();break;default:break;}       }/** 松开键盘时,坦克停止运动*/public void keyReleased(KeyEvent e) {int key = e.getKeyCode();switch (key){case KeyEvent.VK_A:left = false;Music.moveStop();break;case KeyEvent.VK_S:down = false;Music.moveStop();break;case KeyEvent.VK_D:right = false;Music.moveStop();break;case KeyEvent.VK_W:up = false;Music.moveStop();break;default:break;}        }

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;public class PlayerOne extends Tank{private boolean up=false;private boolean down=false;private boolean left=false;private boolean right=false;public PlayerOne(String img, int x, int y, String upImage, String downImage, String leftImage, String rightImage,GamePanel gamePanel) {super(img, x, y, upImage, downImage, leftImage, rightImage, gamePanel);// TODO Auto-generated constructor stub}/** 按下键盘时坦克持续运动*/public void keyPressed(KeyEvent e) {int key=e.getKeyCode();switch(key) {case KeyEvent.VK_A:left=true;Music.playBackground();break;case KeyEvent.VK_D:right=true;Music.playBackground();break;case KeyEvent.VK_W:up=true;Music.playBackground();break;case KeyEvent.VK_S:down=true;Music.playBackground();break;case KeyEvent.VK_SPACE:Music.attackPlay();this.attack();break;default:break;}      }/** 松开键盘时,坦克停止运动*/public void keyReleased(KeyEvent e) {int key = e.getKeyCode();switch (key){case KeyEvent.VK_A:left = false;Music.moveStop();break;case KeyEvent.VK_S:down = false;Music.moveStop();break;case KeyEvent.VK_D:right = false;Music.moveStop();break;case KeyEvent.VK_W:up = false;Music.moveStop();break;default:break;}        }/** 坦克移动*/public void move() {if(left) {leftWard();}else if(right) {rightWard();}else if (up) {upWard();}else if (down) {downWard();}else {return;}}@Overridepublic void paintSelf(Graphics g) {g.drawImage(img,x,y,null);move();}public Rectangle getRec() {return new Rectangle(x,y,width,height);}}

人机

人机具有哪些属性呢?

随机、随机、随机还是随机!

1、随机移动

2、随机射击


1、随机移动

/** 人机移动*/public void go() {attack();if(moveTime>=20) {direction=randomDirection();moveTime=0;}else{moveTime+=1;}switch(direction) {case UP:upWard();break;case DOWN:downWard();break;case RIGHT:rightWard();break;case LEFT:leftWard();break;}}/** 人机移动随机方向*/public Direction randomDirection() {Random r=new Random();int rnum=r.nextInt(4);switch(rnum) {case 0:return Direction.UP;case 1:return Direction.RIGHT;case 2:return Direction.LEFT;default:return Direction.DOWN;}}

2、随机射击

随机射击写在人机子弹类里

import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Random;public class Bot extends Tank{//人机朝一个方向运动时间int moveTime=0;public Bot(String img, int x, int y, String upImage, String downImage, String leftImage, String rightImage,GamePanel gamePanel) {super(img, x, y, upImage, downImage, leftImage, rightImage, gamePanel);}        /** 人机移动*/public void go() {attack();if(moveTime>=20) {direction=randomDirection();moveTime=0;}else{moveTime+=1;}switch(direction) {case UP:upWard();break;case DOWN:downWard();break;case RIGHT:rightWard();break;case LEFT:leftWard();break;}}/** 人机移动随机方向*/public Direction randomDirection() {Random r=new Random();int rnum=r.nextInt(4);switch(rnum) {case 0:return Direction.UP;case 1:return Direction.RIGHT;case 2:return Direction.LEFT;default:return Direction.DOWN;}}/** 随机攻击*/public void attack() {Point p=getHeadPoint();Random r=new Random();int rnum=r.nextInt(100);if(rnum<2) {EnemyBullet enemyBullet=new EnemyBullet("images/bullet/bulletYellow.gif",p.x,p.y,direction,gamePanel);this.gamePanel.enemyBulletsList.add(enemyBullet);}}/** 绘制人机*/@Overridepublic void paintSelf(Graphics g) {g.drawImage(img,x,y,null);go();}@Overridepublic Rectangle getRec() {return new Rectangle(x,y,width,height);}}

初始化子弹类

玩家子弹

  1. 子弹射击
  2. 子弹碰撞检测

子弹射击

  1. 子弹运动方向与坦克一致

/** 子弹运动方向*/public void go() {switch(direction) {case UP:upWard();break;case DOWN:downWard();break;case LEFT:leftWard();break;case RIGHT:rightWard();break;}}
/** 子弹运动坐标改变*/private void rightWard() {// TODO Auto-generated method stubx+=speed;}private void downWard() {// TODO Auto-generated method stuby+=speed;}private void leftWard() {// TODO Auto-generated method stubx-=speed;}private void upWard() {// TODO Auto-generated method stuby-=speed;}

碰撞检测

  1. 子弹与土墙

  2. 子弹与铁墙

  3. 子弹与基地

  4. 子弹与边界

  5. 子弹与子弹


1、子弹与土墙

/** 子弹和土墙碰撞*/public void hitWall() {Rectangle next=this.getRec();List<Wall>walls=this.gamePanel.wallList;for(Wall w:walls) {if(w.getRec().intersects(next)) {Music.wallPlay();this.gamePanel.wallList.remove(w);this.gamePanel.removeList.add(this);break;}}}

2、子弹与铁墙

/** 子弹与Fe*/public void hitFe() {Rectangle next=this.getRec();List<Fe>fes=this.gamePanel.feList;for(Fe f:fes) {if(f.getRec().intersects(next)) {Music.wallPlay();this.gamePanel.removeList.add(this);break;}}}

3、子弹与基地

/** 击中基地*/public void hitBase() {Rectangle next=this.getRec();for(Base base:gamePanel.baseList) {if(base.getRec().intersects(next)) {this.gamePanel.baseList.remove(base);this.gamePanel.removeList.add(this);this.gamePanel.state=3;break;}}}

4、子弹与边界

/** 移动到边界*/public void moveToBorder() {if(x<0||x>this.gamePanel.getWidth()) {this.gamePanel.removeList.add(this);}if(y<0||y>this.gamePanel.getHeight()) {this.gamePanel.removeList.add(this);}}

5、子弹与子弹

/** 子弹与子弹碰撞检测*/public void hitEnemyBullet() {Rectangle next=this.getRec();List<EnemyBullet>enemyBullets=this.gamePanel.enemyBulletsList;for(EnemyBullet enemyBullet:enemyBullets) {if(enemyBullet.getRec().intersects(next)) {Music.wallPlay();this.gamePanel.removeList2.add(enemyBullet);this.gamePanel.removeList.add(this);break;}}}

import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.List;public class Bullet extends GameObject{//长宽private int width=10;private int height=10;//速度private int speed=7;//方向Direction direction;//初始化子弹public Bullet(String img,int x,int y,Direction direction,GamePanel gamePanel) {super(img,x,y,gamePanel);this.direction=direction;            }/** 子弹运动方向*/public void go() {switch(direction) {case UP:upWard();break;case DOWN:downWard();break;case LEFT:leftWard();break;case RIGHT:rightWard();break;}}/** 子弹运动坐标改变*/private void rightWard() {// TODO Auto-generated method stubx+=speed;}private void downWard() {// TODO Auto-generated method stuby+=speed;}private void leftWard() {// TODO Auto-generated method stubx-=speed;}private void upWard() {// TODO Auto-generated method stuby-=speed;}/** 子弹与坦克碰撞检测*/public void hitBot() {//矩形类Rectangle next=this.getRec();List<Bot>bots=this.gamePanel.botList;//子弹和人机for(Bot bot:bots) {if(bot.getRec().intersects(next)) {Explode[]arr=new Explode[7];for(int i=0;i<7;i++) {Explode explode=new Explode("images/explode/"+(i+1)+".gif", x, y, this.gamePanel);this.gamePanel.explodeList.add(explode);}Music.explodePlay();this.gamePanel.botList.remove(bot);this.gamePanel.removeList.add(this);break;}}}/** 子弹与子弹碰撞检测*/public void hitEnemyBullet() {Rectangle next=this.getRec();List<EnemyBullet>enemyBullets=this.gamePanel.enemyBulletsList;for(EnemyBullet enemyBullet:enemyBullets) {if(enemyBullet.getRec().intersects(next)) {Music.wallPlay();this.gamePanel.removeList2.add(enemyBullet);this.gamePanel.removeList.add(this);break;}}}/** 子弹和土墙碰撞*/public void hitWall() {Rectangle next=this.getRec();List<Wall>walls=this.gamePanel.wallList;for(Wall w:walls) {if(w.getRec().intersects(next)) {Music.wallPlay();this.gamePanel.wallList.remove(w);this.gamePanel.removeList.add(this);break;}}}/** 子弹与Fe*/public void hitFe() {Rectangle next=this.getRec();List<Fe>fes=this.gamePanel.feList;for(Fe f:fes) {if(f.getRec().intersects(next)) {Music.wallPlay();this.gamePanel.removeList.add(this);break;}}}/** 移动到边界*/public void moveToBorder() {if(x<0||x>this.gamePanel.getWidth()) {this.gamePanel.removeList.add(this);}if(y<0||y>this.gamePanel.getHeight()) {this.gamePanel.removeList.add(this);}}/** 击中基地*/public void hitBase() {Rectangle next=this.getRec();for(Base base:gamePanel.baseList) {if(base.getRec().intersects(next)) {this.gamePanel.baseList.remove(base);this.gamePanel.removeList.add(this);this.gamePanel.state=3;break;}}}@Overridepublic void paintSelf(Graphics g) {// TODO Auto-generated method stubg.drawImage(img,x,y,null);go();//碰撞检测hitBot();hitWall();hitBase();hitFe();hitEnemyBullet();}@Overridepublic Rectangle getRec() {return new Rectangle(x,y,width,height);}}

人机子弹

人机与玩家的子弹有什么差别呢?

  1. 可以击中玩家,不能击中坦克

击中玩家

/** 击中玩家*/public void hitPlayer() {Rectangle next=this.getRec();List<Tank>tanks=this.gamePanel.tankList;//子弹和坦克for(Tank tank:tanks) {if(tank.getRec().intersects(next)) {tank.alive=false;this.gamePanel.tankList.remove(tank);this.gamePanel.removeList.add(this);break;}}}

import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.List;
/** 人机子弹*/
public class EnemyBullet extends Bullet{public EnemyBullet(String img, int x, int y, Direction direction, GamePanel gamePanel) {super(img, x, y, direction, gamePanel);}public void hitPlayer() {//攻击到玩家,玩家死亡Rectangle next=this.getRec();List<Tank>tanks=this.gamePanel.tankList;//子弹和坦克for(Tank tank:tanks) {if(tank.getRec().intersects(next)) {tank.alive=false;this.gamePanel.tankList.remove(tank);this.gamePanel.removeList.add(this);break;}}}@Overridepublic void hitBase() {//攻击到基地,游戏结束Rectangle next=this.getRec();for(Base base:gamePanel.baseList) {if(base.getRec().intersects(next)) {this.gamePanel.baseList.remove(base);this.gamePanel.removeList2.add(this);this.gamePanel.state=3;break;}}}public void hitWall() {//攻击到土墙,子弹与土均消失Rectangle next=this.getRec();List<Wall>walls=this.gamePanel.wallList;for(Wall w:walls) {if(w.getRec().intersects(next)) {this.gamePanel.wallList.remove(w);this.gamePanel.removeList2.add(this);break;}}}public void hitFe() {//攻击到铁墙,墙不变,子弹消失Rectangle next=this.getRec();List<Fe>fes=this.gamePanel.feList;for(Fe f:fes) {if(f.getRec().intersects(next)) {this.gamePanel.removeList2.add(this);break;}}}public void paintSelf(Graphics g) {g.drawImage(img,x,y,null);go();hitBase();hitWall();hitFe();hitPlayer();}
}

添加围墙、基地

剩下的就是间的添加围墙、基地等对象了。

Base

import java.awt.Graphics;
import java.awt.Rectangle;public class Base extends GameObject{public int width=60;public int height=60;public Base(String img,int x,int y,GamePanel gamePanel) {super(img,x,y,gamePanel);}@Overridepublic void paintSelf(Graphics g) {g.drawImage(img,x,y,null);// TODO Auto-generated method stub}@Overridepublic Rectangle getRec() {return new Rectangle(x,y,width,height);}
}

Wall

import java.awt.Graphics;
import java.awt.Rectangle;public class Wall extends GameObject{private int width=60;private int height=60;public Wall(String img,int x,int y,GamePanel gamePanel) {super(img,x,y,gamePanel);}@Overridepublic void paintSelf(Graphics g) {g.drawImage(img,x,y,null);}@Overridepublic Rectangle getRec() {        return new Rectangle(x,y,width,height);}
}

Grass

import java.awt.Graphics;
import java.awt.Rectangle;public class Grass extends GameObject{public Grass(String img,int x,int y,GamePanel gamePanel) {super(img,x,y,gamePanel);}@Overridepublic void paintSelf(Graphics g) {g.drawImage(img,x,y,null);}@Overridepublic Rectangle getRec() {return null;}}

Fe

import java.awt.Graphics;
import java.awt.Rectangle;public class Fe extends GameObject{private int width=60;private int height=60;public Fe(String img,int x,int y,GamePanel gamePanel) {super(img,x,y,gamePanel);}@Overridepublic void paintSelf(Graphics g) {g.drawImage(img,x,y,null);}@Overridepublic Rectangle getRec() {return new Rectangle(x,y,width,height);}}

优化

爆炸

import java.awt.Graphics;
import java.awt.Rectangle;public class Explode extends GameObject{public Explode(String img,int x,int y,GamePanel gamePanel) {super(img,x,y,gamePanel);}@Overridepublic void paintSelf(Graphics g) {g.drawImage(img,x-25,y-30,null);//x,y为图片左上角的坐标,调整图片位置,使图片居中}@Overridepublic Rectangle getRec() {// TODO Auto-generated method stubreturn null;}
}

音乐

import java.io.File;import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;public class Music{private static Clip start;//刚进入游戏的音乐private static Clip move;//玩家移动音乐private static Clip attack;//玩家射击音乐private static Clip explode;//爆炸音效private static Clip wall;//击中墙体音效static {File bgMusicStartFile = new File("D:\\JAVA\\eclipse\\TankWar\\musics\\bgm.wav");File bgMusicAttackFile = new File("D:\\JAVA\\eclipse\\TankWar\\musics\\attack.wav");File bgMusicMoveFile = new File("D:\\JAVA\\eclipse\\TankWar\\musics\\move.wav");File bgMusicExplodeFile = new File("D:\\JAVA\\eclipse\\TankWar\\musics\\explode.wav");File bgMusicWallFile = new File("D:\\JAVA\\eclipse\\TankWar\\musics\\wall.wav");try {AudioInputStream audioInputStreamStart = AudioSystem.getAudioInputStream(bgMusicStartFile);start = AudioSystem.getClip();start.open(audioInputStreamStart);AudioInputStream audioInputStreamAttack = AudioSystem.getAudioInputStream(bgMusicAttackFile);attack = AudioSystem.getClip();attack.open(audioInputStreamAttack);AudioInputStream audioInputStreamStartMove = AudioSystem.getAudioInputStream(bgMusicMoveFile);move = AudioSystem.getClip();move.open(audioInputStreamStartMove);AudioInputStream audioInputStreamStartExplode = AudioSystem.getAudioInputStream(bgMusicExplodeFile);explode = AudioSystem.getClip();explode.open(audioInputStreamStartExplode); AudioInputStream audioInputStreamStartWall = AudioSystem.getAudioInputStream(bgMusicWallFile);wall = AudioSystem.getClip();wall.open(audioInputStreamStartWall); } catch (Exception e) {e.printStackTrace();}}public static void playBackground(){//循环播放move.setFramePosition(0);move.loop(Clip.LOOP_CONTINUOUSLY);       }public static void startPlay(){start.start();start.setFramePosition(0);}public static void attackPlay(){attack.start();//将进度条调为0attack.setFramePosition(0);}public static void movePlay(){move.start();move.setFramePosition(0);}public static void moveStop(){move.stop();}public static void explodePlay(){explode.start();explode.setFramePosition(0);}public static void wallPlay() {wall.start();wall.setFramePosition(0);}
}

重写GamePanel

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;import javax.swing.JFrame;public class GamePanel extends JFrame{boolean one=true;boolean run=true;//关卡private int level=1;//定义双缓存图片Image offScreenImage=null;/** //游戏状态* 0 未开始,1 单人模式,* 2 游戏暂停,3 游戏失败,4 游戏胜利*/public int state =0;//游戏是否开始private boolean start=false;//临时变量private int a=1;//窗口长宽private int width=1260;private int height=800;//指针初始纵坐标private int y=260;//基地private Base base=new Base("images/base.gif",660,740,this);//重绘次数public int count=0;//敌人数量private int enemyCount=0;///随机生成的墙体数量private int wallCount=100;private boolean d=true;private int[]xrr=new int[wallCount];private int[]yrr=new int[wallCount];//物体集合public List<Bullet>bulletList=new ArrayList<>();public List<Tank>tankList=new ArrayList<>();public List<Bot>botList=new ArrayList<>();public List<Bullet>removeList=new ArrayList<>();public List<Wall>wallList=new ArrayList<>();public List<Grass>grassList=new ArrayList<>();public List<Fe>feList=new ArrayList<>();public List<Base> baseList = new ArrayList<>();public List<EnemyBullet>enemyBulletsList=new ArrayList<>();public List<EnemyBullet>removeList2=new ArrayList<>();public List<Explode>explodeList=new ArrayList<>();//玩家private PlayerOne playerOne = new PlayerOne("images/player1/p1tankU.gif", 500, 700,"images/player1/p1tankU.gif","images/player1/p1tankD.gif","images/player1/p1tankL.gif","images/player1/p1tankR.gif", this);public void random() {Random r=new Random(); for(int i=0;i<wallCount;) {int x=r.nextInt(20);int y=r.nextInt(8)+3;if(i>0) {boolean repeat=false;for(int j=0;j<i;j++) {if(xrr[j]==x*60&&yrr[j]==y*60) {repeat=true;}}if(!repeat) {xrr[i]=x*60;yrr[i]=y*60;i++;}else {continue;}}else {xrr[i]=x*60;yrr[i]=y*60;i++;}}}/** 重置*/public void reset() {count=0;enemyCount=0;playerOne = new PlayerOne("images/player1/p1tankU.gif", 500, 700,"images/player1/p1tankU.gif","images/player1/p1tankD.gif","images/player1/p1tankL.gif","images/player1/p1tankR.gif", this);feList.clear();bulletList.clear();tankList.clear();botList.clear();removeList.clear();wallList.clear();grassList.clear();baseList.clear();enemyBulletsList.clear();removeList2.clear();explodeList.clear();}/** 添加墙,基地*/public void add() {random();//添加围墙for(int i=0;i<21;i++) {wallList.add(new Wall("images/walls.gif",i*60,120,this));}wallList.add(new Wall("images/walls.gif",600,740,this));wallList.add(new Wall("images/walls.gif",600,680,this));wallList.add(new Wall("images/walls.gif",660,680,this));wallList.add(new Wall("images/walls.gif",720,680,this));wallList.add(new Wall("images/walls.gif",720,740,this));for(int i=0;i<wallCount;i++) {Random a=new Random();int num=a.nextInt(4);if(num<2) {Wall wall=new Wall("images/walls.gif",xrr[i], yrr[i], null);wallList.add(wall);}else if(num<3){Grass g=new Grass("images/cao.gif",xrr[i], yrr[i], null);grassList.add(g);}else {Fe f=new Fe("images/fe.gif",xrr[i], yrr[i], null);feList.add(f);}}//添加基地baseList.add(base);}/** 窗口的启动方法*/public void launch() {//标题setTitle("坦克大战无尽版");//窗口初始化大小setSize(width,height);//使屏幕居中setLocationRelativeTo(null);//添加关闭事件setDefaultCloseOperation(3);//用户不能调整大小setResizable(false);//使窗口可见setVisible(true);//初始化基地围墙;add();//添加键盘事件this.addKeyListener(new GamePanel.KeyMonitor());while(true) {if(botList.size()==0&&enemyCount==10) {state=4;}if(tankList.size()==0&&(state==1)) {state=3;}if(state==1) {if(count%50==1&&enemyCount<10) {boolean a=true;Random r=new Random();int rnum=r.nextInt(19)+1;if(enemyCount>0) {for(Bot b:botList) {if(Math.abs(rnum*60-b.getX())<60) {a=false;}}if(a) {botList.add(new Bot("images/enemy/enemy1U.gif", rnum*60, 60,"images/enemy/enemy1U.gif","images/enemy/enemy1D.gif","images/enemy/enemy1L.gif","images/enemy/enemy1R.gif", this));enemyCount++;}}else {botList.add(new Bot("images/enemy/enemy1U.gif", rnum*60, 60,"images/enemy/enemy1U.gif","images/enemy/enemy1D.gif","images/enemy/enemy1L.gif","images/enemy/enemy1R.gif", this));enemyCount++;}}if(count%2==1) {if(!explodeList.isEmpty()) {explodeList.remove(0);}}}if(run) {repaint();}try {Thread.sleep(25);}catch (Exception e) {e.printStackTrace();// TODO: handle exception}}}@Overridepublic void paint(Graphics g) {//创建和容器一样大小的Image图片if(offScreenImage==null) {offScreenImage=this.createImage(width,height);}//获得该图片的画布Graphics gImage=offScreenImage.getGraphics();//填充整个画布gImage.fillRect(0,0,width,height);if(state==0) {//添加图片gImage.drawImage(Toolkit.getDefaultToolkit().getImage("images/interface.png"),0,0,this);//添加鼠标事件this.addMouseListener(new GamePanel.MouseMonitor());}else if(state==1) {//paint重绘游戏元素for(Tank player:tankList) {player.paintSelf(gImage);}for(Bullet bullet:bulletList) {bullet.paintSelf(gImage);}bulletList.removeAll(removeList);for(EnemyBullet enemyBullet:enemyBulletsList) {enemyBullet.paintSelf(gImage);}enemyBulletsList.removeAll(removeList2);for(Explode explode:explodeList) {explode.paintSelf(gImage);}for(Bot bot:botList) {bot.paintSelf(gImage);}for(Wall wall:wallList) {wall.paintSelf(gImage);}for(Fe fe:feList) {fe.paintSelf(gImage);}for(Grass grass:grassList) {grass.paintSelf(gImage);}for(Base base:baseList) {base.paintSelf(gImage);}//重绘次数+1count++;}else if(state==2) {}else if(state==3) {gImage.drawImage(Toolkit.getDefaultToolkit().getImage("images/shibai.png"),315,200,this);//添加鼠标事件this.addMouseListener(new GamePanel.MouseMonitor());}else if(state==4) {gImage.drawImage(Toolkit.getDefaultToolkit().getImage("images/win.png"),(width-322)/2,(height-84)/2,this);this.addMouseListener(new GamePanel.MouseMonitor());}//将缓冲区绘制好的图形整个绘制到容器的画布中g.drawImage(offScreenImage,0,0,null);}/** 添加键盘监听*/private class KeyMonitor extends KeyAdapter{@Overridepublic void keyPressed(KeyEvent e) {int key=e.getKeyCode();switch(key) {case KeyEvent.VK_ENTER:if(!start) {tankList.add(playerOne);//将坦克添加至坦克集合state=a;start=true;}break;case KeyEvent.VK_P:if(state!=2) {a=state;state=2;run=false;}else {state=a;run=true;if(a==0) {a=1;}}break;                default:playerOne.keyPressed(e);break;}}@Overridepublic void keyReleased(KeyEvent e) {playerOne.keyReleased(e);}}//添加鼠标监听private class MouseMonitor extends MouseAdapter{@Overridepublic void mouseClicked(MouseEvent e) {Point p=new Point(e.getX(),e.getY());if(!start&&state==0) {if(p.x>450&&p.x<750&&p.y>400&&p.y<480) {tankList.add(playerOne);state=1;start=true;Music.startPlay();}}else if(state==3||state==4) {reset();start=false;state=0;add();}}} public static void main(String[]args) {GamePanel gamePanel=new GamePanel();gamePanel.launch();}
}

游戏最终成品

java实现坦克大战相关推荐

  1. 基于Java的坦克大战游戏的设计与实现(论文+PPT+源码)

    幻灯片1 基于Java的坦克大战游戏的设计与实现 幻灯片2 CONTENTS 1 4 设计工具与相关技术 详细设计 2 5 系统分析 结论 3 总体设计 幻灯片3 PPT模板下载:http://www ...

  2. java小组坦克大战游戏开发文档开发日志_java实现坦克大战游戏

    本文实例为大家分享了java实现坦克大战游戏的具体代码,供大家参考,具体内容如下 一.实现的功能 1.游戏玩法介绍 2.自定义游戏(选择游戏难度.关卡等) 3.自定义玩家姓名 4.数据的动态显示 二. ...

  3. Java实现坦克大战,单机版和联网版

    Java实现坦克大战 源码获取途径 部分源代码 源码获取途径 百度网盘链接: 百度网盘地址 提取码:5r7i GitHub Github获取地址 部分源代码 public class TankClie ...

  4. 【JAVA程序设计】基于JAVA的坦克大战小游戏--入门级小游戏

    基于JAVA的坦克大战小游戏--入门级小游戏 零.项目获取 一.项目简介 二.开发环境 三.游戏玩法 四.运行截图 零.项目获取 获取方式(点击下载):是云猿实战 项目经过多人测试运行,可以确保100 ...

  5. java坦克大战登录界面设计_基于JAVA的坦克大战设计和实现-代码.doc

    JISHOU UNIVERSITY 本科生毕业设计 题 目:基于JAVA的坦克大战设计与实现作 者:学 号:所属学院:专业年级:指导教师:职 称:完成时间:2012年5月7日 吉首大学 基于JAVA的 ...

  6. Java版坦克大战游戏

    技术:Java等 摘要: Java随着各种电子设备,其中尤其是移动通信设备的发展所诞生的一项新的开发技术.Java定位在各种电子设备产品的功能应用上,对电子产品的多样,智能化,提供了很大的帮助.本次设 ...

  7. java 坦克大战 教程_[Java教程]坦克大战(一)

    [Java教程]坦克大战(一) 0 2016-09-16 08:00:05 坦克大战(一) 相信大家对坦克大战都不陌生,并且网上也有很多用java实现的小程序,最近用了几天时间将其使用javaScri ...

  8. java怎么连发子弹_【Java_项目篇1】--JAVA实现坦克大战游戏--子弹连发+爆炸效果(四)...

    前期相关文章 [Java_项目篇<1>]–JAVA实现坦克大战游戏–画出坦克(一) [Java_项目篇<1>]–JAVA实现坦克大战游戏–坦克移动+添加敌方坦克(二) [Jav ...

  9. 【Java_项目篇1】--JAVA实现坦克大战游戏--坦克移动+添加敌方坦克(二)

    前期文章: [Java_项目篇<1>]--JAVA实现坦克大战游戏--画出坦克(一) 控制小球移动 1.外部类 实现KeyListener监听接口写法 package com.test3; ...

  10. 【java版坦克大战---准备篇】 java 绘图

    要写坦克大战当然要先画出坦克.java画图是基础. package com.game; import java.awt.*; import javax.swing.*; public class Pr ...

最新文章

  1. cloudera hbase集群简单思路
  2. springBoot 搭建web项目(前后端分离,附项目源代码地址)
  3. 网络营销之CPA、CPS、CPM、CPT、CPC 是什么
  4. docker 容器环境 检测方法
  5. IO多路复用 select、poll、epoll
  6. jmeter之ip欺骗
  7. SAP Commerce Cloud OAuth 实现介绍
  8. FLOAT或DOUBLE列与具有数值类型的数值进行比较 问题
  9. (Python)时序预测的七种方法
  10. 入门monkeyrunner7-monkeyrunner demo3 EasyMonkeyDevice+hierarchyviewer +monkeyrunner+截图对比
  11. 多线程调用生成主键流水号存储过程产生主键冲突问题解决方案
  12. 安装pattern出错mysql_config not found
  13. JS总结 循环 退出循环 函数
  14. IDEA切换使用的语言
  15. 1010 -- 青蛙的约会
  16. stm32f4 ov7670 屏幕一直显示,OV7670 ERR 且MID(PID) 读出来是65535(0xffff)的解决办法
  17. ES7和 ES8 一览
  18. TikTok与抖音有什么不同?为什么TikTok被称为“海外版抖音”?
  19. 荣耀60和荣耀x30max哪个好
  20. 2011, 完全用 GNU/Linux 工作

热门文章

  1. 非线性系统基于干扰观测器的抗干扰控制
  2. springboot整合aceadmin
  3. C++ 结构体内存对齐
  4. dedecms分页样式修改 内容页 上一页 下一页
  5. Querydsl使用fetchCount()报错
  6. 香港股票交易成本计算器 android,股票交易手续费计算器
  7. win11-vscode-wsl2 学习linux源码之linux源码在win11下的编译
  8. 阿里巴巴常用的12个后端开发工具
  9. 各个省市mysql表附带行政id(一)
  10. PyTorch 实现 GradCAM