共计 10856 个字符,预计需要花费 28 分钟才能阅读完成。
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
*/
@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);
}
}
第四个版本:
从这一个版本开始,一个游戏的简略雏形曾经有了,这一个版本实现了让敌人挪动的同时发射子弹的性能,同时我方的坦克射击敌人的时候,能够让敌人隐没
怎么样让敌人能够边挪动边发射子弹:
咱们须要在敌人的多线程 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,承诺给予回答然而不会再改变代码了。