一、项目介绍

1、前言

基于韩顺平老师坦克大战的框架和思路,进行了一些优化。编码上尽量按照阿里的代码规约;有非常详尽的注释;引入了线程池,线程安全集合类,原子类等;通过这个小项目的学习,可以深入地理解面向对象、集合、IO、多线程等,特别是多线程JUC,是整个项目的一个灵魂,所有业务都围绕着它。游戏经过测试,高峰时线程100多个,运行流畅。 感谢韩顺平老师的无私奉献!大家可以在B站搜索老师的视频,结合视频做项目,事半功倍!

2、涉及知识点

Java (JDK1.8)

编码:UTF-8

GUI、面向对象、集合、IO、多线程、原子类...

不涉及数据库

3、关键代码

4、运行数据

二、主要优化方面

1、系统常量

定义单独的类存放系统常量 Constant.java,放置在 constant 包下

public class Constant {// 游戏界面宽度public static final int WINDOW_WIDTH = 1200;// 游戏界面高度public static final int WINDOW_HEIGHT = 900;// 界面顶部标题栏高度public static final int WINDOW_TITLE_HEIGHT = 40;// 信息栏高度public static  final int INFO_BAR_HEIGHT = 100;...
}

2、用线程池管理线程

// 我方坦克生成线程池
private ThreadPoolExecutor myTankThreadPool;/**
* 初始化
*/
private void init() {... myTankThreadPool = new ThreadPoolExecutor(1, // 核心线程数1, // 最大线程数200, // 空闲时间TimeUnit.MILLISECONDS, // 时间单位new ArrayBlockingQueue<>(5), // 阻塞队列new MyThreadFactory("我方坦克"), // 线程工厂new ThreadPoolExecutor.DiscardOldestPolicy()); // 拒绝策略...
}

3、采用线程安全集合类

// 我方坦克集合
private volatile List<herotank> myTanks = new CopyOnWriteArrayList<>();
// 敌方坦克集合
private volatile List<enemytank> enemyTanks = new CopyOnWriteArrayList<>();

4、控制坦克发射频率

在坦克中定义一个布尔变量:boolean allowShot,表示是否可以发射;

同时,定义一个常量:发射的最小时间间隔;

在每次发射时,检查状态,是否可以发射;如是可以发射,就开始发射,发射完之后,把状态设为不可发射;然后开一个新线程,按发射的最小时间间隔来计时,结束后把状态设置可以发射

5、敌方自动追击我方

// 改变方向,追击目标坦克
if (myTanks.size() > 0 && myTanks.get(0) != null && myTanks.get(0).isLive()) {Tank myTank = myTanks.get(0);// 开启新线程,改变方向getShotIntervalThreadPool().execute(() -> {try {// 随机休眠ThreadLocalRandom random = ThreadLocalRandom.current();int t = 1000 + 1000 * random.nextInt(10);TimeUnit.MILLISECONDS.sleep(t);// 判断我方坦克位置,从而改变方向// x 轴距离int xDistance = myTank.getX() - getX() + Constant.TANK_WHEEL_HEIGHT;// y 轴距离int yDistance = myTank.getY() - getY() + Constant.TANK_WHEEL_HEIGHT;// 改变方向if (Math.abs(xDistance) < Math.abs(yDistance)) {// 纵向改变方向if (yDistance >= 0) {// 向下setDirection(Constant.DOWN);} else {// 向上setDirection(Constant.UP);}} else {// 横向改变方向if (xDistance >= 0) {// 向右setDirection(Constant.RIGHT);} else {// 向左setDirection(Constant.LEFT);}}} catch (InterruptedException interruptedException) {interruptedException.printStackTrace();}});
}

6、避免坦克重叠

思路:

在 Tank 父类中增加 allTanks 属性,再增加判断重叠的方法 isTouch(),每次移动时,先判断是否有重叠;如果有重叠,本次不移动;

在 面板类 MyPanel 类有 Tank 属性,可以计算坦克总数,而坦克类不设置 MyPanel 属性;因为面板只有一个,而坦克有多个;所以从 Tank 类中无法直接获取面板类的坦克总数,但坦克类中必须得到所有坦克,才能判断是否与其它坦克发生碰撞,怎么办?想得到而自已又没有能力获取到?

换种思路:可以让别人主动赠予!

让 MyPanel 给 Tank 中的 allTanks 赋值。坦克中只要有个接收方法即可:setAllTanks()

MyPanel 在坦克数量发生变化后 (初始化或者被摧毁),就给所有坦克赋一次值;

  • 具体实现

坦克每次运动时,先判断与其它坦克是否有重叠

/**
* 上移
*/
public void moveUp() {// 判断是否有重叠if (isTouch()) {// 重叠时自动转向direction = y % 2 == 0 ? Constant.DOWN : Constant.RIGHT;} else {y -= Constant.TANK_SPEED;// 到达边界时顺时针转向if (y < Constant.INFO_BAR_HEIGHT) {y = Constant.INFO_BAR_HEIGHT;direction = Constant.RIGHT;}}
}

isTouch() 方法

判断 当前坦克this 是否与其它坦克发生碰撞

方法中有大量重复代码,把重复代码提取成单独方法:

isTouchCurrentTankAndOtherTank(this, p1, p2) :判断当前坦克的两点与其它坦克是否相撞

/*** 判断 当前坦克this 是否与其它坦克发生碰撞** @return*/
private boolean isTouch() {// 坦克数量大于1时再判断。至少两个坦克才可能发生碰撞if (allTanks.size() > 1) {// 当前坦克的前方一点int x1,y1;Point p1;// 当前坦克的前方另一点int x2,y2;Point p2;// 根据当前坦克的方向分类判断switch (getDirection()) {/*向上判断当前坦克的 上面左右任一端 是否进入另一辆坦克的区域*/case Constant.UP:// 当前坦克上方左侧x1 = this.getX();y1 = this.getY();p1 = new Point(x1, y1);// 当前坦克上方右侧x2 = x1 + 2 * Constant.TANK_WHEEL_WIDTH + Constant.TANK_BODY_WIDTH;y2 = y1;p2 = new Point(x2, y2);// 判断当前坦克与其它坦克是否相撞if(isTouchCurrentTankAndOtherTank(this, p1, p2)){return true;}break;/*向右判断当前坦克的 右面上下任一端 是否进入另一辆坦克的区域*/case Constant.RIGHT:// 当前坦克右侧上方x1 = this.getX() + 2 * Constant.TANK_WHEEL_WIDTH + Constant.TANK_BODY_WIDTH;y1 = this.getY();p1 = new Point(x1, y1);// 当前坦克右侧下方x2 = x1;y2 = y1 + 2 * Constant.TANK_WHEEL_WIDTH + Constant.TANK_BODY_WIDTH;p2 = new Point(x2, y2);// 判断当前坦克与其它坦克是否相撞if(isTouchCurrentTankAndOtherTank(this, p1, p2)){return true;}break;/*向下判断当前坦克的 下面左右任一端 是否进入另一辆坦克的区域*/case Constant.DOWN:// 当前坦克下方左侧x1 = this.getX();y1 = this.getY() + Constant.TANK_WHEEL_HEIGHT;p1 = new Point(x1, y1);// 当前坦克下方右侧x2 = x1 + 2 * Constant.TANK_WHEEL_WIDTH + Constant.TANK_BODY_WIDTH;y2 = y1;p2 = new Point(x2, y2);// 判断当前坦克与其它坦克是否相撞if(isTouchCurrentTankAndOtherTank(this, p1, p2)){return true;}break;/*向左判断当前坦克的 左面上下任一端 是否进入另一辆坦克的区域*/default:// 当前坦克左侧上方x1 = this.getX();y1 = this.getY();p1 = new Point(x1, y1);// 当前坦克左侧下方x2 = x1;y2 = y1 + 2 * Constant.TANK_WHEEL_WIDTH + Constant.TANK_BODY_WIDTH;p2 = new Point(x2, y2);// 判断当前坦克与其它坦克是否相撞if(isTouchCurrentTankAndOtherTank(this, p1, p2)){return true;}}}return false;
}

判断当前坦克的两点与其它坦克是否相撞

isTouchCurrentTankAndOtherTank(this, p1, p2)

引用其它方法:

getBoundsByTank(tank) 得到 tank 的区域面

isPointInBounds(p1, b) 判断 p1 是否与 目标坦克区域面 b 重叠

/**
* 判断当前坦克与其它坦克是否相撞
* @param currentTank
* @param p1 当前坦克前方一点
* @param p2 当前坦克前方另一点
* @return
*/
private boolean isTouchCurrentTankAndOtherTank(Tank currentTank,Point p1,Point p2){// 目标坦克区域面Bounds b;// 遍历坦克集合。比较与其它坦克是否发生碰撞for (Tank tank : allTanks) {// 避免与自已比较if (!currentTank.equals(tank)) {// 得到 tank 的区域面b = getBoundsByTank(tank);// 判断当前坦克的 上左点p1 或 上右点p2 是否与 目标坦克区域面b 重叠if (isPointInBounds(p1, b) || isPointInBounds(p2, b)) {return true;}}}return false;
}

得到 tank 的区域面

getBoundsByTank(tank)

由于面是一个规则的矩形,由 左上(xy)、右上(x)、右下(y) 三个点即可确定位置

/**
* 获取坦克的区域面
* @param tank
* @return
*/
private Bounds getBoundsByTank(Tank tank){// 目标坦克的上左int op1_x = tank.getX();int op1_y = tank.getY();Point op1 = new Point(op1_x, op1_y);// 目标坦克的上右xint op2_x;// 目标坦克的下右yint op3_y;// 目标坦克方向int d = tank.getDirection();if (d == Constant.UP || d == Constant.DOWN) { // 上下移动:计算方法一致// 目标坦克的上右xop2_x = op1_x + 2 * Constant.TANK_WHEEL_WIDTH + Constant.TANK_BODY_WIDTH;// 目标坦克的下右yop3_y = op1_y + Constant.TANK_WHEEL_HEIGHT;} else { // 左右移动:计算方法一致// 目标坦克的上右xop2_x = op1_x + Constant.TANK_WHEEL_HEIGHT;// 目标坦克的下右yop3_y = op1_y + 2 * Constant.TANK_WHEEL_WIDTH + Constant.TANK_BODY_WIDTH;}// 目标坦克的区域面return new Bounds(op1, op2_x, op3_y);
}

判断 p1 是否与 目标坦克区域面 b 重叠

isPointInBounds(p1, b)

/*** 判断点 p 是否在面 b 内部** @param p* @param b* @return*/
private boolean isPointInBounds(Point p, Bounds b) {/*原理:用点 p ,分别与面 b 的四个值比较同时满足以下三个条件即表示点在面内,返回 true1) p 在面左上角的右下方2) p 在面右上角的左下方3) p 在面右下角的左上方*//*p 与 b 的左上角比较如果不返回 false,说明 p 在 矩形左上角 的右下方●---------------|      ||      ||      ||      |--------||*/if (p.x < b.p1.x || p.y < b.p1.y) {return false;}/*p 与 b 的右上角 x 比较如果不返回 false ,说明 p 不在矩形的右方-------●|      ||      ||      ||      |--------|      ||      |*/if (p.x > b.p2x) {return false;}/*p 与 b 的右下角 y 比较如果不返回 false ,说明 p 不在矩形的下方,可以确定 p 在 b 中(包括边界)--------|      ||      ||      ||      |-------●*/if (p.y > b.p3y) {return false;}return true;
}

内部类:点、面

/*** 内部类:点*/
class Point {int x;int y;public Point(int x, int y) {this.x = x;this.y = y;}
}/*** 内部类:面* 由于面是一个规则的矩形,由 左上(xy)、右上(x)、右下(y) 三个点即可确定位置* 右上点的 y 和 左上点的 y 相等* 右下点的 x 和 右上点的 x 相等* 左下点的 x 和 左上点的 x 相等* 左下点的 y 和 右下点的 y 相等*/
class Bounds {// 左上Tank.Point p1;// 右上xint p2x;// 右下yint p3y;public Bounds(Tank.Point p1, int p2x, int p3y) {this.p1 = p1;this.p2x = p2x;this.p3y = p3y;}
}

7、生成一组不重复随机整数

实现原理:

利用 Set 集合不能存放重复元素的特性

用于初始化坦克的位置

package util;import java.util.HashSet;
import java.util.Random;
import java.util.Set;/*** @author ajun* Date 2021/7/14* @version 1.0* 工具类*/
public class MyUitls {public static void main(String[] args) {Set<integer> set = getRandoms(-30, -20, -5);System.out.println("数量:" + set.size());for (Integer s : set) {System.out.println(s);}}/*** 生成一组不重复随机数** @param start 开始位置:可以为负数* @param end   结束位置:end > start* @param count 数量 >= 0* @return*/public static Set<integer> getRandoms(int start, int end, int count) {// 参数有效性检查if (start > end || count < 1) {count = 0;}// 结束值 与 开始值 的差小于 总数量if ((end - start) < count) {count = (end - start) > 0 ? (end - start) : 0;}// 定义存放集合Set<integer> set = new HashSet<>(count);if (count > 0) {Random r = new Random();// 一直生成足够数量后再停止while (set.size() < count) {set.add(start + r.nextInt(end - start));}}return set;}
}
//结果
数量:5
-25
-26
-27
-30
-23

8、统计数量时采用原子类

每一辆坦克、每一发炮弹都是一个线程,如果有两发炮弹同时摧毁两辆坦克,要想统计结果准确,就要保证线程安全性,这时采用原子类,效率高

// 摧毁敌方坦克数量
public volatile static AtomicInteger destroyEnemyTankNum = new AtomicInteger(0);
// 我方战毁数量
public volatile static AtomicInteger destroyMyNum = new AtomicInteger(0);
// 我方发射子弹数量
public volatile static AtomicInteger myBulletNum = new AtomicInteger(0);// 摧毁敌方坦克数量加1
public static void destroyEnemyTankNumAdd() {destroyEnemyTankNum.getAndIncrement();
}

Gitee:韩顺平老师坦克大战优化版

韩顺平老师坦克大战优化版相关推荐

  1. 韩顺平老师坦克大战项目总结

    韩顺平老师讲的坦克大战项目,用代码进行了复现,有几个自己的总结 1 有个别功能没有实现,EnemyTank中敌人坦克向四周移动功能没有实现,只是实现了随机转向,但一直停在原地不动,没有找到bug所在. ...

  2. 韩顺平 java坦克大战_坦克大战完整版(韩顺平java)

    [实例简介] 坦克大战完整源代码(韩顺平java视频配套) [实例截图] [核心代码] 5i86q5 └── 源码 └── Class14 ├── 111.wav ├── bin │   ├── bo ...

  3. 韩顺平 java 坦克大战_HTML5坦克大战(韩顺平版本)

    1 //定义敌人和我们自己的坦克的颜色 2 var enemyColor = new Array("#0BB","#0FF");3 var heroColor ...

  4. Java坦克大战 跟学韩顺平老师视频开发

    这里写目录标题 TankBigWarGame 介绍 界面展示 项目架构 安装教程 游戏说明 项目涉及技术功能 游戏结束判断 项目不足与优化空间 相关代码展示 主方法Main 绘图界面 MyPanelF ...

  5. Java集合深入剖析【韩顺平老师版】

    Java集合知识体系[思维导图] 1.集合体系 1.1.为什么使用集合? 1.数组的不足 长度固定,不能更改 存储的必须是同一类型的元素(基本类型或引用类型) 增加.删除元素比较麻烦 2.集合的优势 ...

  6. 韩顺平老师讲诉如何学习PHP

    有很多网友发来邮件询问各种问题,有深有浅, 有难有易.因为很多时间需要上课,没有一一回答,这里给大家道个歉,这里我举例出了几封网友的来信: 发件人:Chen Ma 发送时间: 2012-09-18 1 ...

  7. Unity3D_(游戏)2D坦克大战 像素版

    2D坦克大战    像素版 游戏规则: 玩家通过上.下.左.右移动坦克,空格键发射子弹 敌人AI出身时朝向己方大本营(未防止游戏快速结束,心脏上方三个单位障碍物设为刚体) 当玩家被击杀次数>=3 ...

  8. Java基础易忘重点内容笔记【附B站韩顺平老师课程链接】

    B站课程链接:https://www.bilibili.com/video/BV1fh411y7R8?spm_id_from=333.999.0.0 1. 文档注释 用于对Java方法的注释,可据此生 ...

  9. 优秀课程案例:使用Scratch制作坦克大战增强版!

    点击上面微信号关注我关注我哟每天坚持推送文章,争取做到日更,喜欢的可以设置星标,并分享点赞我们的文章,非常感谢大家的支持,您的点击的在看就是我们的动力! 今天我们来分享一个坦克大战增强版.同时大家持续 ...

最新文章

  1. python 目录下的__init__.py
  2. 算法入门书籍--算法图解
  3. python3安装第三方模块
  4. git clone远程仓库时,输错密码如何解决?
  5. Linux—Centos 7.x安装Tomcat8
  6. 如何用指针判定系统是32bit还是64bit?一次惨痛的经历,想想都脸红
  7. Vue,简洁方式开发web,仅添加vue.js文件,不使用全家桶
  8. Spring笔记001 Spring容器的基本实现—— Spring的结构组成
  9. 一位程序员和他的程序员老婆分手了,原因竟是…
  10. 为什么在 Windows 7系统下无法显示 STEP 7 MicroWin SP9的帮助文件?
  11. 机器学习-累计分布函数(CDF)
  12. postman常用断言
  13. 守望先锋地图英文和英雄英文
  14. AI论文中 ACKNOWLEDGMENT的写法
  15. Timer定时器用法详解
  16. 【TortoiseSVN】配置比较工具为Beyond Compare
  17. 揭秘考研英语阅读理解技巧(何凯文)
  18. 实战-H3C交换机之Web界面
  19. idea的excluded
  20. android ims注册流程,ims注册流程volte通话流程总结

热门文章

  1. sql语句计算缺失率
  2. PHP 数组合并,将二维或多维数组合并为 一维数组
  3. Windows程序设计-通用对话框
  4. Tortoisegit日常操作步骤
  5. LTE RRC 连接重建
  6. 咸鱼Maya笔记—路径动画
  7. Ubuntu16 单机 Hadoop3.2.2 Hbase 2.3.5
  8. DM365高低温下偶尔无法启动问题记录:
  9. Python(面向对象5——高级)
  10. 在模拟器上安装APK文件的方法