Java坦克大战 跟学韩顺平老师视频开发
Posted 心向阳光的天域
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java坦克大战 跟学韩顺平老师视频开发相关的知识,希望对你有一定的参考价值。
这里写目录标题
TankBigWarGame
介绍
TankBigWar,坦克大战,跟着韩顺平老师的30天Java基础课程一点一点自己敲的,完成后很有成就感
项目源码放到了码云上,需要的可以自取
坦克大战项目
界面展示
整体展示了设计布局
右侧玩家信息栏,如下图所示:
项目架构
软件架构说明:简单用JFrame组件绘制的坦克和界面,对于多线程、面向对象编程、JFrame的使用有很好的帮助,是初学者不错的练手项目。
安装教程
- 下载后只需要执行Main方法即可
- 声音和txt文件统一放到src目录底下,用的是相对路径编写的代码,所以不用担心运行不了
- 启动Main方法后,要在控制台选择是新游戏还是读档,键盘输入1:新游戏;键盘输入2:读档
游戏说明
- 右侧是击毁敌方坦克数量、玩家坦克剩余数量
- 进入游戏后,一般输入法会切换成中文,要默认切换一下再进行操作
- 键盘方向键控制坦克方向,空格键射击。
- 进入游戏后按数字键1可以召唤敌方坦克
- 进入游戏后,如果我方坦克被敌方摧毁,可以按数字键2原地复活
- 进入游戏后有BGM背景音乐、我方发射子弹有音效、我方或者敌方坦克被摧毁有爆炸音效
项目涉及技术功能
- 玩家坦克移动、开火、撞墙判定
- 敌方坦克多线程移动、开火、移动时碰撞判定和转向
- 背景音乐
- 游戏进度存档
- 玩家击毁后原地复活
- 召唤敌方坦克
- 显示玩家坦克剩余生命,显示击毁敌方坦克数量
- 点击叉关闭窗体后保存敌方坦克位置,后续读档的时候可以恢复敌方坦克位置
游戏结束判断
- 敌方击败我方坦克3条命,即游戏结束
项目不足与优化空间
- 游戏结束后无背景音乐
- 碰撞处理:敌方坦克在运行时,碰撞后会发生转向无误,但是如果转向的时候发生碰撞,则两辆坦克会卡住
- 可以增加游戏结束机制:比如可以增加胜利条件,击毁多少敌方坦克就可以胜利,或者增加我方的家,一旦敌方击毁我家即失败
- 在召唤敌方的基础上,做到触发定时任务,每隔一段时间召唤一定数量敌方坦克
- 增加我方坦克的碰撞处理
- 直接点叉关闭窗口的时候,保存我方坦克位置,下次读档的时候恢复我方坦克位置、敌方子弹位置、我方子弹位置
- 增加一定障碍物:墙壁、河流、绿茵等等
- 显示敌方剩余坦克数量
相关代码展示
主方法Main
package com.View;
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Scanner;
/**
* @author wty
* @date 2022/10/21 0:07
* 主方法启动
*/
public class Main extends JFrame
MyPanelForGame mypanel = null;
static Scanner scanner = new Scanner(System.in);
public static void main(String[] args)
new Main();
public Main()
System.out.println("请输入选择:");
System.out.println("1:开始新游戏");
System.out.println("2:读取存档");
String key = scanner.nextLine();
mypanel = new MyPanelForGame(key);
// 将mypanel启动
new Thread(mypanel).start();
this.add(mypanel);
// 窗口的大小(窗口大小)
this.setSize(1300, 800);
this.addKeyListener(mypanel);
this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.setVisible(true);
this.setTitle("Tank Big Game");
// 在JFram增加相应和关闭窗口的处理:
this.addWindowListener(new WindowAdapter()
@Override
public void windowClosing(WindowEvent windowEvent)
System.out.println("监听到关闭窗口");
Record.saveRecordEnemy();
Record.saveRecordPlayer();
System.exit(0);
);
绘图界面 MyPanelForGame
package com.View;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.Vector;
/**
* @author wty
* @date 2022/10/21 0:01
* 绘图区
* 为了不停的重绘子弹,必须要MyPanelForGame实现一个Runnable接口,当作线程使用
*/
public class MyPanelForGame extends JPanel implements KeyListener, Runnable
// 定义我的坦克
PlayerTank playerTank = null;
Vector<EnemyTank> enemyTanks = new Vector<>();
int enemyTankSize = 3; // 敌人 坦克的数量
// 炸弹
Vector<Boom> booms = new Vector<>();
// 定义三张炸弹图片,用于显示爆炸效果
// 当子弹击中敌人时,就加载图片
Image image_boom1 = null;
Image image_boom2 = null;
Image image_boom3 = null;
// 我方坦克生命值
Image image_heart = null;
// 定义一个nodes存放Record中的node信息
private static Vector<Node> nodes = new Vector<>();
private String key = "";
public String getKey()
return key;
public void setKey(String key)
this.key = key;
public Vector<EnemyTank> getEnemyTanks()
return enemyTanks;
public void setEnemyTanks(Vector<EnemyTank> enemyTanks)
this.enemyTanks = enemyTanks;
public MyPanelForGame(String key)
// 先判断记录的文件是否存在
// 如果存在,就正常执行,如果不存在,提示只能开启新游戏,并把key = 1
File fileEnemy = new File(Record.getRecordFile());
File filePlayer = new File(Record.getRecordFilePlay());
if (fileEnemy.exists() && filePlayer.exists())
nodes = Record.getNodesandCountEnemy(); // 初始化时,将右侧敌人游戏数据读取
Record.getNodesandCountPlayer();// 初始化时,将右侧玩家游戏数据读取
else
System.out.println("文件不存在,只能开启新游戏!");
key = "1";
setKey(key); // 设置是否是新游戏
// 针对是新游戏还是读取存档
switch (key)
case "1":
initEnemyTank(); // 新游戏
Record.setAllEnemyTankCount(0);
Record.setAllPlayerTankCount(3);
break;
case "2":
loadEnemyTank(); // 读取存档
loadPlayerTank();
break;
default:
System.out.println("您的输入有误,请重新输入");
// 加在这里(初始化敌人的坦克)
initPlayerTank();
// 初始化炸弹的图片
image_boom1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));
image_boom2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));
image_boom3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));
image_heart = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/heart.png"));
// 播放音乐
new AePlayWave("src\\\\111.wav").start();
// 初始化敌人的坦克
public void initEnemyTank()
// 将MyPanelForGame的VectorenemyTanks赋值给Record的enemyTanks
Record.setEnemyTanks(enemyTanks);
// 初始化敌人的坦克
for (int i = 0; i < enemyTankSize; i++)
// 每创建一个敌人坦克,就设定速度和方向
EnemyTank enemyTank = new EnemyTank(200 * (i + 1), 0);
enemyTank.setSpeed(4);
enemyTank.setDirection(2);
// 给敌人坦克增加子弹初始位置
Shot shot = new Shot(enemyTank.getX() + 37, enemyTank.getY() + 70, enemyTank.getDirection());
// 并且把子弹加入EnemyTank的enemy_Shoots管理
enemyTank.getEnemy_Shoots().add(shot);
// 将enemyTank集合设置给setEnemyTanks
enemyTank.setEnemyTanks(enemyTanks);
// 启动shot对象
new Thread(shot).start();
enemyTanks.add(enemyTank);
// 启动敌人坦克自动移动的线程
new Thread(enemyTank).start();
// 读取存档敌人的坦克
public void loadEnemyTank()
// 将MyPanelForGame的VectorenemyTanks赋值给Record的enemyTanks
Record.setEnemyTanks(enemyTanks);
// 初始化敌人的坦克
for (int i = 0; i < nodes.size(); i++)
Node node = nodes.get(i);
// 每创建一个敌人坦克,就设定速度和方向
EnemyTank enemyTank = new EnemyTank(node.getX(), node.getY());
enemyTank.setSpeed(4);
enemyTank.setDirection(node.getDirection());
// 给敌人坦克增加子弹初始位置
Shot shot = new Shot(enemyTank.getX() + 37, enemyTank.getY() + 70, enemyTank.getDirection());
// 并且把子弹加入EnemyTank的enemy_Shoots管理
enemyTank.getEnemy_Shoots().add(shot);
// 将enemyTank集合设置给setEnemyTanks
enemyTank.setEnemyTanks(enemyTanks);
// 启动shot对象
new Thread(shot).start();
enemyTanks.add(enemyTank);
// 启动敌人坦克自动移动的线程
new Thread(enemyTank).start();
public void initPlayerTank()
// 初始化玩家坦克
playerTank = new PlayerTank(300, 400);
//playerTank = new PlayerTank(30, 40);
// 设置速度
playerTank.setSpeed(4);
playerTank.setDirection(0);
// 存储玩家坐标
Record.setX_player(playerTank.getX());
Record.setY_player(playerTank.getY());
Record.setDirection_player(playerTank.getDirection());
// 读取存档中玩家坦克的信息
public void loadPlayerTank()
// 初始化玩家坦克
playerTank = new PlayerTank(Record.getX_player(), Record.getY_player());
//playerTank = new PlayerTank(30, 40);
// 设置速度
playerTank.setSpeed(4);
playerTank.setDirection(Record.getDirection_player());
// 展示玩家信息
public void showGameInfo(Graphics graphics)
graphics.setColor(Color.BLACK);
Font font = new Font("宋体", Font.BOLD, 25);
graphics.setFont(font);
graphics.drawString("您累积击毁坦克", 1020, 30);
// 击毁敌方坦克
drawTank(1020, 60, graphics, 0, 0); // 方向朝上,己方坦克
graphics.setColor(Color.BLACK);
graphics.drawString(Record.getAllEnemyTankCount() + " ", 1170, 100);
// 我方坦克生命
graphics.drawImage(image_heart, 1020, 200, 84, 84, this);
graphics.setColor(Color.BLACK);
graphics.drawString("X " + Record.getAllPlayerTankCount() + " ", 1170, 250);
// 游戏提示
graphics.setColor(Color.BLACK);
Font font2 = new Font("黑体", Font.PLAIN, 23);
graphics.setFont(font2);
graphics.drawString("操作指引:", 1020, 400);
graphics.drawString("方向键控制方向", 1020, 450);
graphics.drawString("空格开火", 1020, 500);
graphics.drawString("数字键1增加敌方坦克", 1020, 550);
graphics.drawString("数字键2初始我方坦克", 1020, 600);
@Override
public void paint(Graphics graphics)
super.paint(graphics);
// 背景(黑色的) 游戏场景
graphics.fillRect(0, 0, 1000, 750);
// 右侧玩家信息栏
showGameInfo(graphics);
// 玩家坦克
if (playerTank.isLive() && playerTank != null)
drawTank(playerTank.getX(), playerTank.getY(), graphics, playerTank.getDirection(), 0);
//drawTank(playerTank.getX() + 100, playerTank.getY(), graphics, 1, 0);
//drawTank(playerTank.getX() + 100, playerTank.getY() + 100, graphics, 2, 0);
//drawTank(playerTank.getX(), playerTank.getY() + 100, graphics, 3, 0);
// 绘制敌人坦克
for (int i = 0; i < enemyTanks.size(); i++)
// enemyTank是Vector里面存储的是EnemyTank对象
EnemyTank enemyTank = enemyTanks.get(i);
// 判断当前坦克是否存活,存活才能画
if (enemyTank.isLive() == true)
drawTank(enemyTank.getX(), enemyTank.getY(), graphics, enemyTank.getDirection(), 11、说明
写了14个版本,终于完成了,项目结构如下
2、游戏效果
(1)击中敌人
(2)被敌人击中
3、代码如下
(1)TankGame 主方法
package cn.bloghut;
import cn.bloghut.component.MyPanel;
import cn.bloghut.service.Recorder;
import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Scanner;
/**
* @Classname TankGame
* @Description TODO
* @Date 2022/5/8 8:25
* @Created by 闲言
*/
public class TankGame extends JFrame
private MyPanel panel;
public static void main(String[] args)
new TankGame();
public TankGame()
System.out.println("请输入选择: 1 表示新游戏,2是继续上局");
Scanner scanner = new Scanner(System.in);
//将panel放入到thread中
this.panel = new MyPanel(scanner.next());
scanner.close();
new Thread(panel).start();
//设置绑定监听器
this.addKeyListener(panel);
this.add(panel);
//设置窗体关闭
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("闲言-坦克大战");
//获取当前屏幕分辨率
int width = Toolkit.getDefaultToolkit().getScreenSize().width;
int height = Toolkit.getDefaultToolkit().getScreenSize().height;
//设置窗体大小不可变
this.setResizable(false);
//设置窗体位置
width = (width - 1300) / 2;
height = (height - 750) / 2;
this.setBounds(width,height,1300,750);
this.setBackground(Color.black);
//设置窗体显示
this.setVisible(true);
//在JFrame 中增加相关关闭窗口的处理
this.addWindowListener(new WindowAdapter()
@Override
public void windowClosing(WindowEvent e)
Recorder.keepRecord();
System.exit(0);
);
(2)MyPanel 视图
package cn.bloghut.component;
import cn.bloghut.pojo.*;
import cn.bloghut.service.PlayAudio;
import cn.bloghut.service.Recorder;
import cn.bloghut.service.Shot;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.util.Vector;
/**
* @Classname MyPanel
* @Description 坦克大战绘图区域
* 为了让panel 不停的重绘子弹,需要将MyPanel
* @Date 2022/5/8 8:44
* @Created by 闲言
*/
public class MyPanel extends JPanel implements KeyListener, Runnable
//定义我的坦克
Hero hero = null;
//定义敌人坦克
Vector<EnemyTank> enemyTanks = new Vector<>();
//用于存放炸弹
//当子弹击中坦克时,就加入一个bomb 对象
Vector<Bomb> bombs = new Vector<>();
//定义三种图片,用于显示炸弹
Image img1 = null;
Image img2 = null;
Image img3 = null;
//定义一个存放Node 对象的Vector,用于恢复敌人坦克的坐标和放向
Vector<Node> nodes = null;
//敌人坦克数量
int enemyTankSize = 3;
public MyPanel(String type)
//判断记录文件是否存在
//如果存在就正常执行,如果文件不存在,提示只能开启新的游戏
File file = new File(Recorder.getRecordFile());
if (!file.exists())
type = "1";
System.out.println("文件不存在只能开启新游戏");
else
nodes = Recorder.getNodesAndTanks();
//初始化我的坦克
hero = new Hero(150, 150);
//设置坦克速度
hero.setSpeed(5);
switch (type)
case "1":
//初始化敌人坦克
for (int i = 0; i < enemyTankSize; i++)
EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
enemyTank.setDirection(2);
//启动敌人坦克线程
new Thread(enemyTank).start();
//给该enemyTank 加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
//加入到
enemyTank.getShots().add(shot);
//为每个坦克,放入所有的敌人对象
enemyTank.setEnemyTanks(enemyTanks);
//启动shot
new Thread(shot).start();
enemyTanks.add(enemyTank);
break;
case "2": //继续上局游戏
//初始化敌人坦克
for (int i = 0;i < nodes.size();i++)
Node node = nodes.get(i);
EnemyTank enemyTank = new EnemyTank(node.getX(),node.getY());
enemyTank.setDirection(node.getDirection());
//启动敌人坦克线程
new Thread(enemyTank).start();
//给该enemyTank 加入一颗子弹
Shot shot = new Shot(enemyTank.getX() + 20, enemyTank.getY() + 60, enemyTank.getDirection());
//加入到
enemyTank.getShots().add(shot);
//为每个坦克,放入所有的敌人对象
enemyTank.setEnemyTanks(enemyTanks);
//启动shot
new Thread(shot).start();
enemyTanks.add(enemyTank);
break;
default:
System.out.println("你的输入有误");
//获取项目路径
//1.获取图片资源
String parent = System.getProperty("user.dir");
parent += "\\\\";
//2.初始化图片
img1 = Toolkit.getDefaultToolkit().getImage(parent + "bomb_1.gif");
img2 = Toolkit.getDefaultToolkit().getImage(parent + "bomb_2.gif");
img3 = Toolkit.getDefaultToolkit().getImage(parent + "bomb_3.gif");
//存放敌人坦克信息
Recorder.setEnemyTanks(enemyTanks);
//播放指定的音乐
new PlayAudio("bg.wav").start();
@Override
public void paint(Graphics g)
super.paint(g);
//填充矩形,默认黑色
g.fillRect(0, 0, 1000, 750);
//画出游戏信息
showInfo(g);
//画自己坦克
if (hero != null && hero.isLive())
drawTank(hero.getX(), hero.getY(), g, hero.getDirection(), 1);
//画出自己射击的子弹
//不为空,且处于发射状态
if (hero.getShot() != null && hero.getShot().isLive())
g.draw3DRect(hero.getShot().getX(), hero.getShot().getY(), 2, 2, false);
if (hero.getShots().size() > 0)
for (int i = 0; i < hero.getShots().size(); i++)
Shot shot = hero.getShots().get(i);
if (shot != null && shot.isLive())
g.draw3DRect(shot.getX(), shot.getY(), 2, 2, false);
else
//如果shot对象已经无效,就从子弹集合中移除
hero.getShots().remove(shot);
//画敌人坦克
for (int i = 0; i < enemyTanks.size(); i++)
EnemyTank enemyTank = enemyTanks.get(i);
//判断当前坦克是否还存活,只有存活的时候才去画
if (enemyTank.isLive())
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 0);
//绘制敌人坦克子弹
Vector<Shot> shots = enemyTank.getShots();
for (int j = 0; j < shots.size(); j++)
Shot shot = shots.get(j);
//子弹存活的时候才绘制
if (shot.isLive())
g.draw3DRect(shot.getX(), shot.getY(), 1, 1, false);
else
//子弹出边界了,移除子弹
enemyTank.getShots().remove(shot);
//如果bombs 集合中有对象,就画出
for (int i = 0; i < bombs.size(); i++)
Bomb bomb = bombs.get(i);
//根据当前炸弹的life值画出对应的图片
if (bomb.getLife() > 6)
g.drawImage(img1, bomb.getX(), bomb.getY(), 60, 60, this);
else if (bomb.getLife() > 3)
g.drawImage(img1, bomb.getX(), bomb.getY(), 60, 60, this);
else
g.drawImage(img1, bomb.getX(), bomb.getY(), 60, 60, this);
//让这个炸弹的生命值减少
bomb.lifeDown();
//如果bomb 生命值为0,就从集合中删除
if (bomb.getLife() == 0)
bombs.remove(bomb);
/**
* 显示 击毁敌方的坦克信息
*
* @param g 画图
*/
public void showInfo(Graphics g)
g.setColor(Color.BLACK);
Font font = new Font("宋体", Font.BOLD, 25);
g.setFont(font);
g.drawString("您累计击毁敌方坦克", 1020, 30);
drawTank(1020, 60, g, 0, 0);
g.setColor(Color.BLACK);
g.drawString(Recorder.getAllEnemyTankTotal() + "", 1080, 100);
/**
* 绘制坦克
*
* @param x 坦克的左上角x坐标
* @param y 坦克的左上角y坐标
* @param g 画笔
* @param direction 坦克方向(上下左右)
* @param type 坦克类型
*/
public void drawTank(int x, int y, Graphics g, int direction, int type)
//根据不同类型的坦克设置不同颜色
switch (type)
case 0://0 敌人的坦克
g.setColor(Color.cyan);
break;
//
case 1://1 我们的坦克
g.setColor(Color.yellow);
break;
//根据坦克的方向来绘制坦克
//direction 0-向上,1-向右,2-向下,3-向左
switch (direction)
case 0:
//左右以上是关于Java坦克大战 跟学韩顺平老师视频开发的主要内容,如果未能解决你的问题,请参考以下文章
韩顺平循序渐进学Java零基础 项目篇(09161820222628)