一、简单介绍

写在前面:
这是我通过学习网上的教程写出的代码,并不是简单的复制粘贴,发出此代码的目的主要是为了让大家学习编程的思路,希望可以通过此项目让你学习到新的内容。

实现的内容:
1、单人游戏
2、双人游戏
3、选择关卡
4、游戏帮助
5、游戏关于
6、退出游戏

主要内容

游戏截图

GameMain类

package c02.n02.app;import c02.n02.game.GameFrame;public class GameMain {public static void main(String[] args) {//窗口初始化new GameFrame();}
}

Bullet类

package c02.n02.game;import c02.n02.tank.Tank;
import c02.n02.util.Constant;import java.awt.*;/*** 子弹类*/
public class Bullet {public static final int DEFAULT_SPEED = 20;    //子弹默认速度public static final int RADIUS = 4;   //炮弹的半径private int x, y; //子弹坐标private int speed = DEFAULT_SPEED;  //子弹速度private int dir;    //方向private int atk;    //攻击力private Color color;    //颜色private boolean visible = true;    //子弹是否可见/*** 子弹类构造函数** @param x     X坐标* @param y     Y坐标* @param dir   方向* @param atk   攻击力* @param color 颜色*/public Bullet(int x, int y, int dir, int atk, Color color) {this.x = x;this.y = y;this.dir = dir;this.atk = atk;this.color = color;}/*** 无参构造函数* 给对象池使用的,参数全部默认,因为炮弹是哪个坦克发射的都不知道*/public Bullet() {}/*====================START=============GETTER/SETTER========================================*/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 getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public int getDir() {return dir;}public void setDir(int dir) {this.dir = dir;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public Color getColor() {return color;}public void setColor(Color color) {this.color = color;}public boolean isVisible() {return visible;}public void setVisible(boolean visible) {this.visible = visible;}/*====================END=============GETTER/SETTER========================================*//*** 炮弹自身的绘制方法** @param g*/public void draw(Graphics g) {//如果子弹不可见就不画了if (!visible) return;logic();g.setColor(color);  //设置炮弹颜色//画实心炮弹g.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2, RADIUS * 2);}/*** 炮弹的逻辑* 逻辑就是移动*/private void logic() {move();}/*** 炮弹的移动方法*/private void move() {switch (dir) {case Tank.DIR_UP:y -= speed;if (y <= 0) {//如果子弹飞出屏幕外就为不可见状态visible = false;}break;case Tank.DIR_DOWN:y += speed;if (y > Constant.FRAME_HIGH) {visible = false;}break;case Tank.DIR_LEFT:x -= speed;if (x < 0) {visible = false;}break;case Tank.DIR_RIGHT:x += speed;if (x > Constant.FRAME_WIDTH) {visible = false;}break;}}}

Explode类

package c02.n02.game;import c02.n02.util.MyUtil;import java.awt.*;/*** 爆炸效果类*/
public class Explode {//设置爆炸效果一共有几帧public static final int EXPLODE_FRAME_COUNT = 6;//导入爆炸图片private static Image[] img;//在静态代码块中进行初始化static {img = new Image[EXPLODE_FRAME_COUNT];for (int i = 0; i < img.length; i++) {img[i] = MyUtil.createImage("image/boom/b" + i + ".png");}}//爆炸效果的属性private int x;private int y;//当前播放的是第几帧的下标private int index;//是否可见private boolean visible = true;//爆炸图片宽度和高度的一半private static int explodeWidth = 60 / 2;private static int explodeHight = 60 / 2;//============START==========GETTER/SETTER===========================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 getIndex() {return index;}public void setIndex(int index) {this.index = index;}public boolean isVisible() {return visible;}public void setVisible(boolean visible) {this.visible = visible;}//============END==========GETTER/SETTER===========================/*** 爆炸类的无参构造函数* 给对象池使用的,参数全部默认,因为爆炸效果是哪个坦克的都不知道*/public Explode() {//设置为从第0帧开始index = 0;}/*** 爆炸类的构造方法** @param x X位置* @param y Y位置*/public Explode(int x, int y) {this.x = x;this.y = y;//设置为从第0帧开始index = 0;}/*** 炮弹类的绘制方法** @param g*/public void draw(Graphics g) {//如果帧下标超出最大的话就重新设置为0//为了防止调用时没有手动设置为从第0帧开始播放导致下标越界if (index > EXPLODE_FRAME_COUNT - 1) {index = 0;}//只有可见的时候才绘制if (visible) {g.drawImage(img[index], x - explodeWidth, y - explodeHight, null);index++;}//播放完最后一帧变成不可见状态if (index >= EXPLODE_FRAME_COUNT) {visible = false;}}}

GameFrame类

package c02.n02.game;import c02.n02.map.GameMap;
import c02.n02.tank.EnemyTank;
import c02.n02.tank.MyTank;
import c02.n02.tank.Tank;
import c02.n02.util.Constant;
import c02.n02.util.EnemyTanksPool;
import c02.n02.util.MusicUtil;
import c02.n02.util.MyUtil;import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;import static c02.n02.util.Constant.*;/**游戏主窗口类*所有游戏显示的内容都在此窗口中实现*注意:为了方便维护所有的常量都在常量类中*继承了Frame类用于绘制**/
public class GameFrame extends Frame implements Runnable {// 定义一张和屏幕一样大的图片用于双缓冲private BufferedImage bufImg = new BufferedImage(FRAME_WIDTH, FRAME_HIGH, BufferedImage.TYPE_4BYTE_ABGR);// 菜单界面背景private Image menuImg = null;//游戏地图背景private Image mapImg = null;//选择关卡背景private Image levelImg = null;//帮助界面背景private Image helpImg = null;//关于界面背景private Image aboutImg = null;// 游戏结束的图片,第一次使用的时候加载,而不是类加载的时候加载,这样效率更高private Image overImg = null;// 游戏通关的图片,第一次使用的时候加载,而不是类加载的时候加载,这样效率更高private Image winImg = null;// 游戏状态private static int gameState;// 菜单指向public static int menuIndex;// 关卡指向public static int levelIndex;// 标题栏的高度public static int titleBarH;// 定义坦克对象private static Tank myTank;// 定义第二个坦克对象private static Tank myTank2;// 定义一个变量用来判断是否是双人模式private static boolean isTwoMode = true;// 是否开启坦克之间的碰撞private static boolean tankCollide = false;// 是否开启队友伤害private static boolean killMyTank = false;// 用来记录本关卡产生了多少个敌人private static int bornEnemyCount;// 用来记录本关消灭了多少敌人public static int killEnemyCount;// 定义敌人的坦克容器private static List<Tank> enemies = new ArrayList<>();// 定义并创建地图对象private static GameMap gameMap = new GameMap();// 获取总关卡数量private static int levlCount = GameInfo.getLevelCount();public static int getGameState() {return gameState;}public static void setGameState(int gameState) {GameFrame.gameState = gameState;}/** 对窗口进行初始化*/public GameFrame() {//初始化属性initFrame();// 初始化窗口按键监听initEventListener();// 启用用于刷新窗口的进程new Thread(this).start();// 播放背景音乐MusicUtil.playStart();}/*** 对游戏内容进行初始化*/public void initGAME() {gameState = STATE_MENU;}/*** 初始化函数 对属性进行初始化*/private void initFrame() {// 设置标题setTitle(GAME_TITLE);// 设置窗口大小setSize(FRAME_WIDTH, FRAME_HIGH);setLocation(FRAME_X, FRAME_Y);// 设置窗口大小不可变setResizable(false);// 设置窗口可见setVisible(true);// 求标题栏的高度titleBarH = getInsets().top;}/*** 该方法负责所有的绘制的内容,也就是说需要在屏幕中显示的内容都需要在该方法中调用 updata方法是继承的,自动调用,不用自己调用*/public void update(Graphics g1) {// 得到图片的画笔,用于双缓冲Graphics g = bufImg.getGraphics();g.setFont(FONT);switch (gameState) {case STATE_MENU:drawMenu(g);break;case STATE_LEVEL:drawLevel(g);break;case STATE_HELP:drawHelp(g);break;case STATE_ABOUT:drawAbout(g);break;case STATE_RUN:drawRun(g);break;case STATE_OVER:drawOver(g);break;case STATE_WIN:drawWin(g);break;}// 使用系统画笔将图片绘制到frame上,用于双缓冲g1.drawImage(bufImg, 0, 0, null);}/* =================START==========绘制方法==================================== *//*** 绘制菜单的方法** @param g*/private void drawMenu(Graphics g) {// 设置画笔颜色g.setColor(Color.MAGENTA);// 画个矩形当做背景// g.fillRect(0, 0, FRAME_WIDTH, FRAME_HIGH);// 只有menuImg为空的时候才加载if (menuImg == null) {menuImg = MyUtil.createImage("image/Menu/background.png");}// 使用图片作为背景g.drawImage(menuImg, 0, 0, null);// 定位文字位置int x = 560;int y = 350;int dis = 60; // 行间距// g.setColor(Color.PINK);// 根据选择设置菜单颜色for (int i = 0; i < MENUS.length; i++) {if (i == menuIndex) {g.setColor(Color.cyan);} else {g.setColor(Color.BLUE);}g.drawString(MENUS[i], x, y + dis * i);}}/*** 绘制关卡界面方法** @param g*/private void drawLevel(Graphics g) {// 只有levelImg为空的时候才加载if (levelImg == null) {levelImg = MyUtil.createImage("image/Menu/level.png");}// 使用图片作为背景g.drawImage(levelImg, 0, 0, null);g.setColor(Color.BLACK);// 定位文字位置int x = 500;int y = 280;int dis = 60; // 行间距
//        g.fillRect(0, 0, FRAME_WIDTH, FRAME_HIGH);  //绘制纯色背景g.setFont(FONT);for (int i = 0; i < levlCount; i++) {if (i == levelIndex) {g.setColor(Color.RED);} else {g.setColor(Color.BLUE);}g.drawString(i + 1 + "", x + dis * i, y);}}/*** 游戏运行时的绘制方法** @param g*/private void drawRun(Graphics g) {//绘制地图背景g.setColor(Color.BLACK);
//        g.fillRect(0, 0, FRAME_WIDTH, FRAME_HIGH); // 单色背景if (mapImg == null) {mapImg = MyUtil.createImage("image/Map/background.png");}g.drawImage(mapImg, 0, 0, null);   //绘制地图背景// TODO 显示内测版本g.setColor(Color.CYAN);g.setFont(SMALL_FONT);g.drawString("版本 v2020-12-24 最终答辩版", 10, FRAME_HIGH - 10);// 绘制地图,先绘制没有遮挡的块gameMap.drawBK(g);// 绘制敌人的坦克drawEnemies(g);//如果己方两个坦克都死亡的话就切换到游戏结束状态if (myTank.isDie() && myTank2.isDie()) {GameFrame.setGameState(Constant.STATE_OVER);}// 绘制自己的坦克//只有坦克没有死的时候才绘制if (!myTank.myTankisDie()) {myTank.draw(g);} else {    //坦克死亡后就绘制到屏幕外myTank.draw(g);myTank.setX(1500);myTank.setY(1500);}if (!myTank2.myTankisDie()) {myTank2.draw(g);} else {myTank2.draw(g);myTank2.setX(1500);myTank2.setY(1500);}// 绘制地图的遮挡层gameMap.drawCover(g);// 子弹和坦克的碰撞方法bulletCollideTank();// 爆炸效果drawExplodes(g);// 坦克和子弹和所有地图块的碰撞bulletAndTanksCollideMapTile();//坦克之间的碰撞if (tankCollide) {tankCollideTank();}}/*** 绘制敌人坦克的方法,并且移除已经死亡的坦克** @param g*/private void drawEnemies(Graphics g) {for (int i = 0; i < enemies.size(); i++) {Tank enemy = enemies.get(i);// 如果坦克已经死亡就从容器中移除if (enemy.isDie()) {enemies.remove(i);i--;continue;}enemy.draw(g);}}/*** 绘制关于界面的方法** @param g*/private void drawAbout(Graphics g) {//绘制关于界面g.setColor(Color.BLACK);
//        g.fillRect(0, 0, FRAME_WIDTH, FRAME_HIGH); // 单色背景if (aboutImg == null) {aboutImg = MyUtil.createImage("image/Menu/about.png");}g.drawImage(aboutImg, 0, 0, null);   //绘制地图背景}/*** 绘制帮助界面的方法** @param g*/private void drawHelp(Graphics g) {//绘制帮助界面g.setColor(Color.BLACK);
//        g.fillRect(0, 0, FRAME_WIDTH, FRAME_HIGH); // 单色背景if (helpImg == null) {helpImg = MyUtil.createImage("image/Menu/help.png");}g.drawImage(helpImg, 0, 0, null);   //绘制地图背景}// 绘制游戏结束的方法private void drawOver(Graphics g) {// 只有overImg为空的时候才加载if (overImg == null) {overImg = MyUtil.createImage("image/gameover.png");}// 求图片宽和高,让图片居中显示int imgW = overImg.getWidth(null);int imgH = overImg.getHeight(null);g.drawImage(overImg, (FRAME_WIDTH - imgW) / 2, (FRAME_HIGH - imgH) / 2, null);// 添加按键提示信息g.setColor(Color.RED);g.drawString(OVER_STR[0], 120, FRAME_HIGH - 100);g.setColor(Color.BLUE);g.drawString(OVER_STR[1], FRAME_WIDTH - 500, FRAME_HIGH - 100);}// 绘制游戏胜利的方法private void drawWin(Graphics g) {// 只有winImg为空的时候才加载if (winImg == null) {winImg = MyUtil.createImage("image/gamewin.png");}// 求图片宽和高,让图片居中显示int imgW = winImg.getWidth(null);int imgH = winImg.getHeight(null);g.drawImage(winImg, (FRAME_WIDTH - imgW) / 2, (FRAME_HIGH - imgH) / 2, null);// 添加按键提示信息g.setColor(Color.RED);g.drawString(OVER_STR[0], 120, FRAME_HIGH - 100);g.setColor(Color.BLUE);g.drawString(OVER_STR[1], FRAME_WIDTH - 500, FRAME_HIGH - 100);}/* =================END==========绘制方法==================================== *//* =================START==========按键响应==================================== *//*** 菜单状态对按键的相应** @param keyCode*/private void keyPressEventMenu(int keyCode) {//每按一次就播放一次音乐
//        MusicUtil.playMenuCase();     //效果不佳,取消此选项switch (keyCode) {// 按了上键和W键case KeyEvent.VK_UP:case KeyEvent.VK_W:// 每按一次menuIndex就减1,小于0时变成最大menuIndex--;if (menuIndex < 0) {menuIndex = MENUS.length - 1;}break;// 按了下键和S键case KeyEvent.VK_DOWN:case KeyEvent.VK_S:menuIndex++;if (menuIndex > MENUS.length - 1) {menuIndex = 0;}break;// 如果按下了回车键,就说明开始新游戏case KeyEvent.VK_ENTER:switch (menuIndex) {case 0: // 开始游戏isTwoMode = false;    //设置为单人模式newGame(1);break;case 1: //双人游戏isTwoMode = true;     //设置为双人模式newGame(1);break;case 2: // 选择关卡setGameState(STATE_LEVEL);break;case 3: // 帮助setGameState(STATE_HELP);break;case 4: // 关于setGameState(STATE_ABOUT);break;case 5: // 退出游戏System.exit(0);break;}break;}}/*** 菜单状态对按键的相应** @param keyCode*/private void keyPressEventlevel(int keyCode) {switch (keyCode) {case KeyEvent.VK_LEFT:// 每按一次menuIndex就减1,小于0时变成最大levelIndex--;if (levelIndex < 0) {levelIndex = levlCount - 1;}break;case KeyEvent.VK_RIGHT:levelIndex++;if (levelIndex > levlCount - 1) {levelIndex = 0;}break;// 如果按下了回车键,就说明开始新游戏case KeyEvent.VK_ENTER:newGame(levelIndex + 1);break;}}/*** 游戏运行中的按键处理方法-移动** @param keyCode*/private void keyPressEventRun(int keyCode) {switch (keyCode) {case KeyEvent.VK_UP:myTank2.setDir(Tank.DIR_UP);myTank2.setState(Tank.STATE_MOVE); // 二号坦克移动break;case KeyEvent.VK_W:myTank.setDir(Tank.DIR_UP);myTank.setState(Tank.STATE_MOVE); // 坦克移动break;case KeyEvent.VK_DOWN:myTank2.setDir(Tank.DIR_DOWN);myTank2.setState(Tank.STATE_MOVE); // 二号坦克移动break;case KeyEvent.VK_S:myTank.setDir(Tank.DIR_DOWN);myTank.setState(Tank.STATE_MOVE); // 坦克移动break;case KeyEvent.VK_LEFT:myTank2.setDir(Tank.DIR_LEFT);myTank2.setState(Tank.STATE_MOVE); // 二号坦克移动break;case KeyEvent.VK_A:myTank.setDir(Tank.DIR_LEFT);myTank.setState(Tank.STATE_MOVE); // 坦克移动break;case KeyEvent.VK_RIGHT:myTank2.setDir(Tank.DIR_RIGHT);myTank2.setState(Tank.STATE_MOVE); // 二号坦克移动break;case KeyEvent.VK_D:myTank.setDir(Tank.DIR_RIGHT);myTank.setState(Tank.STATE_MOVE); // 坦克移动break;case KeyEvent.VK_SPACE:myTank.fire();break;case KeyEvent.VK_NUMPAD0:myTank2.fire();break;}}/*** 游戏运行中的按键处理方法-松开按键坦克停止** @param keyCode*/private void keyReleasedEventRun(int keyCode) {switch (keyCode) {case KeyEvent.VK_UP:myTank2.setState(Tank.STATE_STAND); // 二号坦克停止break;case KeyEvent.VK_W:myTank.setState(Tank.STATE_STAND); // 坦克停止break;case KeyEvent.VK_DOWN:myTank2.setState(Tank.STATE_STAND); // 二号坦克停止break;case KeyEvent.VK_S:myTank.setState(Tank.STATE_STAND); // 坦克停止break;case KeyEvent.VK_LEFT:myTank2.setState(Tank.STATE_STAND); // 二号坦克停止break;case KeyEvent.VK_A:myTank.setState(Tank.STATE_STAND); // 坦克停止break;case KeyEvent.VK_RIGHT:myTank2.setState(Tank.STATE_STAND); // 二号坦克停止break;case KeyEvent.VK_D:myTank.setState(Tank.STATE_STAND); // 坦克停止break;}}/*** 游戏关于界面按键处理** @param keyCode*/private void keyPressEventAbout(int keyCode) {setGameState(STATE_MENU);}/*** 游戏帮助按界面键处理** @param keyCode*/private void keyPressEventHelp(int keyCode) {setGameState(STATE_MENU);}/*** 游戏结束的按键处理** @param keyCode*/private void keyPressEventOver(int keyCode) {// 如果按了ESC就退出游戏if (keyCode == KeyEvent.VK_ESCAPE) {System.exit(0);} else if (keyCode == KeyEvent.VK_ENTER) {// 如果按了ENTER就回到主菜单,并且开始播放背景音乐setGameState(STATE_MENU);MusicUtil.playStart();// 还需要关闭很多游戏操作,重置某些属性resetGame();}}/*** 游戏通关按键的处理** @param keyCode*/private void keyPressEventWin(int keyCode) {keyPressEventOver(keyCode); // 直接调用游戏结束的按键处理}/* =================END==========按键响应==================================== *//*** 开始游戏方法** @param level 关卡信息*/private static void newGame(int level) {// 先清空敌人坦克容器enemies.clear();if (gameMap == null) {gameMap = new GameMap();}// 设置关卡信息gameMap.initMap(level);// 播放背景音乐// MusicUtil.playStart();MusicUtil.stopStart();// 先把产生的敌人数量设置为0bornEnemyCount = 0;// 初始化消灭的敌人killEnemyCount = 0;// 设置当前模式gameState = STATE_RUN;// 创建坦克对象,x y就是坦克出生的坐标,最后的属性是设置坦克默认朝向myTank = new MyTank(500, 670, Tank.DIR_UP);     //创建一号坦克对象myTank2 = new MyTank(780, 670, Tank.DIR_UP);    //创建二号坦克对象myTank2.setTwo(true);   //设置为二号坦克// 判断是否为单人模式,如果为单人模式那么二号坦克对象直接设置为0血if (!isTwoMode) {myTank2.setHp(0);}// 使用一个单独的线程用于创建敌人的坦克new Thread() {@Overridepublic void run() {while (true) {// 如果没有超过本关最大坦克数量和屏幕最大坦克数量才新建坦克if (LevelInof.getInstance().getEnemyCount() > bornEnemyCount && enemies.size() < ENEMY_MAX_COUNT) {Tank enemy = EnemyTank.createEnemy();enemies.add(enemy);bornEnemyCount++; // 每创建一个敌人就加1}// 产生坦克生成间隔时间try {Thread.sleep(ENEMY_BORN_INTERVAL);} catch (InterruptedException e) {e.printStackTrace();}// 如果游戏不在RUN状态就不再创建敌人if (gameState != STATE_RUN) {break;}}}}.start();}/*** 初始化窗口按键监听*/private void initEventListener() {// 注册监听事件addWindowListener(new WindowAdapter() {// 点击关闭按钮时自动调用此方法@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}});// 添加按键监听事件addKeyListener(new KeyAdapter() {// 按下按键@Overridepublic void keyPressed(KeyEvent e) {// 获取按下的键int keyCode = e.getKeyCode();// 把获取到的键值传递给当前的状态switch (gameState) {case STATE_MENU:keyPressEventMenu(keyCode);break;case STATE_LEVEL:keyPressEventlevel(keyCode);break;case STATE_HELP:keyPressEventHelp(keyCode);break;case STATE_ABOUT:keyPressEventAbout(keyCode);break;case STATE_RUN:keyPressEventRun(keyCode);break;case STATE_OVER:keyPressEventOver(keyCode);break;case STATE_WIN:keyPressEventWin(keyCode);break;}}// 按键松开时,游戏中的处理方法@Overridepublic void keyReleased(KeyEvent e) {// 获取按下的键int keyCode = e.getKeyCode();// 把获取到的键值传递给当前的状态if (gameState == STATE_RUN) {keyReleasedEventRun(keyCode);}}});}/*** 单独设置刷新率的线程*/@Overridepublic void run() {while (true) {// 在此调用repaintrepaint();try {Thread.sleep(REPAINT_INTERVAL);} catch (InterruptedException e) {e.printStackTrace();}}}/*** 子弹碰撞方法*/private void bulletCollideTank() {// 我的子弹和敌人的坦克碰撞for (int i = 0; i < enemies.size(); i++) {Tank enemy = enemies.get(i);enemy.collideBullets(myTank.getBullets());enemy.collideBullets(myTank2.getBullets());//队友伤害if (killMyTank) {myTank2.collideBullets(myTank.getBullets());myTank.collideBullets(myTank2.getBullets());}}// 敌人的子弹和我的坦克碰撞for (int i = 0; i < enemies.size(); i++) {Tank enemy = enemies.get(i);myTank.collideBullets(enemy.getBullets());myTank2.collideBullets(enemy.getBullets());}}/*** 坦克和坦克的碰撞方法*/private void tankCollideTank() {//敌方坦克和我方坦克的碰撞for (Tank enemy : enemies) {boolean collideTank = enemy.isCollideTank(myTank);boolean collideTank2 = enemy.isCollideTank(myTank2);if (collideTank || collideTank2) {enemy.back();}}//我方坦克和坦克的碰撞boolean collideTanks = myTank.isCollideTanks(enemies);   //一号坦克和敌方坦克的碰撞检测boolean collideTanks2 = myTank2.isCollideTanks(enemies); //二号坦克和敌方坦克的碰撞检测boolean collideTank = myTank.isCollideTank(myTank2);     //一号坦克和二号坦克的碰撞检测boolean collideTank2 = myTank2.isCollideTank(myTank);     //二号坦克和一号坦克的碰撞检测//如果发生了碰撞坦克就后退if (collideTank || collideTanks) {myTank.back();}if (collideTank2 || collideTanks2) {myTank2.back();}}/*** 子弹和地图块的碰撞方法 坦克和地图块的碰撞方法*/private void bulletAndTanksCollideMapTile() {// 自己坦克子弹和所有地图块的碰myTank.bulletsCollideMapTiles(gameMap.getTiles());myTank2.bulletsCollideMapTiles(gameMap.getTiles());// 敌人坦克子弹和所有地图块的碰撞for (Tank enemy : enemies) {enemy.bulletsCollideMapTiles(gameMap.getTiles());}// 我的坦克和地图块的碰撞boolean collideTile = myTank.isCollideTile(gameMap.getTiles());boolean collideTile2 = myTank2.isCollideTile(gameMap.getTiles());//如果发生了碰撞坦克就后退if (collideTile) {myTank.back();}if (collideTile2) {myTank2.back();}// 敌人的坦克和地图块的碰撞for (Tank enemy : enemies) {boolean collideTile1 = enemy.isCollideTile(gameMap.getTiles());if (collideTile1) {enemy.back();}}// 清理所有的被销毁的地图块gameMap.clearDestoryTile();}// 绘制所有坦克上的爆炸效果private void drawExplodes(Graphics g) {// 绘制所有敌人坦克上的爆炸效果for (Tank enemy : enemies) {enemy.drawExplodes(g);}// 绘制自己坦克上的爆炸效果myTank.drawExplodes(g);myTank2.drawExplodes(g);}/*** 重置游戏状态方法*/private void resetGame() {menuIndex = 0; // 重置菜单指向myTank.bulletsReturn(); // 还回所有子弹,并清空子弹容器myTank = null; // 销毁自己的坦克myTank2.bulletsReturn(); // 还回所有子弹,并清空子弹容器myTank2 = null; // 销毁自己的坦克// 还回敌人所有子弹,并且清空子弹容器,归还坦克for (Tank enemy : enemies) {enemy.bulletsReturn();EnemyTanksPool.theReturn(enemy);}enemies.clear(); // 清空敌人坦克容器gameMap.clearMap(); // 还回所有地图块gameMap = null; // 清空地图资源}/*** 判断游戏是否是最后一关** @return true表示为最后一关*/public static boolean isLastLevel() {// 获取当前关卡信息int currLevel = LevelInof.getInstance().getLevel();// int levlCount = GameInfo.getLevelCount();// 如果当前关卡是最后一关就返回truereturn currLevel == levlCount;}/*** 判断是否过关** @return true为过关*/public static boolean isCrossLevel() {// 如果杀敌数量等于关卡规定数量就说明过关了return killEnemyCount == LevelInof.getInstance().getEnemyCount();}/*** 进入下一关的方法*/public static void nextLevel() {// 当前关卡加一newGame(LevelInof.getInstance().getLevel() + 1);}}

GameInfo类

package c02.n02.game;import java.io.FileInputStream;
import java.util.Properties;/*** 游戏相关的信息类*/
public class GameInfo {//关卡数量private static int levelCount;static {Properties prop = new Properties();try {//加载配置文件prop.load(new FileInputStream("level/gameinfo"));//获取关卡数量levelCount = Integer.parseInt(prop.getProperty("levelCount"));} catch (Exception e) {e.printStackTrace();}}/*** 获取关卡总数量** @return 返回关卡数量*/public static int getLevelCount() {return levelCount;}}

LevelInfo类

package c02.n02.game;import c02.n02.util.MyUtil;/*** 用来管理当前关卡的信息的类*/
public class LevelInof {//关卡编号private int level;//敌人的数量private int enemyCount;//通关的要求的时长,-1不限时private int crossTime = -1;//敌人类型信息private int[] enemyType;//游戏难度private int levelType;//===========START=================GETTER/SETTER===============public int getLevel() {return level;}public void setLevel(int level) {this.level = level;}public int getEnemyCount() {return enemyCount;}public void setEnemyCount(int enemyCount) {this.enemyCount = enemyCount;}public int getCrossTime() {return crossTime;}public void setCrossTime(int crossTime) {this.crossTime = crossTime;}public int[] getEnemyType() {return enemyType;}public void setEnemyType(int[] enemyType) {this.enemyType = enemyType;}public int getLevelType() {if (levelType <= 0) {levelType = 1;}return levelType;}public void setLevelType(int leevelType) {this.levelType = leevelType;}//===========END=================GETTER/SETTER===============//构造方法私有化private LevelInof() {}//定义静态的本类类型的变量,用来指向唯一的实例private static LevelInof instance;/*** 懒汉模式的单例* 第一次使用该实例的时候创建唯一的实例** @return*/public static LevelInof getInstance() {if (instance == null) {//创建了唯一的实例instance = new LevelInof();}return instance;}/*** 获得一个随机的敌人类型** @return 返回敌人类型*/public int getRandomEnemyType() {int index = MyUtil.getRandomNumber(0, enemyType.length);return enemyType[index];}}

GameMap类

package c02.n02.map;import c02.n02.game.GameFrame;
import c02.n02.game.LevelInof;
import c02.n02.tank.Tank;
import c02.n02.util.Constant;
import c02.n02.util.MapTilePool;import java.awt.*;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;/*** 游戏地图类* 游戏中显示的地图都来自此类*/
public class GameMap {//设置地图坐标,地图离边框1.5个坦克距离public static final int MAP_X = Tank.RADIUS * 3;public static final int MAP_Y = Tank.RADIUS * 3 + GameFrame.titleBarH;//设置地图宽高public static final int MAP_WIDTH = Constant.FRAME_WIDTH - Tank.RADIUS * 6;public static final int MAP_HEIGHT = Constant.FRAME_HIGH - Tank.RADIUS * 8 - GameFrame.titleBarH;//地图距离边框为0
//    public static final int MAP_X = 0;
//    public static final int MAP_Y = 30;
//    public static final int MAP_WIDTH = Constant.FRAME_WIDTH;
//    public static final int MAP_HEIGHT = Constant.FRAME_HIGH;private int width;  //地图宽private int height; //地图高//地图元素块的容器private List<MapTile> tiles = new ArrayList<>();//基地对象private TankHouse house;//===========START=================GETTER/SETTER===============public List<MapTile> getTiles() {return tiles;}public void setTiles(List<MapTile> tiles) {this.tiles = tiles;}//===========END=================GETTER/SETTER===============/*** 游戏地图类的无参构造函数*/public GameMap() {}/*** 初始化地图元素块** @param level 第几关*/public void initMap(int level) {//初始化地图,先把里面原有的块清空掉重新加载tiles.clear();//从关卡文件里读取关卡信息try {loadLevel(level);} catch (Exception e) {e.printStackTrace();}//初始化基地house = new TankHouse();addHouse();}/*** 加载关卡信息** @param level 第几关*/private void loadLevel(int level) throws Exception {//获得关卡信息类的唯一实例对象LevelInof levelInof = LevelInof.getInstance();//将所有的地图信息都加载进来Properties prop = new Properties();prop.load(new FileInputStream("level/lv_" + level));//从文件中读取指定的信息//敌人数量int enemyCount = Integer.parseInt(prop.getProperty("enemyCount"));//敌人类型,根据逗号分隔String[] enemyType = prop.getProperty("enemyType").split(",");int[] type = new int[enemyType.length];for (int i = 0; i < type.length; i++) {type[i] = Integer.parseInt(enemyType[i]);}//调用的方法String methodName = prop.getProperty("method");//方法调用的次数int invokeCount = Integer.parseInt(prop.getProperty("invokeCount"));//把参数读取到数组中String[] params = new String[invokeCount];for (int i = 1; i <= invokeCount; i++) {params[i - 1] = prop.getProperty("param" + i);}//获取关卡难度信息int levelType = Integer.parseInt(prop.getProperty("levelType"));//设置关卡信息levelInof.setEnemyCount(enemyCount);    //设置敌人数量levelInof.setLevel(level);      //设置关卡编号levelInof.setEnemyType(type);   //设置敌人类型levelInof.setLevelType(levelType);  //设置关卡难度//根据方法的名字和参数调用对应的方法invokeMethod(methodName, params);}/*** 根据方法的名字和参数调用对应的方法** @param name   调用的方法名* @param params 参数*/private void invokeMethod(String name, String[] params) {for (String param : params) {//获取每一行的参数,解析String[] split = param.split(",");//使用一个数组来保存解析后的内容int[] arr = new int[split.length];for (int i = 0; i < split.length; i++) {arr[i] = Integer.parseInt(split[i]);}//块之间的间隔是地图块的倍数final int DIS = MapTile.tileW;//根据配置信息生成块switch (name) {case "addRow":addRow(MAP_X + arr[0] * DIS, MAP_Y + arr[1] * DIS, MAP_X + MAP_WIDTH - arr[2] * DIS, arr[3], arr[4] * DIS);break;case "addCol":addCol(MAP_X + arr[0] * DIS, MAP_Y + arr[1] * DIS, MAP_Y + MAP_HEIGHT - arr[2] * DIS, arr[3], DIS);break;case "addRect":addRect(MAP_X + arr[0] * DIS, MAP_Y + arr[1] * DIS, MAP_X + MAP_WIDTH - arr[2] * DIS, MAP_Y + MAP_HEIGHT - arr[2] * DIS, arr[4], DIS);break;case "addCustom":addCustom(params);return;}}}/*** 将基地的块添加到地图容器中*/private void addHouse() {tiles.addAll(house.getTiles());}/*** 判断某个点是否和tiles集合中的所有块发生了重叠** @param tiles tiles集合* @param x     点x* @param y     点y* @return 有重叠返回true,否则返回false*/private boolean isCollide(List<MapTile> tiles, int x, int y) {for (MapTile tile : tiles) {int tileX = tile.getX();int tileY = tile.getY();//如果点到块左上点的距离小于块的宽度就说明发生了重叠if (Math.abs(tileX - x) < MapTile.tileW && Math.abs(tileY - y) < MapTile.tileW) {return true;}}return false;}/*** 用来绘制没有遮挡效果的块* 比如 砖块、铁块** @param g*/public void drawBK(Graphics g) {for (MapTile tile : tiles) {if (tile.getType() != MapTile.TYPE_COVER) {tile.draw(g);}}}/*** 用来绘制有遮挡效果的块* 比如 草块** @param g*/public void drawCover(Graphics g) {for (MapTile tile : tiles) {if (tile.getType() == MapTile.TYPE_COVER) {tile.draw(g);}}}/*** 将所有不可见的地图块从容器中移除*/public void clearDestoryTile() {for (int i = 0; i < tiles.size(); i++) {MapTile tile = tiles.get(i);if (!tile.isVisible()) {tiles.remove(i);}}}/*** 往地图容器中添加一行指定类型的地图块** @param startX 起始x点坐标* @param startY 起始y点坐标* @param endX   结束点x坐标,因为是生成一行,所以不需要结束点Y坐标* @param type   地图块类型* @param DIS    地图块的间隔,如果是块的宽度就说明地图块是连续的*               如果大于块的宽度就说明是不连续的*/public void addRow(int startX, int startY, int endX, int type, final int DIS) {//计算一行有多少个地图块int count = 0;count = (endX - startX) / (MapTile.tileW + DIS);for (int i = 0; i < count; i++) {MapTile tile = MapTilePool.get();tile.setType(type);tile.setX(startX + i * (MapTile.tileW + DIS));tile.setY(startY);tile.setVisible(true);tiles.add(tile);}}/*** 往地图容器中添加一列指定类型的地图块** @param startX 起始点x坐标* @param startY 起始点y坐标* @param endY   结束点y坐标* @param type   地图块类型* @param DIS    地图块的间隔,如果是块的宽度就说明地图块是连续的*               如果大于块的宽度就说明是不连续的*/public void addCol(int startX, int startY, int endY, int type, final int DIS) {//计算一列有多少个地图块int count = 0;count = (endY - startY) / (MapTile.tileW + DIS);for (int i = 0; i < count; i++) {MapTile tile = MapTilePool.get();tile.setType(type);tile.setX(startX);tile.setY(startY + i * (MapTile.tileW + DIS));tile.setVisible(true);tiles.add(tile);}}/*** 往地图容器中添加一个矩形地图块** @param startX 起始点x* @param startY 起始点y* @param endX   结束点x* @param endY   结束点y* @param type   地图块类型* @param DIS    地图块水平和垂直间隔*/public void addRect(int startX, int startY, int endX, int endY, int type, final int DIS) {//        //计算一行应该填充多少地图块
//        int rows = (endX - startX) / (MapTile.tileW + DIS);//计算一列应该填充多少地图块int cols = (endY - startY) / (MapTile.tileW + DIS);//一个矩形区域由若干行组成for (int i = 0; i < cols; i++) {addRow(startX, startY + i * (MapTile.tileW + DIS), endX, type, DIS);}}/*** 往地图容器中添加自定义的地图块,通过配置文件里的数组来设置地图** @param params*/public void addCustom(String[] params) {for (int i = 0; i < params.length; i++) {String param = params[i];//获取每一行的参数,解析String[] split = param.split(",");//使用一个数组来保存解析后的内容int[] arr = new int[split.length];for (int j = 0; j < split.length; j++) {arr[j] = Integer.parseInt(split[j]);}//如果类型等于9那么就跳过本次循环for (int j = 0; j < arr.length; j++) {int a = arr[j];if (a == 9) {continue;}//砖块对象池中拿出一个砖块MapTile tile = MapTilePool.get();tile.setType(a);tile.setX(MAP_X + j * 60);tile.setY(MAP_Y + i * 60);tile.setVisible(true);tiles.add(tile);}}}/*** 清空地图容器并且把所有地图块还回池中*/public void clearMap() {for (MapTile tile : tiles) {tile.setVisible(false);MapTilePool.theReturn(tile);}tiles.clear();}}

MapTile类

package c02.n02.map;import c02.n02.game.Bullet;
import c02.n02.util.MyUtil;import java.awt.*;
import java.util.List;/*** 地图中的所有块的类*/
public class MapTile {//地图块的类型public static final int TYPE_HOUSE = 0;     //基地块public static final int TYPE_NORMAL = 1;    //普通块(砖块)public static final int TYPE_HARD = 2;      //铁块public static final int TYPE_COVER = 3;     //遮挡块(草丛)//设置地图块类型,默认砖块private int type = TYPE_NORMAL;//设置图片宽,因为图片是正方形,所以只需要获取一个public static int tileW = 60;//设置半径public static int radius = tileW / 2;//创建图片数组private static Image[] tileImg;static {tileImg = new Image[4];tileImg[TYPE_NORMAL] = MyUtil.createImage("image/Map/tile.png");tileImg[TYPE_HOUSE] = MyUtil.createImage("image/Map/house.png");tileImg[TYPE_COVER] = MyUtil.createImage("image/Map/cover.png");tileImg[TYPE_HARD] = MyUtil.createImage("image/Map/hard.png");}//砖块左上角坐标private int x;private int y;//砖块是否可见private boolean visible = true;//===========START=================GETTER/SETTER===============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 boolean isVisible() {return visible;}public void setVisible(boolean visible) {this.visible = visible;}public int getType() {return type;}public void setType(int type) {this.type = type;}//===========END=================GETTER/SETTER===============/*** 地图块类的无参构造函数*/public MapTile() {}/*** 地图块类的构造函数** @param x X坐标* @param y Y坐标*/public MapTile(int x, int y) {this.x = x;this.y = y;}/*** 绘制方法* 根据不同的类型绘制不同的砖块** @param g*/public void draw(Graphics g) {//不可见就不绘制if (!visible) {return;}g.drawImage(tileImg[type], x, y, null);}/*** 判断块是否碰到了子弹(所有子弹)* 遮挡块不用判断** @param bullets 所有子弹所在的列表* @return 碰撞了返回true否则返回false*/public boolean isCollideBullet(List<Bullet> bullets) {//如果地图块不可见或者块类型为遮挡块,直接返回falseif (!visible || type == TYPE_COVER) {return false;}for (Bullet bullet : bullets) {//获取子弹的坐标int bulletX = bullet.getX();int bulletY = bullet.getY();//判断是否碰撞,因为传入的是方块的中心点,所以x y需要加半径boolean collide = MyUtil.isCollide(x + radius, y + radius, radius, bulletX, bulletY);if (collide) {//销毁子弹bullet.setVisible(false);return true;}}return false;}/*** 判断当前的地图块是否是基地** @return  如果为基地块就返回true*/public boolean isHouse() {return type == TYPE_HOUSE;}}

TankHouse类

package c02.n02.map;import c02.n02.util.Constant;import java.awt.*;
import java.util.ArrayList;
import java.util.List;/*** 我方基地* 大概就长这样* ■■■* ■☆■*/
public class TankHouse {//坐标public static final int HOUSE_X = (Constant.FRAME_WIDTH - 3 * MapTile.tileW) / 2;public static final int HOUSE_Y = Constant.FRAME_HIGH - 2 * MapTile.tileW;//地图块容器private List<MapTile> tiles = new ArrayList<>();public TankHouse() {tiles.add(new MapTile(HOUSE_X + MapTile.tileW, HOUSE_Y + MapTile.tileW));    //中间,基地tiles.add(new MapTile(HOUSE_X, HOUSE_Y));    //左上tiles.add(new MapTile(HOUSE_X, HOUSE_Y + MapTile.tileW));    //左下tiles.add(new MapTile(HOUSE_X + MapTile.tileW, HOUSE_Y));    //上中tiles.add(new MapTile(HOUSE_X + 2 * MapTile.tileW, HOUSE_Y));    //右上tiles.add(new MapTile(HOUSE_X + 2 * MapTile.tileW, HOUSE_Y + MapTile.tileW));    //右下//设置基地块的类型tiles.get(0).setType(MapTile.TYPE_HOUSE);}/*** 获取我方基地块** @return 返回一个地图块数组*/public List<MapTile> getTiles() {return tiles;}/*** 设置我方基地块** @param tiles 传入一个地图块数组*/public void setTiles(List<MapTile> tiles) {this.tiles = tiles;}/*** 我方基地绘制方法** @param g*/public void draw(Graphics g) {for (MapTile tile : tiles) {tile.draw(g);}}
}

EnemyTank类

package c02.n02.tank;import c02.n02.game.GameFrame;
import c02.n02.game.LevelInof;
import c02.n02.util.Constant;
import c02.n02.util.EnemyTanksPool;
import c02.n02.util.MyUtil;import java.awt.*;/*** 敌人坦克类* 继承了坦克类*/
public class EnemyTank extends Tank {public static final int TYPE_GREEN = 0;   //绿坦克public static final int TYPE_YELLOW = 1;    //黄色坦克//敌人坦克类型,默认绿坦克private int type = TYPE_GREEN;//定义坦克图片数组private static Image[] greenImg;    //绿色坦克图片数组private static Image[] yellowImg;    //黄色图片数组//记录5秒开始的时间private long aiTime;//在静态代码块中进行初始化static {greenImg = new Image[4];greenImg[0] = MyUtil.createImage("image/TankImg/enemyTank/green/u.png");greenImg[1] = MyUtil.createImage("image/TankImg/enemyTank/green/d.png");greenImg[2] = MyUtil.createImage("image/TankImg/enemyTank/green/l.png");greenImg[3] = MyUtil.createImage("image/TankImg/enemyTank/green/r.png");
//todo 替换图片yellowImg = new Image[4];yellowImg[0] = MyUtil.createImage("image/TankImg/enemyTank/yellow/u.png");yellowImg[1] = MyUtil.createImage("image/TankImg/enemyTank/yellow/d.png");yellowImg[2] = MyUtil.createImage("image/TankImg/enemyTank/yellow/l.png");yellowImg[3] = MyUtil.createImage("image/TankImg/enemyTank/yellow/r.png");}/*** 获取敌人坦克类型** @return 返回类型*/public int getType() {return type;}/*** 设置坦克类型** @param type 传入类型*/public void setType(int type) {this.type = type;}/*** 敌人坦克类的无参构造方法*/public EnemyTank() {//当敌人创建时获取系统时间aiTime = System.currentTimeMillis();//随机创建不同的坦克
//        type = MyUtil.getRandomNumber(0, 2);}/*** 敌人坦克类的构造方法** @param x   X坐标* @param y   Y坐标* @param dir 方向*/public EnemyTank(int x, int y, int dir) {super(x, y, dir);//当敌人创建时获取系统时间aiTime = System.currentTimeMillis();//随机创建不同的坦克
//        type = MyUtil.getRandomNumber(0, 2);}/*** 重写坦克类的绘制方法** @param g*/@Overridepublic void drawImgTank(Graphics g) {ai();   //调用AI//根据不同的类型绘制不同的坦克if (type == TYPE_GREEN) {g.drawImage(greenImg[getDir()], getX() - RADIUS, getY() - RADIUS, null);} else if (type == TYPE_YELLOW) {g.drawImage(yellowImg[getDir()], getX() - RADIUS, getY() - RADIUS, null);}}/*** 创建一个敌人坦克** @return 返回一个敌人坦克对象*/public static Tank createEnemy() {int x = MyUtil.getRandomNumber(0, 2);  //生成0或1int y = GameFrame.titleBarH + RADIUS;   //定位在顶部int dir = DIR_DOWN;   //方向默认朝下//只在左上角和右上角生成坦克if (x == 0) {x = RADIUS;   //如果X=0就在左上角生成坦克} else {x = Constant.FRAME_WIDTH - RADIUS;}EnemyTank enemy = (EnemyTank) EnemyTanksPool.get();  //从坦克池里拿坦克,强制转换成敌人坦克类型enemy.setX(x);enemy.setY(y);enemy.setDir(dir);enemy.setEnemy(true); //设置为敌方坦克enemy.setState(STATE_MOVE);//为了避免拿到已经死亡的坦克就要重新设血量属性//根据关卡难度设置血量int hp = DEFAULT_HP * LevelInof.getInstance().getLevelType();enemy.setHp(hp);enemy.setMaxHp(hp);//通过关卡信息中的敌人类型来设置当前出生的敌人类型int enemyType = LevelInof.getInstance().getRandomEnemyType();enemy.setType(enemyType);return enemy;}/*** 坦克AI* 每隔一段时间让敌人坦克随机获得一个状态(站立,行走)* 游戏的每一帧都随机(概率可调)判断敌人是否发射子弹*/private void ai() {//如果当前的时间减去创建坦克的时间大于2秒就切换状态if (System.currentTimeMillis() - aiTime > Constant.ENEMY_AI_INTERVAL) {//改变方向int dir = MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT + 1);  //获取一个随机的方向int tankDir = getDir(); //获取目前坦克的方向//判断是否在边缘,如果在边缘并且方向还是往外,那么就立即重新随机获取一个方向if (super.getX() <= 30 && dir == DIR_LEFT) {dir = MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT + 1);} else if (super.getX() >= Constant.FRAME_WIDTH - 70 && dir == DIR_RIGHT) {dir = MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT + 1);} else if (super.getY() <= 50 && dir == DIR_UP) {dir = MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT + 1);} else if (super.getX() >= Constant.FRAME_HIGH - 30 && dir == DIR_DOWN) {dir = MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT + 1);}setDir(dir);//改变状态int i = MyUtil.getRandomNumber(0, 2);if (i == 0) {setState(STATE_STAND);} else {setState(STATE_MOVE);}//重新计时aiTime = System.currentTimeMillis();}//生成一个0-1的随机数,如果小于概率的话就发射炮弹if (Math.random() < Constant.ENEMY_FIRE_PERCENT) {fire();}}}

MyTank类

package c02.n02.tank;import c02.n02.util.MyUtil;import java.awt.*;/*** 自己坦克类* 继承了坦克类*/
public class MyTank extends Tank {//坦克图片数组private static Image[] tankImg;//二号坦克图片数组private static Image[] tankImg2;//在静态代码块中进行初始化static {tankImg = new Image[4];tankImg[0] = MyUtil.createImage("image/TankImg/myTank/u.png");tankImg[1] = MyUtil.createImage("image/TankImg/myTank/d.png");tankImg[2] = MyUtil.createImage("image/TankImg/myTank/l.png");tankImg[3] = MyUtil.createImage("image/TankImg/myTank/r.png");tankImg2 = new Image[4];tankImg2[0] = MyUtil.createImage("image/TankImg/myTank/u2.png");tankImg2[1] = MyUtil.createImage("image/TankImg/myTank/d2.png");tankImg2[2] = MyUtil.createImage("image/TankImg/myTank/l2.png");tankImg2[3] = MyUtil.createImage("image/TankImg/myTank/r2.png");}/*** 自己坦克的构造方法** @param x   X坐标* @param y   Y坐标* @param dir 方向*/public MyTank(int x, int y, int dir) {super(x, y, dir);}/*** 重写draw方法** @param g*/@Overridepublic void drawImgTank(Graphics g) {//如果为二号坦克就画二号坦克的图片if (!isTwo()) {g.drawImage(tankImg[getDir()], getX() - RADIUS, getY() - RADIUS, null);} else {g.drawImage(tankImg2[getDir()], getX() - RADIUS, getY() - RADIUS, null);}}}

Tank类

package c02.n02.tank;import c02.n02.game.Bullet;
import c02.n02.game.Explode;
import c02.n02.game.GameFrame;
import c02.n02.map.MapTile;
import c02.n02.util.*;import java.awt.*;
import java.util.ArrayList;
import java.util.List;/** 坦克类* */
public abstract class Tank {//四个方向public static final int DIR_UP = 0;public static final int DIR_DOWN = 1;public static final int DIR_LEFT = 2;public static final int DIR_RIGHT = 3;//坦克的半径public static final int RADIUS = 30;//默认速度,每帧的速度public static final int DEFAULT_SPEED = 10;//坦克的状态public static final int STATE_STAND = 0; //静止public static final int STATE_MOVE = 1;  //移动public static final int STATE_DIE = 2;   //死亡//坦克的初始生命值public static final int DEFAULT_HP = 1000;//坦克的最大血量private int maxHp = DEFAULT_HP;private int x, y;       //坦克坐标private int oldX = -1;  //坦克旧坐标private int oldY = -1;private int hp = DEFAULT_HP;    //当前血量private String name;    //坦克名字private int atk;        //攻击力private boolean isTwo = false;    //设置是否为第二号坦克//攻击力范围public static final int ATK_MAX = 200;public static final int ATK_MIN = 150;//坦克上一次开火的时间,用于设置开火间隔private long fireTime;//开火间隔public static final int FIRE_INTERVAL = 200;private int speed = DEFAULT_SPEED;  //速度private int dir;        //方向private int state = STATE_STAND;    //坦克状态private Color color;    //坦克颜色private boolean isEnemy = false;    //是否是敌人//创建血条对象private BloodBar bar = new BloodBar();//炮弹容器private List<Bullet> bullets = new ArrayList();//爆炸效果容器private List<Explode> explodes = new ArrayList<>();//===========START=================GETTER/SETTER===============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 getHp() {return hp;}public void setHp(int hp) {this.hp = hp;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getSpeed() {return speed;}public void setSpeed(int speed) {this.speed = speed;}public int getDir() {return dir;}public void setDir(int dir) {this.dir = dir;}public int getState() {return state;}public void setState(int state) {this.state = state;}public Color getColor() {return color;}public void setColor(Color color) {this.color = color;}public List getBullets() {return bullets;}public void setBullets(List bullets) {this.bullets = bullets;}public boolean isEnemy() {return isEnemy;}public void setEnemy(boolean enemy) {isEnemy = enemy;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getMaxHp() {return maxHp;}public void setMaxHp(int maxHp) {this.maxHp = maxHp;}public boolean isTwo() {return isTwo;}public void setTwo(boolean isTwo) {this.isTwo = isTwo;}//==============END==============GETTER/SETTER==================//构造函数public Tank(int x, int y, int dir) {this.x = x;this.y = y;this.dir = dir;color = MyUtil.getRandomColor();name = MyUtil.getRandomName();atk = MyUtil.getRandomNumber(ATK_MIN, ATK_MAX);    //随机一个攻击力}//无参构造函数public Tank() {color = MyUtil.getRandomColor();name = MyUtil.getRandomName();atk = MyUtil.getRandomNumber(ATK_MIN, ATK_MAX);    //随机一个攻击力}/** 全部的绘制方法* 绘制坦克*  绘制炮弹* */public void draw(Graphics g) {logic();drawImgTank(g);     //绘制坦克drawBullets(g);     //绘制炮弹drawName(g);    //绘制名字bar.draw(g);    //绘制血条}private void drawName(Graphics g) {g.setColor(color);g.setFont(Constant.SMALL_FONT);g.drawString(name, x - RADIUS, y - 50);}//绘制图片坦克public abstract void drawImgTank(Graphics g);
//    {//        //敌我坦克图片不同,所以需要判断
//        if (isEnemy) {//            g.drawImage(enemyImg[dir], x - RADIUS, y - RADIUS, null);
//        } else {//            g.drawImage(tankImg[dir], x - RADIUS, y - RADIUS, null);
//        }
//    }/*** 绘制坦克方法* 已弃用!!!!** @param g*/public void drawTank(Graphics g) {g.setColor(color);//绘制坦克的圆g.fillOval(x - RADIUS, y - RADIUS, RADIUS * 2, RADIUS * 2);//绘制炮管int endX = x;int endY = y;switch (dir) {case DIR_UP:endY = y - RADIUS * 2;break;case DIR_DOWN:endY = y + RADIUS * 2;break;case DIR_LEFT:endX = x - RADIUS * 2;;break;case DIR_RIGHT:endX = x + RADIUS * 2;break;}g.drawLine(x, y, endX, endY);}//坦克的逻辑处理private void logic() {switch (state) {case STATE_STAND:break;case STATE_MOVE://处于move状态时就调用move方法move();break;case STATE_DIE:break;}}//坦克移动的方法private void move() {//设置旧的坐标,用于坦克和墙碰撞oldX = x;oldY = y;switch (dir) {case DIR_UP:y -= speed;//0坐标是从左上角算的,所以要减去标题栏的高度if (y < RADIUS + GameFrame.titleBarH) {y = RADIUS + GameFrame.titleBarH;}break;case DIR_DOWN:y += speed;if (y > Constant.FRAME_HIGH - RADIUS) {y = Constant.FRAME_HIGH - RADIUS;}break;case DIR_LEFT:x -= speed;if (x < RADIUS) {x = RADIUS;}break;case DIR_RIGHT:x += speed;if (x > Constant.FRAME_WIDTH - RADIUS) {x = Constant.FRAME_WIDTH - RADIUS;}break;}}/*** 开火方法* 包括控制开火速度*/public void fire() {//坦克上一次开火和这一次开火的时间间隔指定时间后才能再次开火if (System.currentTimeMillis() - fireTime > FIRE_INTERVAL) {//计算炮弹出现坐标(炮口处坐标)int bulletX = x;int bulletY = y;switch (dir) {case DIR_UP:bulletY -= RADIUS;break;case DIR_DOWN:bulletY += RADIUS;break;case DIR_LEFT:bulletX -= RADIUS;break;case DIR_RIGHT:bulletX += RADIUS;break;}//从池塘中拿子弹Bullet bullet = BulletsPool.get();//设置参数bullet.setX(bulletX);bullet.setY(bulletY);bullet.setDir(dir);bullet.setAtk(atk);bullet.setColor(color);bullet.setVisible(true);//放到炮弹容器中bullets.add(bullet);//记录本次发射时间fireTime = System.currentTimeMillis();//播放开火音效MusicUtil.playBoom();}}//将坦克发射的炮弹绘制出来private void drawBullets(Graphics g) {for (Bullet bullet : bullets) {bullet.draw(g);}//遍历全部的子弹,将不可见的子弹移除,并还原到对象池for (int i = 0; i < bullets.size(); i++) {Bullet bullet = bullets.get(i);if (!bullet.isVisible()) {//移除时会返回移除的对象,用remove接收Bullet remove = bullets.remove(i);  //移除BulletsPool.theReturn(remove);      //还原i--;    //ArrayList删除元素后,当前位置后面的元素下标会变成被当前位置,如果没有i--那么就会少判断一个元素}}}//坦克销毁时,还回所有子弹并清空子弹容器public void bulletsReturn() {for (Bullet bullet : bullets) {BulletsPool.theReturn(bullet);}bullets.clear();}//坦克和敌人子弹的碰撞方法public void collideBullets(List<Bullet> bullets) {//遍历所有子弹,和当前的坦克进行碰撞检测for (Bullet bullet : bullets) {//获取子弹坐标int bulletX = bullet.getX();int bulletY = bullet.getY();//如果子弹和坦克发生了碰撞if (MyUtil.isCollide(x, y, RADIUS, bulletX, bulletY)) {//子弹消失bullet.setVisible(false);//坦克受到伤害hurt(bullet);//添加爆炸效果addExplode(bulletX, bulletY);}}}//添加爆炸效果的方法,传入子弹坐标private void addExplode(int bulletX, int bulletY) {//添加爆炸效果,坐标是子弹的坐标Explode explode = ExplodePool.get();explode.setX(bulletX);explode.setY(bulletY);explode.setIndex(0);    //重新设置从第0帧开始播放explode.setVisible(true);explodes.add(explode);}/*** 坦克减血方法** @param bullet 传入子弹,用于获取子弹的攻击力*/private void hurt(Bullet bullet) {final int atk = bullet.getAtk();hp = hp - atk;if (hp < 0) {hp = 0;die();}}/*** 坦克死亡方法*/private void die() {//判断是否为敌方坦克if (isEnemy) {//消灭的敌人加一GameFrame.killEnemyCount++;//绘制一个在坦克中间的爆炸效果addExplode(x, y);//坦克被消灭之后就归还对象池EnemyTanksPool.theReturn(this);//判断本关是否结束if (GameFrame.isCrossLevel()) {//判断是为最后一关if (GameFrame.isLastLevel()) {//如果是最后一关就设置为胜利状态GameFrame.setGameState(Constant.STATE_WIN);} else {//TODO 否则进入下一关GameFrame.nextLevel();}}} else {//            //玩家死亡若干毫秒后进入游戏结束画面
//            delaySecondsToOver(0);}}/*** 判断当前坦克是否死亡** @return*/public boolean isDie() {//只有坦克血量小于或等于0并且坦克身上的爆炸效果绘制完毕才视为死亡,解决了坦克死亡后立即消失的问题if (hp <= 0 && explodes.size() == 0) {return true;} else {return false;}}public boolean myTankisDie() {if (hp <= 0) {return true;} else {return false;}}/*** 绘制当前坦克上的爆炸效果,和砖块上的爆炸效果** @param g*/public void drawExplodes(Graphics g) {for (Explode explode : explodes) {explode.draw(g);}//遍历全部的爆炸效果,将不可见的爆炸效果移除,并还原到对象池for (int i = 0; i < explodes.size(); i++) {Explode explode = explodes.get(i);if (!explode.isVisible()) {//移除时会返回移除的对象,用remove接收Explode remove = explodes.remove(i);  //移除ExplodePool.theReturn(remove);      //还原i--;}}}/*** 子弹和所有地图块的碰撞** @param tiles 所有地图块所在的数组*/public void bulletsCollideMapTiles(List<MapTile> tiles) {for (MapTile tile : tiles) {if (tile.isCollideBullet(bullets)) {//添加爆炸效果,在方块中心addExplode(tile.getX() + MapTile.radius, tile.getY() + MapTile.radius);//如果是铁块的话就直接从这结束if (tile.getType() == MapTile.TYPE_HARD) {continue;}//设置地图块销毁tile.setVisible(false);//归还对象池MapTilePool.theReturn(tile);//当基地被击毁之后,若干毫秒切换到游戏结束的画面if (tile.isHouse()) {delaySecondsToOver(500);}}}}/*** 延迟若干毫秒进入死亡状态** @param millisSecond 延迟多少毫秒*/private void delaySecondsToOver(int millisSecond) {new Thread() {@Overridepublic void run() {try {Thread.sleep(millisSecond);} catch (InterruptedException e) {e.printStackTrace();}GameFrame.setGameState(Constant.STATE_OVER);}}.start();}/*** 地图块和当前坦克碰撞方法* 遮挡块没有碰撞效果* 从地图中提取8个点,如果坦克有任何一个点和这8个点发生了碰撞就说明坦克和点发生了碰撞* 8个点的顺序是从左上角开始顺时针一圈** @param tiles 所有地图块所在的集合* @return 碰到返回true否则返回false*/public boolean isCollideTile(List<MapTile> tiles) {for (MapTile tile : tiles) {//如果地图块不可见或者地图块类型为遮挡块就直接结束if (!tile.isVisible() || tile.getType() == MapTile.TYPE_COVER) {continue;}//第一个点,左上int tileX = tile.getX();int tileY = tile.getY();boolean collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第二个点,上中tileX += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第三个点,右上//因为上面已经加过半径了,所以这里只用再加一次就行tileX += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第四个点,右中tileY += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第五个点,右下tileY += MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第六个点,下中tileX -= MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第七个点,左下tileX -= MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第八个点,左中tileY -= MapTile.radius;collide = MyUtil.isCollide(x, y, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}}//如果都没有碰到就返回falsereturn false;}/*** 坦克和多个坦克发生碰撞的方法** @param tanks 传入坦克数组* @return 如果发生了碰撞返回true*/public boolean isCollideTanks(List<Tank> tanks) {for (Tank tank : tanks) {//第一个点,左上int tileX = tank.getX();int tileY = tank.getY();boolean collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第二个点,上中tileX += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第三个点,右上//因为上面已经加过半径了,所以这里只用再加一次就行tileX += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第四个点,右中tileY += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第五个点,右下tileY += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第六个点,下中tileX -= Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第七个点,左下tileX -= Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第八个点,左中tileY -= Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}}//如果都没有碰到就返回falsereturn false;}/*** 坦克和单个坦克碰撞的方法** @param tank 传入单个坦克* @return 如果发生了碰撞返回true*/public boolean isCollideTank(Tank tank) {//第一个点,左上int tileX = tank.getX();int tileY = tank.getY();boolean collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第二个点,上中tileX += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第三个点,右上//因为上面已经加过半径了,所以这里只用再加一次就行tileX += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第四个点,右中tileY += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第五个点,右下tileY += Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第六个点,下中tileX -= Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第七个点,左下tileX -= Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//第八个点,左中tileY -= Tank.RADIUS;collide = MyUtil.isCollide(x + 30, y + 30, RADIUS, tileX, tileY);//如果碰上了就直接返回,否则就判断下一个点if (collide) {return true;}//如果都没有碰到就返回falsereturn false;}/*** 坦克回退方法*/public void back() {//如果发生了碰撞那么坦克就回到上一步的坐标x = oldX;y = oldY;}/*** 血条类* 为了方便封装所以写成了内部类*/class BloodBar {//血条宽度public static final int BAR_LENGTH = 50;//血条高度public static final int BAR_HEIGHT = 5;//血条离坦克的距离public static final int BAR_DISTANCE = 15;public void draw(Graphics g) {//设置底色g.setColor(Color.CYAN);//绘制矩形,当做血条//因为要显示在坦克上方,所以才要 y-RADIUS-20g.fillRect(x - RADIUS, y - RADIUS - BAR_DISTANCE, BAR_LENGTH, BAR_HEIGHT);//设置血量颜色g.setColor(Color.RED);//当前血量乘血条长度除以默认血量就是当前血条长度g.fillRect(x - RADIUS, y - RADIUS - BAR_DISTANCE, hp * BAR_LENGTH / maxHp, BAR_HEIGHT);//设置边框颜色并且绘制边框g.setColor(Color.GREEN);g.drawRect(x - RADIUS, y - RADIUS - BAR_DISTANCE, BAR_LENGTH, BAR_HEIGHT);}}
}

BulletsPool类

在这里插入代码片package c02.n02.util;import c02.n02.game.Bullet;import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;/*** 子弹对象池类*/
public class BulletsPool {//设置默认的池大小可以容纳200个子弹对象public static final int DEFAULT_POOL_SIZE = 200;//设置池塘最多能放多少子弹对象public static final int POOL_MAX_SIZE = 300;//用于保存所有子弹的容器private static List<Bullet> pool = new ArrayList<>();private static HashSet<Bullet> poo=new HashSet<>();//在类加载的时候创建200个子弹对象static {for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {pool.add(new Bullet());}}/*** 从池塘中获取一个子弹对象** @return 返回一个子弹对象*/public static Bullet get() {Bullet bullet = null;if (pool.size() == 0) {//如果池塘被拿空了就新建一个子弹bullet = new Bullet();
//            System.out.println("池空");} else {//取出一个子弹,并且在池塘中删除bullet = pool.remove(0);
//            System.out.println("从池中取出子弹,池中还剩"+pool.size());}return bullet;}/*** 归还子弹** @param bullet 传入子弹对象*/public static void theReturn(Bullet bullet) {if (pool.size() == POOL_MAX_SIZE) {//池塘中的子弹个数到达了最大值那就不再归还
//            System.out.println("池满");return;} else {//            System.out.println("归还子弹");pool.add(bullet);}}}

Constant类

package c02.n02.util;import java.awt.*;/*** 游戏常量类,所有的常量都在此类中*/
public class Constant {/*=================START============游戏窗口相关============================*///标题public static final String GAME_TITLE = "坦克大战";//游戏窗口大小public static final int FRAME_WIDTH = 1280;public static final int FRAME_HIGH = 720;//动态获取系统的宽和高public static final int SCREEN_W = Toolkit.getDefaultToolkit().getScreenSize().width;public static final int SCREEN_H = Toolkit.getDefaultToolkit().getScreenSize().height;//左上角位置,把游戏窗口定位到屏幕中间public static final int FRAME_X = (SCREEN_W - FRAME_WIDTH) / 2;public static final int FRAME_Y = (SCREEN_H - FRAME_HIGH) / 2;/*=================END============游戏窗口相关============================*//*=================START============游戏菜单相关============================*///七个状态public static final int STATE_MENU = 0;     //菜单public static final int STATE_HELP = 1;     //帮助public static final int STATE_ABOUT = 2;    //关于public static final int STATE_RUN = 3;      //开始游戏public static final int STATE_OVER = 4;     //死亡public static final int STATE_WIN = 5;      //胜利public static final int STATE_LEVEL = 6;    //选择关卡public static final String[] MENUS = {"单人游戏","双人游戏","选择关卡","游戏帮助","游戏关于","退出游戏"};public static final String[] OVER_STR = {"ESC键退出游戏","ENTER键返回主菜单"};//字体public static final Font FONT = new Font("迷你简菱心", Font.PLAIN, 36);//小号状态public static final Font SMALL_FONT = new Font("黑体", Font.PLAIN, 14);/*=================END============游戏菜单相关============================*///刷新时间,毫秒public static final int REPAINT_INTERVAL = 30;//敌人坦克数量public static final int ENEMY_MAX_COUNT = 10;//敌人坦克产生间隔,毫秒public static final int ENEMY_BORN_INTERVAL = 2000;//坦克AI,敌人改变状态间隔public static final int ENEMY_AI_INTERVAL = 2000;//发射子弹概率public static final double ENEMY_FIRE_PERCENT = 0.05;}

EnemyTanksPool类

package c02.n02.util;import c02.n02.tank.EnemyTank;
import c02.n02.tank.Tank;import java.util.ArrayList;
import java.util.List;/*** 敌人坦克池*/
public class EnemyTanksPool {//设置默认的池大小可以容纳20个坦克对象public static final int DEFAULT_POOL_SIZE = 20;//设置池塘最多能放多少坦克对象public static final int POOL_MAX_SIZE = 30;//用于保存所有坦克的容器private static List<Tank> pool = new ArrayList<>();//在类加载的时候创建20个坦克对象static {for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {pool.add(new EnemyTank());}}/*** 从池塘中获取一个坦克对象** @return 返回一个坦克对象*/public static Tank get() {Tank tank = null;if (pool.size() == 0) {//如果池塘被拿空了就新建一个坦克tank = new EnemyTank();
//            System.out.println("池空");} else {//取出一个坦克,并且在池塘中删除tank = pool.remove(0);
//            System.out.println("从池中取出坦克,池中还剩"+pool.size());}return tank;}/*** 归还坦克** @param tank 传入坦克对象*/public static void theReturn(Tank tank) {if (pool.size() == POOL_MAX_SIZE) {//池塘中的坦克个数到达了最大值那就不再归还
//            System.out.println("池满");return;} else {//            System.out.println("归还坦克");pool.add(tank);}}
}

ExplodePool类

package c02.n02.util;import c02.n02.game.Explode;import java.util.ArrayList;
import java.util.List;/*** 爆炸效果对象池类*/
public class ExplodePool {//设置默认的池大小可以容纳20个爆炸对象public static final int DEFAULT_POOL_SIZE = 20;//设置池塘最多能放多少爆炸对象public static final int POOL_MAX_SIZE = 30;//用于保存所有爆炸效果的容器private static List<Explode> pool = new ArrayList<>();//在类加载的时候创建20个爆炸对象static {for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {pool.add(new Explode());}}/*** 从池塘中获取一个爆炸对象** @return 返回一个爆炸对象*/public static Explode get() {Explode explode = null;if (pool.size() == 0) {//如果池塘被拿空了就新建一个爆炸对象explode = new Explode();
//            System.out.println("池空");} else {//取出一个爆炸对象,并且在池塘中删除explode = pool.remove(0);
//            System.out.println("从池中取出爆炸效果,池中还剩"+pool.size());}return explode;}/*** 归还爆炸效果** @param explode 传入爆炸效果对象*/public static void theReturn(Explode explode) {if (pool.size() == POOL_MAX_SIZE) {//池塘中的爆炸对象到达了最大值那就不再归还
//            System.out.println("池满");return;} else {//            System.out.println("归还爆炸对象");pool.add(explode);}}}

MapTilePool类

package c02.n02.util;import c02.n02.map.MapTile;import java.util.ArrayList;
import java.util.List;/*** 砖块对象池*/
public class MapTilePool {//设置默认的池大小可以容纳100个砖块对象public static final int DEFAULT_POOL_SIZE = 100;//设置池塘最多能放多少砖块对象public static final int POOL_MAX_SIZE = 150;//用于保存所有砖块的容器private static List<MapTile> pool = new ArrayList<>();//在类加载的时候创建100个砖块对象static {for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {pool.add(new MapTile());}}/*** 从池塘中获取一个砖块对象** @return 返回一个砖块对象*/public static MapTile get() {MapTile tile = null;if (pool.size() == 0) {//如果池塘被拿空了就新建一个砖块tile = new MapTile();
//            System.out.println("池空");} else {//取出一个砖块,并且在池塘中删除tile = pool.remove(0);
//            System.out.println("从池中取出砖块,池中还剩"+pool.size());}return tile;}/*** 归还砖块** @param tile 传入砖块对象*/public static void theReturn(MapTile tile) {if (pool.size() == POOL_MAX_SIZE) {//池塘中的砖块个数到达了最大值那就不再归还
//            System.out.println("池满");return;} else {//            System.out.println("归还砖块,池中还剩"+pool.size());pool.add(tile);}}}

MusicUtil类

package c02.n02.util;import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import java.applet.Applet;
import java.applet.AudioClip;
import java.io.File;
import java.io.IOException;/*** 音乐类*/
public class MusicUtil {private static Clip start;private static AudioClip boom;private static AudioClip menuCase;//静态装载音乐资源//音乐资源来自红警2尤里的复仇//不知为何,大的音乐文件无法播放,大文件请使用底下的play方法static {//适用于小文件try {boom = Applet.newAudioClip(new File("music/boom.wav").toURL());    //加载爆炸的音乐menuCase = Applet.newAudioClip(new File("music/menuCase.wav").toURL());    //加载选择菜单时候的音乐} catch (IOException e) {//TODOSystem.out.println("音频接口异常");e.printStackTrace();}//适用于大文件try {start = AudioSystem.getClip();start.open(AudioSystem.getAudioInputStream(new File("music/F-777 - The 7 Seas.wav")));} catch (Exception exc) {exc.printStackTrace(System.out);}}/*** 播放开始音乐* 循环播放10次*/public static void playStart() {start.loop(10);}/*** 结束播放音乐*/public static void stopStart() {start.stop();}/*** 播放爆炸音乐*/public static void playBoom() {boom.play();}/*** 播放选择菜单音乐*/public static void playMenuCase() {menuCase.play();}}

MyUtil类

package c02.n02.util;import java.awt.*;/** 工具类* */
public class MyUtil {private MyUtil() {}/*** 得到指定区间内的随机数** @param min 区间最小值,包含* @param max 区间最大值* @return 随机数*/public static final int getRandomNumber(int min, int max) {return (int) (Math.random() * (max - min) + min);}/*** 返回随机颜色** @return 返回一个颜色*/public static final Color getRandomColor() {int r = getRandomNumber(0, 256);int g = getRandomNumber(0, 256);int b = getRandomNumber(0, 256);Color c = new Color(r, g, b);return c;}/*** 碰撞检测* 判断一个点是否在某一个正方形中** @param rectX  正方形中心点X* @param rectY  正方形中心点Y* @param radius 正方形的半径* @param pointX 点X* @param pointY 点Y* @return 点在正方形中返回true,否则返回false*/public static final boolean isCollide(int rectX, int rectY, int radius, int pointX, int pointY) {//正方形中心点和点的X轴Y轴的距离//因为可能为负数,所以要取绝对值int disX = Math.abs(rectX - pointX);int disY = Math.abs(rectY - pointY);//如果某个方向的距离小于正方形的半径就说明碰撞了if (disX < radius && disY < radius) {return true;} else {return false;}}/*** 根据图片的路径创建加载图片对象** @param path 图片路径* @return 返回对象*/public static final Image createImage(String path) {return Toolkit.getDefaultToolkit().createImage(path);}//形容词public static final String[] MODIFIY = {"一样", "喜欢", "美丽", "一定", "原来", "美好", "开心", "可能","可爱", "明白", "所有", "后来", "重要", "经常", "自然", "真正","害怕", "空中", "红色", "干净"};//名词public static final String[] NAMES = {"豹虎", "蜂猴", "熊猴", "叶猴", "紫貂", "貂熊", "熊狸", "云豹","雪豹", "儒艮", "黑麂", "野马", "鼷鹿", "坡鹿", "豚鹿", "麋鹿","野牛", "藏羚", "河狸", "蝎子"};/*** 返回一个随机的名字** @return 返回一个名字*/public static final String getRandomName() {String s = MODIFIY[getRandomNumber(0, MODIFIY.length)] + "的" + NAMES[getRandomNumber(0, NAMES.length)];return s;}
}

JAVA课程设计坦克大战源码相关推荐

  1. Java课程设计——坦克大战

    坦克大战--坦克类 一. 团队课程设计博客链接 https://www.cnblogs.com/chenhuilin/p/10275664.html 二.个人负责模块和任务说明 模块:坦克类(玩家坦克 ...

  2. java课程设计抽奖程序源码_java课程设计---个人博客 彩票抽奖程序 201821123098 钟海清...

    1.团队课程设计博客链接 2.个人负责模块或任务说明 我主要负责主要负责管理员类,实现滚动抽奖.自动注册购买彩票以及查看中奖信息和用户信息的方法 3.自己的代码提交记录截图 4.自己负责模块或任务详细 ...

  3. java万年历设计报告_JAVA《万年历系统》课程设计报告附源码.doc

    JAVA<万年历系统>课程设计报告附源码 学号<> 课程设计报告 万年历系统专业:计算机科学与技术班级:姓名:学号:指导教师:成绩: 计算机与信息工程系 2014年6月6日目录 ...

  4. 万年历java课程设计报告_java《万年历系统》课程设计报告附源码.doc

    java<万年历系统>课程设计报告附源码.doc 学号<面向对象程序设计>课程设计报告题目:万年历系统专业:计算机科学与技术班级:姓名:学号:指导教师:成绩:计算机与信息工程系 ...

  5. 基于JAVA游戏论坛设计计算机毕业设计源码+系统+数据库+lw文档+部署

    基于JAVA游戏论坛设计计算机毕业设计源码+系统+数据库+lw文档+部署 基于JAVA游戏论坛设计计算机毕业设计源码+系统+数据库+lw文档+部署 本源码技术栈: 项目架构:B/S架构 开发语言:Ja ...

  6. java坦克大战互相碰撞_Java课程设计——坦克大战

    坦克大战--坦克类 一. 团队课程设计博客链接 二.个人负责模块和任务说明 模块:坦克类(玩家坦克类+电脑坦克类),代码整合 三.代码的提交记录截图 四.负责模块和任务详细说明 玩家坦克继承Visib ...

  7. java坦克大战总体功能设计_java课程设计——坦克大战

    ##一.团队课程设计博客链接: ##二.个人负责模块或任务说明: ###模块:GUI设计(游戏面板,登录面板,主窗体,显示关卡面板) ##三.自己的代码提交记录截图 ##四.自己负责模块或任务详细说明 ...

  8. MVC 停车场管理系统java jsp 程序设计 课程设计 毕业设计-附源码02141

    因上传问题  只上传了文案 图片未上传  点赞+收藏+关注  →  私信领取本源代码.数据库 摘  要 如今,我国现代化发展迅速,人口比例急剧上升,在一些大型的商场,显得就格外拥挤,私家车的数量越来越 ...

  9. 面向对象程序设计(Java)课程设计--坦克大战

    目录 一.项目简介 1.功能描述 二.团队成员负责模块 三.功能架构图 四.项目亮点 五.功能需求分析 六.系统演示操作图片 七.项目git地址及团队成员git提交记录截图 一.项目简介 1.功能描述 ...

  10. MVC 网上招聘系统的设计与实现java jsp 程序设计 课程设计 毕业设计-附源码02135

    因上传问题  只上传了文案 图片未上传 网上招聘系统的设计与实现 摘  要 随着时代的发展,中国的互联网技术愈加成熟,已经有越来越多的社会群体开始学会使用互联网技术,整个社会正在朝着智能化.信息化的方 ...

最新文章

  1. docker上传自己的镜像
  2. 7-26晚上实现mystring
  3. 网页中加载obj模型比较慢_Web前端优化技巧分享,让你的网页显示的更流畅
  4. kmeans算法_实战 | KMeans 聚类算法
  5. Android学习之PopupWindow
  6. 利用卷积神经网络实现人脸识别(python+TensorFlow)
  7. php pdo fetchassoc,pdo执行fetch查询语句,出现500错误,请问应该怎么写
  8. Android—修改button属性
  9. 20181114 Redis
  10. java金字塔显示_java控制台输出数字金字塔示例分享
  11. Testing a React+Redux web application
  12. 工资计算问题,类似计算某天是一年中的第几天的问题
  13. java list 树_java list转换为树形
  14. [BZOJ4810][Ynoi2017]由乃的玉米田 莫队+bitset
  15. Java编程:栈的应用实例——逆波兰计算器
  16. 基于MCS-51单片机的数字时钟设计
  17. vscode git提交步骤
  18. 三分钟破解奇迹热门外挂
  19. 第九届蓝桥杯单片机省赛试题
  20. cad文字递增快捷键_cad文字修改快捷键是什么,Auto CAD文字修改快捷键是什么?

热门文章

  1. 车载多媒体没法显示歌词的解决办法 —— 修改 LRC 文件的编码格式为 UTF-8
  2. java程序员待遇怎么样_上海海文告诉你Java程序员工资待遇到底如何
  3. win10中常用快捷键 (包括切换窗口、打开我的电脑等快捷键)
  4. 手机市场的竞争,用户价值才是硬道理
  5. JAVA_OPTS(JVM相关运行参数的变量)设置
  6. 新一配:如何对电脑配置进行评判【转载】
  7. C++读取图片二进制数据并保存
  8. 分位数回归及Stata实现
  9. 算法高级(21)-如何通过IP地址进行定位?
  10. Linux共享后无权限访问,Samba服务无权限访问