Java坦克大战 跟学韩顺平老师视频开发

Posted 心向阳光的天域

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java坦克大战 跟学韩顺平老师视频开发相关的知识,希望对你有一定的参考价值。

这里写目录标题

TankBigWarGame

介绍

TankBigWar,坦克大战,跟着韩顺平老师的30天Java基础课程一点一点自己敲的,完成后很有成就感

项目源码放到了码云上,需要的可以自取
坦克大战项目

界面展示

整体展示了设计布局

右侧玩家信息栏,如下图所示:

项目架构

软件架构说明:简单用JFrame组件绘制的坦克和界面,对于多线程、面向对象编程、JFrame的使用有很好的帮助,是初学者不错的练手项目。

安装教程

  1. 下载后只需要执行Main方法即可
  2. 声音和txt文件统一放到src目录底下,用的是相对路径编写的代码,所以不用担心运行不了
  3. 启动Main方法后,要在控制台选择是新游戏还是读档,键盘输入1:新游戏;键盘输入2:读档

游戏说明

  1. 右侧是击毁敌方坦克数量、玩家坦克剩余数量
  2. 进入游戏后,一般输入法会切换成中文,要默认切换一下再进行操作
  3. 键盘方向键控制坦克方向,空格键射击。
  4. 进入游戏后按数字键1可以召唤敌方坦克
  5. 进入游戏后,如果我方坦克被敌方摧毁,可以按数字键2原地复活
  6. 进入游戏后有BGM背景音乐、我方发射子弹有音效、我方或者敌方坦克被摧毁有爆炸音效

项目涉及技术功能

  1. 玩家坦克移动、开火、撞墙判定
  2. 敌方坦克多线程移动、开火、移动时碰撞判定和转向
  3. 背景音乐
  4. 游戏进度存档
  5. 玩家击毁后原地复活
  6. 召唤敌方坦克
  7. 显示玩家坦克剩余生命,显示击毁敌方坦克数量
  8. 点击叉关闭窗体后保存敌方坦克位置,后续读档的时候可以恢复敌方坦克位置

游戏结束判断

  1. 敌方击败我方坦克3条命,即游戏结束

项目不足与优化空间

  1. 游戏结束后无背景音乐
  2. 碰撞处理:敌方坦克在运行时,碰撞后会发生转向无误,但是如果转向的时候发生碰撞,则两辆坦克会卡住
  3. 可以增加游戏结束机制:比如可以增加胜利条件,击毁多少敌方坦克就可以胜利,或者增加我方的家,一旦敌方击毁我家即失败
  4. 在召唤敌方的基础上,做到触发定时任务,每隔一段时间召唤一定数量敌方坦克
  5. 增加我方坦克的碰撞处理
  6. 直接点叉关闭窗口的时候,保存我方坦克位置,下次读档的时候恢复我方坦克位置、敌方子弹位置、我方子弹位置
  7. 增加一定障碍物:墙壁、河流、绿茵等等
  8. 显示敌方剩余坦克数量

相关代码展示

主方法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坦克大战

韩顺平--Java坦克大战

韩顺平循序渐进学Java零基础 项目篇(09161820222628)

韩顺平循序渐进学Java零基础 项目篇(09161820222628)

第16章 坦克大战1

第20章 坦克大战3

(c)2006-2024 SYSTEM All Rights Reserved IT常识