前言:
这个项目是之前备份电脑资料的时候看到的,不禁一阵感慨自己当初自学编程的心酸和泪水。所以分享一下自己当初写的的垃圾代码。虽然我不是任天堂忠实粉丝,但是对于90后来说坦克大战基本是人人都玩过的一款小霸王游戏机的游戏。
这个项目对于已经入行的人来说没有价值,分享出来主要是希望对于初学编程的人给一点 “吸引”吧,原来代码可以做到这么愉快的事情。这个坦克大战也是自己跟着培训机构的教学视频边看边敲的。
花了一天左右的时间把代码稍微整理了一下代码同时写了这份文档,这里分享出来。
对于入行编程的同学个人的建议如果要快速成长还是多练,多做。很多东西做多了之后,涉及自己的盲区会促使你不断的学习和进步。
PS:代码使用Eclipse写的,这里用IDEA整理了一下代码
此代码如果阅读存在难度,建议看一下韩顺平的JAVA基础坦克大战的课程。这里的源代码也是跟着课程敲的。
真是一个好时代,当初这些资源还要网上翻半天
项目地址
后续文档更新请看readme
项目简介:
个人前几年自学的时候从一个教学视频的,韩顺平老师的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 */ @Override public 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); } } 复制代码