坦克大战②

news2024/11/15 11:47:42

1. 我方坦克发射单颗子弹

  1. 当发射一颗子弹后,就相当于启动一个线程来控制它的位置坐标;
  2. Hero[我方坦克]有子弹的对象,当按下J时,就创建一个发射子弹的线程,通过坐标变化让子弹不停的移动,形成一个射击的效果;
  3. 我们的MyPanel类需要通过不停地重绘画面来重绘子弹,这样才能让子弹实时出现在画面上;
  4. 当子弹移动到面板的边界时,就应该销毁(isLive设置为false,线程结束);
  1. 创建子弹类
public class Bullet implements Runnable {//子弹创建线程
    private int x;//子弹x坐标
    private int y;//y坐标
    private int speed = 2;//速度
    private int direct;//方向
    private boolean isLive = true;

    public Bullet(int x, int y, int direct) {//子弹初始位置
        this.x = x;
        this.y = y;
        this.direct = direct;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }

    @Override
    public void run() {
        while (true) {
            if (direct == 0) {//线程中根据方向调节坐标
                y -= speed;
            } else if (direct == 1) {
                x += speed;
            } else if (direct == 2) {
                y += speed;
            } else if (direct == 3) {
                x -= speed;
            }
            System.out.println("子弹 x=" + x + ",y=" + y);
            if (!(x >= 0 && x <= 500 && y >= 0 && y <= 400)) {
                System.out.println("子弹超出范围,线程结束");
                isLive = false;
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  1. 坦克射击子弹
public class Hero extends Tank {
    private Bullet bullet;//坦克射击子弹
    public Hero(int x, int y) {//坦克的初始位置
        super(x, y);
    }

    public Bullet getBullet() {
        return bullet;
    }

    public void shotBullet() {//根据坦克的方向确定子弹的位置
        switch (getDirect()) {
            case 0:
                bullet = new Bullet(getX() + 20, getY(), 0);
                break;
            case 1:
                bullet = new Bullet(getX() + 60, getY() + 20, 1);
                break;
            case 2:
                bullet = new Bullet(getX() + 20, getY() + 60, 2);
                break;
            case 3:
                bullet = new Bullet(getX(), getY() + 20, 3);
                break;
        }
        new Thread(bullet).start();//发射子弹之前要确定子弹的位置和射击方向
    }
}
  1. 面板通过线程实现重绘
new Thread(myPanel).start();
@Override
    public void run() {
        while (true) {
            this.repaint();//面板时刻被重绘
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
  1. 画出子弹图形
if (hero.getBullet() != null && hero.getBullet().isLive() != false) {
            System.out.println("子弹被重绘");
            g.fillOval(hero.getBullet().getX()-2, hero.getBullet().getY()-2, 4, 4);
        }
  1. 按J发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {
            System.out.println("发射子弹");
            hero.shotBullet();
        }

2.敌方坦克发射子弹

让敌方坦克也能够发射子弹(可以有多颗子弹)

  1. 在敌人坦克类,使用Vector保存多个Bullet;
    在这里插入图片描述
  2. 当每创建一个敌人坦克对象时,给该敌人坦克对象初始化一个Bullet对象,同时启动Bullet线程;
    在这里插入图片描述
  3. 在绘制敌人坦克时,需要遍历敌人坦克对象Vector。绘制所有的子弹,当子弹isLive为false时,就从Vecotr上移除;
    在这里插入图片描述

3. 击中敌方坦克

  1. 给敌方坦克类创建一个isLive属性
    在这里插入图片描述
  1. 在MyPanel类中判断我方子弹是否击中敌方坦克
    调用时需要注意:(1)我方的子弹参数必须是存活的;(2)敌方坦克不止一辆;
//判断我方子弹是否击中敌方坦克
    public void hitTank(Bullet bullet, EnemyTank enemyTank) {
        switch (enemyTank.getDirect()) {
            case 0://向上
            case 2://向下
                if (bullet.getX() > enemyTank.getX() && bullet.getX() < enemyTank.getX() + 40
                        && bullet.getY() > enemyTank.getY() && bullet.getY() < enemyTank.getY() + 60) {
                    bullet.setLive(false);
                    enemyTank.setLive(false);
                }
                break;
            case 1://向右
            case 3://向左
                if (bullet.getX() > enemyTank.getX() && bullet.getX() < enemyTank.getX() + 60
                        && bullet.getY() > enemyTank.getY() && bullet.getY() < enemyTank.getY() + 40) {
                    bullet.setLive(false);
                    enemyTank.setLive(false);
                }
        }
    }
  1. 什么时候判断我方坦克是否击中敌方坦克?(run方法中判断)
    在这里插入图片描述
  2. 在绘制敌方坦克以及绘制敌方坦克的子弹时,要先判断敌方坦克是否还存活;
    在这里插入图片描述
  3. 子弹有两种途径被销毁:第一是碰到边界,第二是碰到敌方坦克;
    在Bullet类中加个条件
    在这里插入图片描述

4. 爆炸效果

  1. 创建一个炸弹类;
public class Bomb {//定义一个炸弹类
    int x,y;
    int life;//炸弹的生命周期
    boolean isLive = true;//炸弹是否存活

    public Bomb(int x, int y) {
        this.x = x;
        this.y = y;
    }

    //减少生命值
    public void HpDown() {
        if (life > 0) {
            life--;
        } else {
            isLive = false;
        }
    }
}
  1. 在MyPanel类创建炸弹集合
    在这里插入图片描述
    2.1 在out文件夹的chapter18文件夹的下一级里放入炸弹爆炸的图片;
    在这里插入图片描述
    2.2在MyPanel构造器中初始化图片对象
    在这里插入图片描述
  2. 在hitTank()方法中,当子弹击中坦克时,坦克被销毁,从坦克集合中去除这辆坦克;并且创建一个bomb对象加入到bombs集合中;
    在这里插入图片描述
  3. 如果bombs集合中有炸弹,就绘制;
//绘制爆炸图片
        for (int i = 0; i < bombs.size(); i++) {
            Bomb bomb = bombs.get(i);//取出炸弹
            if (bomb.getLife() > 6) {//根据当前这个bomb对象的life值判断绘制哪张图片
                g.drawImage(image1, bomb.getX(), bomb.getY(), 40, 60, this);
            } else if (bomb.getLife() > 3) {
                g.drawImage(image2, bomb.getX(), bomb.getY(), 40, 60, this);
            } else {
                g.drawImage(image3, bomb.getX(), bomb.getY(), 40, 60, this);
            }
            //每绘制一次,生命值减一
            bomb.HpDown();
            //最后不要忘了移除集合中的炸弹
            if (bomb.getLife() == 0) {
                bombs.remove(bomb);
            }
        }

5. 敌方坦克自由移动

  1. 将敌方坦克当做线程使用,需要让敌方坦克类继承Runnable接口实现线程,在run方法里实现坦克的自由移动;
@Override
    public void run() {
        while (true) {
            double random = Math.random();
            long beginTime = System.currentTimeMillis();
            while (true) {
                if (random < 0.25) {
                    moveUp();
                    setDirect(0);
                } else if (random >= 0.25 && random < 0.5) {
                    moveRight();
                    setDirect(1);
                } else if (random >= 0.5 && random < 0.75) {
                    moveDown();
                    setDirect(2);
                } else {
                    moveLeft();
                    setDirect(3);
                }
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                long nowTime = System.currentTimeMillis();
                if (nowTime - beginTime > 2000) {
                    break;
                }
                if (!(isLive == true)) {
                    break;
                }
            }
            if (!(isLive == true)) {
                break;
            }
        }
    }

改进后:

@Override
    public void run() {
        while (true) {
            switch (getDirect()) {
                case 0:
                    for (int i = 0; i < 30; i++) {
                        moveUp();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1:
                    for (int i = 0; i < 30; i++) {
                        moveRight();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2:
                    for (int i = 0; i < 30; i++) {
                        moveDown();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3:
                    for (int i = 0; i < 30; i++) {
                        moveLeft();
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
            int random = (int) (Math.random() * 4);
            setDirect(random);
            if (!(isLive == true)) {
                break;
            }
        }
    }
  1. 在创建敌人坦克对象时启动线程;
    在这里插入图片描述

6.移动范围问题

我方坦克规定移动范围

if (e.getKeyCode() == KeyEvent.VK_W) {
            if (hero.getY() > 0) {
                hero.moveUp();
                hero.setDirect(0);
            }
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            if (hero.getX() + 60 < 1000) {
                hero.moveRight();
                hero.setDirect(1);
            }
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            if (hero.getY() + 60 < 600) {
                hero.moveDown();
                hero.setDirect(2);
            }
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            if (hero.getX() > 0) {
                hero.moveLeft();
                hero.setDirect(3);
            }
        }

敌方坦克规定移动范围

switch (getDirect()) {
                case 0://for (int i = 0; i < 30; i++) {
                        if (getY() > 0) {
                            moveUp();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1://for (int i = 0; i < 30; i++) {
                        if (getX()+60 < 1000) {
                            moveRight();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2://for (int i = 0; i < 30; i++) {
                        if (getY()+60 < 600) {
                            moveDown();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3://for (int i = 0; i < 30; i++) {
                        if (getX() > 0) {
                            moveLeft();
                        }
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }

7. 我方坦克发射多颗子弹

  1. 需求一:我方坦克发射的子弹消亡后,才可以发射新的子弹;

思路:(1)在按下J键时,我们判断当前hero对象的子弹,是否已经销毁;
(2)如果没有销毁,就不去触发shotBullt()方法;
(3)如果已经销毁,才去触发shotBullt()方法;
在这里插入图片描述
发现的第一颗子弹射出边界销毁后,第二颗子弹发射不出来;
改进:
在这里插入图片描述

扩展:我方坦克发射多颗子弹
在这里插入图片描述
在这里插入图片描述
在绘制我方子弹时,需要遍历hero的bullets子弹集合
在这里插入图片描述
or(不清楚为什么要判断非空)
在这里插入图片描述
keyPress中条件去掉
在这里插入图片描述
如何控制面板上同时只能出现5颗子弹
在这里插入图片描述
or
在这里插入图片描述
当我方坦克可以发射多颗子弹时,在判断我方子弹是否命中敌方坦克时,需要拿出我方子弹集合中的每一颗子弹,和敌方坦克集合中的每一辆坦克进行判断;然后在MyPanel类的run方法中调用即可;
在这里插入图片描述

8. 敌方发射的子弹消亡后可以再发射子弹

在地方坦克类中创建这个方法

public void createShot() {
        switch (getDirect()) {
            case 0://上
                bullet = new Bullet(getX() + 20, getY(), 0);
                break;
            case 1://右
                bullet = new Bullet(getX() + 60, getY() + 20, 1);
                break;
            case 2://下
                bullet = new Bullet(getX() + 20, getY() + 60, 2);
                break;
            case 3://:
                bullet = new Bullet(getX(), getY() + 20, 3);
                break;
        }
        bullets.add(bullet);
        new Thread(bullet).start();
    }

方案一:可以在paint()方法中,在移除被销毁的子弹时调用createShot()方法,给这辆坦克再赋予一颗子弹;(老师没有使用)
在这里插入图片描述
方案二:在敌人坦克类的run方法里,当子弹集合里没有子弹时,创建子弹;在这里,当坦克掉头后才能发射子弹;
在这里插入图片描述
在这里插入图片描述

9. 我方坦克爆炸

需求:当敌方坦克击中我方坦克时,我方坦克消失,并出现爆炸效果;

  1. 在父类坦克中添加isLive属性;
    在这里插入图片描述
  2. 编写一个方法,判断敌人坦克是否击中我方坦克;
    在这里插入图片描述
  3. 将hitTank()方法的参数修改如下,造成多态效果(可以实现重载,这里使用多态)在这里插入图片描述
  4. 在绘制我方坦克时,加上判断条件;
    在这里插入图片描述
    5.hero的方法中加上一个判断条件 ! isLive,以做到当我方坦克销毁后,不能再发射子弹在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/57923.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

redis 集群搭建的三种方式

文章目录一、Redis主从二、Redis哨兵三、Redis集群一、Redis主从 二、Redis哨兵 三、Redis集群 下载redis wget http://download.redis.io/releases/redis-5.0.3.tar.gz解压redis tar zxvf redis-5.0.3.tar.gz进行重命名 mv redis-5.0.3 redis安装gcc yum install gcc进入red…

如何学习一门技术

如何学习一门技术 同样的生活&#xff0c;在你经历了一些意外和不如意之后&#xff0c;你再回过头来看&#xff0c;之前你所抱怨的生活其实是一个蛮不错的生活。 罗翔&#xff1a;每一个人都应该拥有学习的能力和权力&#xff0c;真正的知识要能走出书斋&#xff0c;去影响每…

StarkNet 性能路线图

目录 前言 区块限制&#xff1a;Validity Rollups vs L1 为什么 L1 吞吐量有限&#xff1f; 为什么相同的障碍不影响validity rollups&#xff1f; Sequencer 并行化 Cairo-VM 的新 Rust 实现 Rust 对 sequencer重新实现 Provers呢&#xff1f; Summary 参考 前言 St…

详解设计模式:迭代器模式

迭代器模式&#xff08;Iterator Pattern&#xff09;也被称为游标模式&#xff08;Cursor Pattern&#xff09;&#xff0c;是在 GoF 23 种设计模式中定义了的行为型模式。是一种最简单也最常见的设计模式。 迭代器模式 可以让用户透过特定的接口巡访容器中的每一个元素而不用…

HTML5期末大作业:美食网页主题网站设计与实现——HTML+CSS+JavaScript月饼美食食品企业网站html模板9页面

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…

[附源码]计算机毕业设计校园运动会管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Linux基本工具——vim

Linux编辑器vim什么是vimvim的三种常用模式vim的基本操作命令模式插入模式底行模式搭配vim环境sudo怎么才能让普通用户使用什么是vim vim是linux下一款功能强大&#xff0c;多模式的编辑器。 现阶段有13种模式。 这就是进入vim的方式。 vim的三种常用模式 命令模式 我们第一…

CentOS7 编译安装最新的Linux Kernel 6.0 rc3

哪个男孩不想手动编译一份自己的内核呢&#xff1f;安装编译环境 CentOS7安装必要的包 yum groupinstall "Development Tools" -y && yum install openssl-devel -y && yum install rpm-build redhat-rpm-config asciidoc hmaccalc perl-ExtUtils-…

Java核心技术卷Ⅰ-第四章对象和类

重点 1.使用预定义类 2.方法参数 3.对象构造 4.包 5.类设计技巧 1.使用预定义类 一个源文件只能有一个公共类&#xff0c;可以有任意数目的非公共类可以使用通配符调用Java编译器&#xff1a;javac Test*.java使用var声明局部变量就不用担心0、0L和0.0之间的区别&#xff0c;因…

找出链表中间结点的三种解法

初阶链表刷题注意&#xff01;&#xff01;&#xff01;学习的是解题的思维&#xff01; 找出链表的中间结点&#xff08;链接在末尾&#xff09; 解题思路 数组解法 由于链表不能通过下标访问对应的结点&#xff0c;所以我们将所有的结点存储在数组中&#xff0c;这样就可以通…

测试开发怎么学?

随着互联网行业的高速发展,快速高质量的产品版本迭代成为企业始终立于不败之地的迫切需求,而在短期迭代的快节奏中.传统测试工作面对更大压力,无法持续提供高效率高质量的人力支撑&#xff0c;所以越来越多的企业需要技术更为全面的测试开发工程师。 测试开发本质上属于测试,区…

【数据结构】二叉树链式结构的实现

&#x1f451;作者主页&#xff1a;进击的安度因 &#x1f3e0;学习社区&#xff1a;进击的安度因&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 文章目录一、二叉树的链式存储二、二叉树链式结构的实现结构设计手动构建二叉树前序遍历中序遍历…

游戏思考26:游戏服务器压力测试文档(最近在忙这个,这个会更新频繁,12/03未完待续)

文章目录一、压力测试关注点二、计算最耗时的加载操作1&#xff09;从数据库读取数据&#xff0c;对加载的类型进一步划分各种类型&#xff0c;计算最耗时操作2&#xff09;查看CPU随着在线人数的变化所占百分比3&#xff09;查看内存变化4&#xff09;备注一、压力测试关注点 …

【C语言字符串】一道题检验你的字符串学习情况

作者&#xff1a;匿名者Unit 目录 一.字符串引言1.字符串基础二.洛谷P5734详解1.字符串相关库函数&#xff08;1&#xff09; strcpy函数 &#xff08;2&#xff09; strcat函数 &#xff08;3&#xff09;strstr函数 2.题目讲解一.字符串引言 1.字符串基础 字符串通常以\0作为…

008. 子集

1.题目链接&#xff1a; 78. 子集 2.解题思路&#xff1a; 2.1.题目要求&#xff1a; 给一个元素各不相同的数组 nums&#xff0c;返回各种可能的子集&#xff08;子集不能重复&#xff09; 比如&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[],[1],[2],[…

做了8年前端,感谢那些优秀的后端,陪伴我工作,教会我成长

☆ 前段时间由于一时的头脑发热&#xff0c;写了一篇《做了8年前端&#xff0c;细说那些曾经让你浴霸不能的后端》的博客&#xff0c;虽然每个细节也都属实吧&#xff0c;但始终是一些负能量的东西&#xff0c;建议大家不要去看了&#xff0c;今年互联网情况已经这样了&#xf…

安卓APP源码和设计报告——体育馆预约系统

项目名称&#xff1a;体育馆体育场预约系统专业&#xff1a;班级&#xff1a;学号&#xff1a;姓名&#xff1a; 目 录 一、项目功能介绍3 二、项目运行环境3 1、开发环境3 2、运行环境3 3、是否需要联网3 三、项目配置文件及工程结构4 1、工程配置文件4 2、工程结构目…

磨金石教育摄影技能干货分享|上海随手拍——叶落满街,秋意未尽

步入十二月以来&#xff0c;气温也随之骤降&#xff0c;这时候才明显感到初冬已至。冬天的寒风就是最好的脱叶剂&#xff0c;走在街道上&#xff0c;抬眼望去两旁的树木多数已经稀疏。只有残留的绿意还在迎着微微的寒风摇动。 我最喜欢的是秋天&#xff0c;因为秋天的草木最有色…

基于keras与tensorflow手工实现ResNet50网络

前言 在文章 基于tensorflow的ResNet50V2网络识别动物&#xff0c;我们使用了keras已经提供的神经网络&#xff0c;完成了图像分类的。这个时候&#xff0c;小明同学就问了&#xff0c;那么我怎么自己去写一个神经网络来进行训练呢&#xff1f; 本文就基于tensorflow&#xff…

Redis原理篇——五种基本数据类型

一、Redis底层数据结构 1. SDS 获取字符串长度耗时&#xff1a; 由于Redis底层是C语言编写的&#xff0c;C语言中没有字符串这个概念&#xff0c;本质上都是字符数组&#xff0c;获取字符串长度就是遍历数组获取长度&#xff08;遍历到 \0结束标识结束 &#xff09;时间复杂度…