JAVA坦克大战项目
文章目录
- 界面
- 技术栈
- 项目结构
- 功能
- 子弹移动(线程)
- 坦克移动(线程)
- 每隔100毫秒,重绘区域,刷新绘图区(线程)
- 解决坦克按下J键射击后原先射击的子弹消失了
- 让敌人的坦克发射的子弹消亡后,可以再发射子弹
- 可以发射多颗子弹
- 限制子弹发射数量
- 防止敌人坦克重叠
- 根据方向射击
- 爆炸效果
- 判断我方子弹是否击中坦克
- 监听键盘按下事件
- 发射一颗子弹
- 所有的子弹都取出和敌人所有的坦克进行判断
- 判断敌人坦克是否击中我方坦克
- 当敌人的坦克击中我方坦克时,我方坦克消失,并出现爆炸效果
- 退出游戏时记录玩家的总成绩(累计击毁敌方坦克数),敌人坦克坐标/方向,存盘退出【io流】
- 玩游戏时,可以选择是开新游戏还是继续上局游戏
- 游戏开始时,播放经典的坦克大战音乐
- 修正下文存储位置
- 处理文件相关异常
- 所有的子弹都取出和敌人所有的坦克进行判断
- 判断敌人坦克是否击中我方坦克
- 当敌人的坦克击中我方坦克时,我方坦克消失,并出现爆炸效果
- 退出游戏时记录玩家的总成绩(累计击毁敌方坦克数),敌人坦克坐标/方向,存盘退出【io流】
- 玩游戏时,可以选择是开新游戏还是继续上局游戏
- 游戏开始时,播放经典的坦克大战音乐
- 修正下文存储位置
- 处理文件相关异常
界面
技术栈
import java.util.Vector;
import java.util.HashMap;
import java.util.Map;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.util.Scanner;
import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
Math.random()
多线程
面向对象
异常处理
流程控制
循环
数据类型与运算符
IO流
项目结构
Tank 坦克父类
属性 | 方法 |
---|---|
x,y 坐标 | moveLeft 向左移动 |
direct 方向 | moveRight 向右移动 |
speed 速度,默认1 | moveDown 向下移动 |
isLive 是否存活 | moveUp 向上移动 |
(x、y、direct、speed)的Getter和Setter |
EnemyTank 敌人坦克类
属性 | 方法 |
---|---|
isLive 敌方坦克是否存活,默认true | run 坦克随机移动 |
shots 子弹集合 | setEnemyTanks 可以将MyPanel 的成员 Vector enemyTanks = new Vector<>(); 设置到 EnemyTank |
enemyTanks 可以得到敌人坦克的Vector | isTouchEnemyTank 判断当前的这个敌人坦克,是否和enemyTanks中的其他坦克发生的重叠或者碰撞 |
directionParams 定义一个映射来存储方向和Shot构造参数 | |
SLEEP_TIME 休眠时间 | |
MOVE_STEPS 移动步数 |
Hero 己方坦克类
属性 | 方法 |
---|---|
shot 表示一个射击(线程) | shotEnemyTank 射击 |
shots 可以发射多颗子弹 |
Bomb 爆炸类
属性 | 方法 |
---|---|
x,y 坐标 | lifeDown 减少生命值 |
isLive 是否存活,默认true | |
life 生命值,默认9 |
Shot 子弹类
属性 | 方法 |
x,y 坐标 | run 让子弹移动 子弹移动到面板的边界时退出,就应该销毁(把启动的子弹的线程销毁) 当子弹碰到敌人坦克时,也应该结束 |
speed 速度 | |
direct 方向 | |
isLive 是否存活 |
MyPanel 面板类
属性 | 方法 |
---|---|
Hero hero = null; // 己方坦克 | initEnemyTanks 初始化敌人坦克 |
Vector enemyTanks = new Vector<>(3); // 敌方坦克集合,默认3个 | paint 绘制 黑色矩形游戏活动范围 记录板 画出自己坦克和子弹 画出敌人的坦克和子弹 爆炸效果 |
// 存放炸弹 Vector bombs = new Vector<>(); | drawTank 画坦克 根据不同类型坦克,设置不同颜色 根据坦克方向,来绘制对应形状坦克 |
int enemyTankSize = 3; // 敌方坦克默认3个 | run 每隔100毫秒,重绘区域,刷新绘图区 |
// 3个爆炸图片对象 Image image1 = null; Image image2 = null; Image image3 = null; | hitTank 判断我方子弹是否击中坦克 |
directionMap 定义方向映射 | keyPressed 监听键盘按下 WSAD改变坦克方向 如果用户按下的是J,就发射 执行重绘 |
nodes 用来存储恢复游戏记录的敌方坦克数据 | initImages 初始化图片对象 |
showInfo 显示我方击毁敌方坦克的信息 | |
drawHeroAndShots 画己方坦克和子弹 | |
enemyTanksAndShots 画出敌人的坦克和子弹 | |
explosionEffect 爆炸效果 | |
hitHero 判断敌人坦克是否击中我的坦克 | |
canMoveInDirection 检查坦克是否可以在指定方向上移动 | |
moveHeroInDirection 根据方向移动坦克 |
AePlayWave 坦克大战音乐类
Node 类
用来恢复上次游戏记录
Recorder 记录类
该类用于记录相关信息的和文件交互
程序入口 类
功能
子弹移动(线程)
子弹移动到面板的边界时退出,就应该销毁(把启动的子弹的线程销毁)
当子弹碰到敌人坦克时,也应该结束
坦克移动(线程)
让坦克保持一个方向走30步,防止每走一步就改变方向乱动
防止坦克穿墙
随机改变坦克方向
如果坦克死亡(被击中)退出线程
每隔100毫秒,重绘区域,刷新绘图区(线程)
绘制黑色矩形游戏活动范围、画出自己坦克和子弹、画出敌人的坦克和子弹、爆炸效果
根据不同类型坦克,设置不同颜色; 根据坦克方向,来绘制对应形状坦克
/** * 画坦克 * * @param x 坦克左上角x坐标 * @param y 坦克左上角y坐标 * @param g 画笔 * @param direct 坦克方向 * @param type 坦克类型 */ public void drawTank(int x, int y, Graphics g, int direct, int type) { // 根据不同类型坦克,设置不同颜色 switch (type) { case 0: g.setColor(Color.cyan); break; case 1: g.setColor(Color.yellow); break; } // 根据坦克方向,来绘制对应形状坦克 switch (direct) { case 0: g.fill3DRect(x, y, 10, 60, false); g.fill3DRect(x + 30, y, 10, 60, false); g.fill3DRect(x + 10, y + 10, 20, 40, false); g.fillOval(x + 10, y + 20, 20, 20); g.drawLine(x + 20, y + 30, x + 20, y); break; case 1: g.fill3DRect(x, y, 60, 10, false); g.fill3DRect(x, y + 30, 60, 10, false); g.fill3DRect(x + 10, y + 10, 40, 20, false); g.fillOval(x + 20, y + 10, 20, 20); g.drawLine(x + 30, y + 20, x + 60, y + 20); break; case 2: g.fill3DRect(x, y, 10, 60, false); g.fill3DRect(x + 30, y, 10, 60, false); g.fill3DRect(x + 10, y + 10, 20, 40, false); g.fillOval(x + 10, y + 20, 20, 20); g.drawLine(x + 20, y + 30, x + 20, y + 60); break; case 3: g.fill3DRect(x, y, 60, 10, false); g.fill3DRect(x, y + 30, 60, 10, false); g.fill3DRect(x + 10, y + 10, 40, 20, false); g.fillOval(x + 20, y + 10, 20, 20); g.drawLine(x + 30, y + 20, x, y + 20); break; default: System.out.println("暂时没有处理"); } }
解决坦克按下J键射击后原先射击的子弹消失了
让敌人的坦克发射的子弹消亡后,可以再发射子弹
可以发射多颗子弹
创建一个shots集合,把新创建的shot子弹放到集合中,每颗子弹都要启动一个线程
在绘制子弹时,需要遍历该集合取出绘制,如果该shot对象已经无效,就从shots集合中拿掉
限制子弹发射数量
防止敌人坦克重叠
判断当前的这个敌人坦克,是否和enemyTanks中的其他坦克发送的重叠或者碰撞
根据方向射击
爆炸效果
爆炸类使用一个数组存放炸弹
让爆炸类生命值减少
每隔10毫秒根据爆炸类的生命值绘制爆炸图片(3张其中一张),如果爆炸类的生命值等于0就移除这个炸弹
try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } for (int i = 0; i < bombs.size(); i++) { Bomb bomb = bombs.get(i); if (bomb.life > 6) { g.drawImage(image1, bomb.x, bomb.y, 60, 60, this); } else if (bomb.life > 3) { g.drawImage(image2, bomb.x, bomb.y, 60, 60, this); } else { g.drawImage(image3, bomb.x, bomb.y, 60, 60, this); } bomb.lifeDown(); if (bomb.life == 0) { bombs.remove(bomb); } }
判断我方子弹是否击中坦克
子弹击中后子弹和坦克就不显示了、防止子弹击中不显示的坦克也会爆炸、防止画敌人坦克的的时候数组越界、击中坦克创建Bomb对象,加入到bombs
/** * 判断我方子弹是否击中坦克 * * @param s * @param enemyTank */ public void hitTank(Shot s, EnemyTank enemyTank) { switch (enemyTank.getDirect()) { case 0: // 上 case 2: // 下 if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 40 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 60) { s.isLive = false; // 子弹击中后坦克就不显示了 enemyTank.isLive = false; // 防止子弹击中不显示的坦克也会爆炸 enemyTanks.remove(enemyTank); // 防止画敌人坦克的的时候数组越界 enemyTankSize--; // 创建Bomb对象,加入到bombs Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY()); bombs.add(bomb); } break; case 1: // 右 case 3: // 左 if (s.x > enemyTank.getX() && s.x < enemyTank.getX() + 60 && s.y > enemyTank.getY() && s.y < enemyTank.getY() + 40) { s.isLive = false; enemyTank.isLive = false; // 防止子弹击中不显示的坦克也会爆炸 enemyTanks.remove(enemyTank); // 防止画敌人坦克的的时候数组越界 enemyTankSize--; Bomb bomb = new Bomb(enemyTank.getX(), enemyTank.getY()); bombs.add(bomb); } break; } }
监听键盘按下事件
改变坦克方向位置、解决坦克穿墙、如果用户按下的是J就发射、需要重绘面板才能生效
发射一颗子弹
1.在按下J键,我们判断当前hero对象的子弹,是否已经销毁
2.如果没有销毁,就不去触发shotEnemyTank
3.如果已经销毁,才去触发shotEnemyTank
所有的子弹都取出和敌人所有的坦克进行判断
判断敌人坦克是否击中我方坦克
遍历所有敌人坦克,取出敌人坦克,遍历敌人坦克对象的所有子弹,取出子弹,如果我们的坦克还活着并且敌人的坦克也活着,判断我方子弹是否击中坦克
当敌人的坦克击中我方坦克时,我方坦克消失,并出现爆炸效果
退出游戏时记录玩家的总成绩(累计击毁敌方坦克数),敌人坦克坐标/方向,存盘退出【io流】
玩游戏时,可以选择是开新游戏还是继续上局游戏
游戏开始时,播放经典的坦克大战音乐
使用一个播放音乐的类
修正下文存储位置
处理文件相关异常
shotEnemyTank
所有的子弹都取出和敌人所有的坦克进行判断
判断敌人坦克是否击中我方坦克
遍历所有敌人坦克,取出敌人坦克,遍历敌人坦克对象的所有子弹,取出子弹,如果我们的坦克还活着并且敌人的坦克也活着,判断我方子弹是否击中坦克
当敌人的坦克击中我方坦克时,我方坦克消失,并出现爆炸效果
退出游戏时记录玩家的总成绩(累计击毁敌方坦克数),敌人坦克坐标/方向,存盘退出【io流】
[外链图片转存中…(img-GUFNYxBZ-1731835156330)]
玩游戏时,可以选择是开新游戏还是继续上局游戏
游戏开始时,播放经典的坦克大战音乐
使用一个播放音乐的类