JAVA根底小我的项目 - 坦克大战
前言:
这个我的项目是之前备份电脑材料的时候看到的,不禁一阵感叹本人当初自学编程的心酸和泪水。所以分享一下本人当初写的的垃圾代码。尽管我不是任天堂忠诚粉丝,然而对于90起初说坦克大战根本是人人都玩过的一款小霸王游戏机的游戏。
这个我的项目对于曾经入行的人来说没有价值,分享进去次要是心愿对于初学编程的人给一点 “吸引”吧,原来代码能够做到这么欢快的事件。这个坦克大战也是本人跟着培训机构的教学视频边看边敲的。
花了一天左右的工夫把代码略微整顿了一下代码同时写了这份文档,这里分享进去。
对于入行编程的同学集体的倡议如果要疾速成长还是多练,多做。很多货色做多了之后,波及本人的盲区会促使你一直的学习和提高。
PS:代码应用Eclipse写的,这里用IDEA整顿了一下代码此代码如果浏览存在难度,倡议看一下韩顺平的JAVA根底坦克大战的课程。这里的源代码也是跟着课程敲的。
https://www.bilibili.com/vide...
真是一个好时代,当初这些资源还要网上翻半天
<!-- more -->
我的项目地址
https://github.com/lazyTimes/...
后续文档更新请看readne
我的项目简介:
集体前几年自学的时候从一个教学视频的,韩顺平老师的JAVA高级坦克大战。集体跟着视频边学边敲之后,对于编程的趣味大大的晋升。前面越学越高兴。
所用技术
- JAVA GUI(远古技术,千万别深刻,看看即可)
- JAVA
面向群体:
- 初学JAVA者,能够看看这个我的项目锤炼入手能力
- 齐全不理解什么是面向对象
- 对于小游戏有点趣味的
- 如果你讨厌干燥的学习,做一个小游戏或者能给你一点能源
我的项目截图:
操作坦克的办法:
最初一版本无效,早起版本局部性能或者所有性能按键有效
- WASD
- J:为射出子弹
需要文档(或者是):
因为当初是跟着视频做的,尽管具体的记忆忘了,然而本人跟着敲的同时忘了做需要的更新,所以这里有局部需要和思路断了。如果有不同的,后续补充GIT的README文档。
/* * 需要: * 坦克大战: * 性能: * 1.画出坦克, * * 思路: * 1.首先坦克设想由五个部件组成两个矩形,一个长方形或者正方形,一个圆 * 一条直线 * * 2.画坦克的时候须要应用到画笔工具 * 必须在构造函数初始化应用画笔工具 * * 3.在设置方向以及画出不同方向的坦克 * * 4.敌方坦克画进去须要应用父类办法 * 敌方坦克的坐标须要设置, * 应用一个汇合保留敌方坦克Vector汇合便于删除和增加 * * 5.发射子弹是一个线程 * 具备线程的性能 * 另外线程对与子弹方向静止轨迹不同 * * 6.须要把子弹画进去 * 在按下J键的时候发射子弹 * 实现连发应用汇合存储 * * 降级: * 1.让敌人可能发射子弹 解决办法 1.敌人发射子弹是一个多线程办法,该当在敌人的run函数当中实现 2.坦克发射子弹和挪动都是坦克自身具备的性能 * * 思路: * 1.在敌人类外面须要增加一个射击办法 * 与我方一样,然而敌人是主动射击或者说每过几秒射击一次 * * 2.我方坦克子弹连发 * 应用一个汇合保留建设的对象,画出子弹应用汇合中的对象 * 我方坦克子弹连发过快,须要限定 * * 3. * 我方坦克击中敌人坦克之后,敌人坦克就要隐没 * 须要获取到敌人的一个定点坐标,而后界定一个范畴 * 写一个专门的函数判断是否击中敌人 * * 在哪里判断是否击中敌人 * · 因为每一颗子弹都要与所有的坦克匹配,并且每一次匹配都要 * 双重判断每次都要进行建设对象 * 图片问题没有失去解决 * * 降级 * 1.须要实现敌人的坦克一直的挪动应用多线程的伎俩实现 * * 2.须要实现敌人可能发射子弹的性能 * 实现办法: * 建设一个敌人的子弹汇合 * 如何敌人何时发射子弹? * 应用多重循环判断是否须要增加薪子弹 * * 3.实现本人被子弹击中也会隐没 * 对于捣毁坦克进行降级 * * 4. * 较难! * 实现坦克不笼罩静止, * 1.首先改判断在坦克类中实现 * 2.须要用到一个办法获取到生成的坦克类 * 3.对于中央其中一辆坦克的抉择,都要循环与其余所有坦克进行比对 * 并且要当时判断是否为我方坦克 * 4.**对于点位的判断要判断两个点,才可能保障不会产生碰撞 * * 5.实现抉择关卡的性能 * 思路: * 1.能够建设一个抉择关卡的面板 * 2.临时先实现不同的关卡敌人坦克的数量不同 * 3.实现闪动性能,应用多线程的办法,留神线程的敞开 * 4.对于选项增加事件属性,增加事件 * * 5.画出我方坦克击中了多少辆中央坦克 * 1.对于总体界面进行批改 * 2.显示敌人坦克的数量 * 扩大: * 1.建设帮忙文档 * 3.扩大:我方坦克的生命值,当生命值为0的时候游戏完结 * 4.记录我方击中了多少中央坦克 * 应用文件操作实现 * * 6.实现从新开始的性能 * * 7.实现存盘退出的性能 * 思路: * 选在主界面减少两个按钮 * 1.记录所有坦克的坐标 * * 8.实现暂停的性能 * 思路: * 暂停性能能够通过一个布尔值进行判断,当按下某个按钮的时候就要进行布尔值的扭转 * 须要暂停的对象 * 将多线程子弹的速度后退性能暂停 * 敌人坦克无奈转向和后退 * 我方坦克无奈转向和后退 * * 9.实现播放音乐的性能 * 自学 - 未实现 * * * * * */
版本迭代和介绍:
介绍:
代码比拟多,我会抽几处了解起来比拟难以了解的中央阐明一下,其余的代码须要看细节。如果有不懂的欢送在issue提出,集体只有有空肯定给予回答。
第一个版本
版本概述:画出坦克(version1)
咱们的第一步是画出咱们的坦克,画出我方坦克的办法还是非常简单的。
* 思路: * 1.首先坦克设想由五个部件组成两个矩形,一个长方形或者正方形,一个圆 * 一条直线 * * 2.画坦克的时候须要应用到画笔工具 * 必须在构造函数初始化应用画笔工具 * * 3.在设置方向以及画出不同方向的坦克
领有坦克的第一步是画出坦克
画出坦克的外围代码如下:
/** * 画坦克须要提取封装 * 1.画进去之前先确定色彩,是敌人坦克还是我方坦克 * 2.参数为坐标做,画笔(重要),以及坦克类型和方向 */ private void paintMyTank(int x, int y, Graphics g, int direct, String type) { //画之前先确定坦克的色彩 switch (type) { case "mytank": { g.setColor(Color.red); break; } case "enemytank": { g.setColor(Color.cyan); break; } } //向上 if (direct == 0) {//先画出我的坦克 //画出右边的矩形,先设置色彩 g.fill3DRect(x, x, 5, 30, false); //画出两头的长方形 g.fill3DRect(x + 5, x + 5, 10, 20, false); //画出两头圆圈,应用填充椭圆 g.fillOval(x + 6, x + 9, 7, 7); //画出一条直线 g.drawLine(x + 10, x, x + 10, x + 15); //画出另一边矩形 g.fill3DRect(x + 15, x, 5, 30, false); } }
如果晓得数字的意思,间接将数字批改大小就能够晓得成果了
第二个版本
版本概述:画出我方坦克不同形态,敌方坦克(version2),我方坦克能够进行口头
在上个版本当中,咱们发现咱们的坦克只有一个朝向,在这个版本中,减少了坦克的不同朝向。同时减少了敌人的坦克类。
因为敌人有很多个,所以用了一个汇合来保护和设置。同时退出了坐标零碎,能够实现不同的坦克挪到不同的地位。
这个版本的要害代码,不是在画坦克的下面,而是在于退出了键盘的监听事件:
// version2.DrawTank.java 更多细节请查看public class DrawTank extends JPanel implements KeyListener { // 省略一大坨代码 /** * 应用wsad进行管制 * 也能够改为上下左右键 * * @param e */ @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_W) { this.mytank.setDirect(0); this.mytank.move_up(); } else if (e.getKeyCode() == KeyEvent.VK_D) { this.mytank.setDirect(1); this.mytank.move_right(); } else if (e.getKeyCode() == KeyEvent.VK_S) { this.mytank.setDirect(2); this.mytank.move_down(); } else if (e.getKeyCode() == KeyEvent.VK_A) { //改变方向 this.mytank.setDirect(3); this.mytank.move_left(); } }}
实现KeyListener
接口并且监听对应的办法。JAVA的GUI有一个事件监听驱动模型,意思就是说咱们实现对应的驱动接口,并且笼罩对应的办法,在代码运行并且触发相干事件的适宜,模型就能够触发咱们实现定义好的代码,这里很显著就是设计模式,有趣味能够去理解一下
第三个版本
从这个版本就开始变得略微简单一点了,用了多线程的内容,因为要让咱们的坦克和敌人的坦克“动”起来,其实让坦克挪动和我方坦克挪动的情理都是一样的:高速的擦写和描述。和咱们的鼠标以及计算机显示画面的实质都是一样的。
这个版本中,比拟外围的内容是如何发射子弹和让子弹隐没:
public class Bullet implements Runnable { /** * 定义子弹的xy坐标 */ private int x, y; /** * 子弹的色彩 */ int color; /** * 子弹的方向 */ int direct; /** * 子弹挪动速度 */ int screed; /** * 判断是否越界 */ boolean isOut = false; /** * 越界范畴 */ int outx = 400; /** * 越界范畴 */ int outy = 300; public Bullet(int x, int y, int direct) { this.x = x; this.y = y; this.direct = direct; this.screed = 1; } // 省略get/set @Override public void run() { //坦克一旦建设就要静止 //因为挪动的太快,须要减慢速度 while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { // e.printStackTrace(); } switch (this.direct) { case 0: y -= screed; break; case 1: x += screed; break; case 2: y += screed; break; case 3: x -= screed; break; } System.out.println(x + "..." + y); //碰到边缘隐没 if (x < 0 || x > outx || y < 0 || y > outy) { isOut = true; break; } } // 子弹什么时候沦亡? } /** * 判断是否越界 */ public void outLine() { }}
- 在坦克的外部保护一个变量
isOut
,断定有没有越界 - 如果呈现了越界,则flag进行设置
接着,在绘画的办法外面,断定有没有越界:
/** * 绘画办法 * @param g */@Overridepublic void paint(Graphics g) { super.paint(g); //画出背景色 g.fill3DRect(0, 0, 600, 400, false); //画出本人的坦克 paintMyTank(mytank.getX(), mytank.getY(), g, mytank.getDirect(), mytank.getColor()); //画出敌人的坦克 paintEnemyTank(g); //画出子弹并且确定没有越界 if (mytank.but != null && !mytank.but.isOut) { g.fill3DRect(mytank.but.getX(), mytank.but.getY(), 5, 5, false); }}
第四个版本:
从这一个版本开始,一个游戏的简略雏形曾经有了,这一个版本实现了让敌人挪动的同时发射子弹的性能,同时我方的坦克射击敌人的时候,能够让敌人隐没
怎么样让敌人能够边挪动边发射子弹:
咱们须要在敌人的多线程run
代码外面,然敌人进行间歇性的走动:
@Override //咱们发现坦克在原地抽搐,咱们要实现坦克的安稳运行 //实现坦克静止不会越界 public void run() { do { switch (this.direct) { case 0: for (int i = 0; i < 30; i++) { if (y > 0) y -= sreed; try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 1: for (int i = 0; i < 30; i++) { if (x < 500) x += sreed; try { // 短暂的进展 Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 2: for (int i = 0; i < 30; i++) { if (y < 400) y += sreed; try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; case 3: for (int i = 0; i < 30; i++) { if (x > 0) x -= sreed; try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } break; } //不同的方向挪动的方向不同 this.direct = (int) (Math.random() * 4); } while (this.isLive); }
至于生成子弹,须要定时去轮询所有的坦克,查看坦克中组合的子弹汇合是否存在子弹,如果小于肯定的数量,须要生成对应的子弹对象同时退出到敌人的坦克当中。因为子弹创立就会开始执行线程进行
@Override public void run() { //限定一段时间从新绘制 while (true) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //判断是否击中 for (int x = 0; x < mytank.vecs.size(); x++) { //每一颗子弹和每一个坦克匹配 //取出一颗子弹之前判断是否有子弹 buts = mytank.vecs.get(x); //判断子弹是否无效 if (buts.isOut()) { continue; } //取出每一个坦克与它判断 for (int y = 0; y < vec.size(); y++) { //判断敌方坦克是否死亡 if (vec.get(y).isLive) { en = vec.get(y); //忘性判断是否击中操作 hitTank(en, buts); } } } //如果子弹数小于肯定数目 for (int x = 0; x < vec.size(); x++) { EnemyTank et = vec.get(x); //遍历每一辆坦克的子弹汇合 if (!et.isLive()) { continue; } if (et.vecs.size() < 1) { //对于不同的坦克方向生成子弹的方向也不同 Bullet enybut = null; switch (et.getDirect()) { case 0: enybut = new Bullet(et.getX() + 10, et.getY(), 0); //将创立的子弹退出到汇合当中 et.vecs.addElement(enybut); break; case 1: enybut = new Bullet(et.getX() + 30, et.getY() + 10, 1); et.vecs.addElement(enybut); break; case 2: enybut = new Bullet(et.getX() + 10, et.getY() + 30, 2); et.vecs.addElement(enybut); break; case 3: enybut = new Bullet(et.getX(), et.getY() + 10, 3); et.vecs.addElement(enybut); break; } new Thread(enybut).start(); } } //重绘 this.repaint(); } }
在子弹类当中进行一直的数值扭转:
上面的内容示意子弹的类
public class Bullet implements Runnable { //暗藏一大段代码: public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } switch (this.direct) { case 0: this.y -= screed; break; case 1: this.x += screed; break; case 2: this.y += screed; break; case 3: this.x -= screed; break; } //碰到边缘隐没 if (x < 0 || x > outx || y < 0 || y > outy) { isOut = true; break; } } }}
第五个版本:
在第五个版本当中,咱们实现了开始菜单的界面,同时眼帘菜单的一直显示:
界面会一直的闪动
接着,敌人减少了子弹能够捣毁咱们的办法
接着,咱们能够实现爆炸的成果:
因为爆炸的成果不好截图,请看源代码
/** * 实现闪动性能 * 重构坦克 - 第五版 * @author zxd * @version 1.0 * @date 2021/1/29 23:54 */class SelectIsSallup extends JPanel implements Runnable { /** * 工夫属性 */ int times = 0; public void paint(Graphics g) { super.paint(g); g.fillRect(0, 0, 600, 400); if (times % 2 == 0) { //画出文字 Font font1 = new Font("华文新魏", Font.BOLD, 20); //设置字体的色彩 g.setColor(Color.yellow); g.setFont(font1); g.drawString("stage 1", 200, 150); } } @Override public void run() { while (true) { try { Thread.sleep(750); } catch (InterruptedException e) { e.printStackTrace(); } if (times > 500) times = 0; times++; this.repaint(); } }}
如何让敌人的子弹对咱们造成挫伤:
/** * 建设一个办法,判断是否产生碰撞 * 是否攻打了其余的坦克 * @return */ private boolean isTouchOther() { // 依据本人的方向进行抉择判断 switch (this.direct) { // 坦克向上走的时候 case 0: // 取出所有的坦克对象 for (int x = 0; x < enevec.size(); x++) { EnemyTank et = enevec.get(x); //如果不是本人的坦克 if (et != this) { //如果敌人的坦克朝上或者朝下的时候 if (et.direct == 0 || et.direct == 2) { //判断边界 //对于第一个点进行判断 if (this.x >= et.x && this.x <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } //对于第二个点进行判断 if (this.x + 20 >= et.x && this.x + 20 <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } } //如果敌人是朝左边或者左边的时候 if (et.direct == 1 || et.direct == 3) { //判断边界 //对于第一个点进行判断 if (this.x >= et.x && this.x <= et.x + 30 && this.y >= et.y && this.y <= et.y + 20) { return true; } //对于第二个点进行判断 if (this.x + 20 >= et.x && this.x + 20 <= et.x + 30 && this.y >= et.y && this.y <= et.y + 20) { return true; } } } } break; // 坦克想左边走的时候 case 1: // 取出所有的坦克对象 for (int x = 0; x < enevec.size(); x++) { EnemyTank et = enevec.get(x); //如果不是本人的坦克 if (et != this) { //如果敌人的坦克朝上或者朝下的时候 if (et.direct == 0 || et.direct == 2) { //判断边界 //对于第一个点进行判断 if (this.x + 30 >= et.x && this.x + 30 <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } //对于第二个点进行判断 if (this.x + 30 >= et.x && this.x + 30 <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } } //如果敌人是朝左边或者左边的时候 if (et.direct == 1 || et.direct == 3) { //判断边界 //对于第一个点进行判断 if (this.x + 30 >= et.x && this.x + 30 <= et.x + 30 && this.y + 20 >= et.y && this.y <= et.y + 20) { return true; } //对于第二个点进行判断 if (this.x + 30 >= et.x && this.x + 30 <= et.x + 30 && this.y + 20 >= et.y && this.y <= et.y + 20) { return true; } } } } // 坦克想下的时候 case 2: // 取出所有的坦克对象 for (int x = 0; x < enevec.size(); x++) { EnemyTank et = enevec.get(x); //如果不是本人的坦克 if (et != this) { //如果敌人的坦克朝上或者朝下的时候 if (et.direct == 0 || et.direct == 2) { //判断边界 //对于第一个点进行判断 if (this.x >= et.x && this.x <= et.x + 20 && this.y + 30 >= et.y && this.y + 30 <= et.y + 30) { return true; } //对于第二个点进行判断 if (this.x + 20 >= et.x && this.x + 20 <= et.x + 20 && this.y + 30 >= et.y && this.y + 30 <= et.y + 30) { return true; } } //如果敌人是朝左边或者左边的时候 if (et.direct == 1 || et.direct == 3) { //判断边界 //对于第一个点进行判断 if (this.x >= et.x && this.x <= et.x + 30 && this.y + 30 >= et.y && this.y + 30 <= et.y + 20) { return true; } //对于第二个点进行判断 if (this.x + 20 >= et.x && this.x + 20 <= et.x + 30 && this.y + 30 >= et.y && this.y + 30 <= et.y + 20) { return true; } } } } break; // 坦克向左挪动的时候 case 3: // 取出所有的坦克对象 for (int x = 0; x < enevec.size(); x++) { EnemyTank et = enevec.get(x); //如果不是本人的坦克 if (et != this) { //如果敌人的坦克朝上或者朝下的时候 if (et.direct == 0 || et.direct == 2) { //判断边界 //对于第一个点进行判断 if (this.x >= et.x && this.x <= et.x + 20 && this.y >= et.y && this.y <= et.y + 30) { return true; } //对于第二个点进行判断 if (this.x >= et.x && this.x <= et.x + 20 && this.y + 20 >= et.y && this.y + 20 <= et.y + 30) { return true; } } //如果敌人是朝左边或者左边的时候 if (et.direct == 1 || et.direct == 3) { //判断边界 //对于第一个点进行判断 if (this.x >= et.x && this.x <= et.x + 30 && this.y >= et.y && this.y <= et.y + 20) { return true; } //对于第二个点进行判断 if (this.x >= et.x && this.x <= et.x + 30 && this.y + 20 >= et.y && this.y + 20 <= et.y + 20) { return true; } } } } } return false; }
最终版本:
在最终的版本当中,一个坦克大战的根本游戏算是实现了,当然还有很多须要实现点。
这里次要提醒一下暂停这一个性能点:
暂停的次要思维是为坦克加一个状态去管制坦克的所有行为。让暂停的flag为false的时候,线程不在执行,绘画每次都是绘制在同一个地位。这样就造成了“暂停”的假象。
//暂停性能if(e.getKeyCode()==KeyEvent.VK_P){ if(this.clickcount%2 == 0) mytank.setSuspend(false); else mytank.setSuspend(true); //利用循环将坦克类中的子弹速度变成0 for(int x=0; x<vec.size(); x++) { en = vec.get(x); //敌方坦克挪动速度归于0 //坦克不容许挪动 if(this.clickcount%2 == 0) en.setSuspend(false); else en.setSuspend(true); for(int y=0; y<en.vecs.size(); y++) { //子弹的速度变成0 if(this.clickcount%2 == 0) en.vecs.get(y).setSuspend(false); else en.vecs.get(y).setSuspend(true); } } this.clickcount++;}
总结:
这个文档不是最终版本,如果有不懂的欢送提issue,承诺给予回答然而不会再改变代码了。