栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java项目笔记 - 第18章:坦克大战2.0

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java项目笔记 - 第18章:坦克大战2.0

第18章:坦克大战2.0
  • 总体内容
  • 项目:我方发射子弹
    • 思路
    • 实现步骤
      • 1. 控制子弹发射的线程
      • 2. 创建+启动线程的方法
      • 3. 监听"J"+绘制子弹+子弹连续显示


总体内容
  • 2.0版本包括:实现我方坦克发射子弹的过程
  • 本文中项目完成包括:
    ①增加一个子弹发射的线程类
    ②在我方坦克类中创建+启动发射线程的对象
    ③监听"J"键+绘制子弹+子弹连续显示

项目:我方发射子弹

需求:当用户按下J键,我方坦克就发射一颗子弹

思路


实现步骤 1. 控制子弹发射的线程

shoot类
① 实现Thread类,成为一个线程类
② 分析包括哪些属性
③ 构造器对哪些属性进行初始化
④ run() 方法中:根据子弹方向对子弹坐标进行更改且当子弹碰到墙壁时,线程结束
⑤ 具体实现的分析 看代码里面的注解吧

package com.wpz.tankgame;


public class Shoot extends Thread {
    int x;//子弹的横坐标
    int y;//子弹的纵坐标
    int direction;//子弹的方向
    int speed = 4;//子弹发射的速度
    boolean isLive = true;//子弹是否存在=>线程创建后子弹就存在,所以默认为true

    //构造器:需要这三个属性是因为 这几个需要根据我方坦克的x,y和direction来定
    public Shoot(int x, int y, int direction) {
        this.x = x;
        this.y = y;
        this.direction = direction;
    }

    @Override
    public void run() {
        //该线程的任务是:
        // ①控制子弹的移动(根据子弹的方向去移动)
        // ②控制子弹是否存在
        while (true) {
            try {
                Thread.sleep(50);//休眠一下,不然子弹一下子就打到墙上了
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // - 根据方向改变x,y坐标
            if (direction == 0) {//上
                y -= speed;
            } else if (direction == 1) {//右
                x += speed;
            } else if (direction == 2) {//下
                y += speed;
            } else if (direction == 3) {//左
                x -= speed;
            }
            System.out.println("x = " + x + " y = " + y);//测试
            // - 如果碰到墙壁,则线程结束(break),子弹消失(置false,面板中不绘制子弹)
            // - 把正确条件取反 就得到了它反面的条件
            if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750)) {
                isLive = false;//子弹消失
                System.out.println("线程结束");
                break;//线程结束
            }
        }
    }
}


2. 创建+启动线程的方法

MyTank类
① 要明白为什么要把创建+启动线程的方法写到MyTank类中=>用户控制射击是我方坦克专有的
② 定义线程类的对象=>关联线程类,就可以通过我方坦克访问到子弹线程的信息了
④ 写一个射击的方法,功能包括:创建+启动线程
⑤ 创建线程时:要通过我方坦克的方向和位置 来确定 子弹的方向和位置
⑥ 具体实现的分析 看代码里面的注解吧

package com.wpz.tankgame;


public class MyTank extends Tank {
    Shoot shoot;//子弹发射的线程

    public MyTank(int x, int y) {
        super(x, y);
    }

    public void shootEnemyTank() {//射击
        //创建线程:根据坦克的位置和方向 来创建 子弹射击的线程
        // - 判断坦克的方向,来创建线程
        if (getDirection() == 0) {//上
            shoot = new Shoot(getX() + 20, getY(), 0);
        } else if (getDirection() == 1) {//右
            shoot = new Shoot(getX() + 60, getY() + 20, 1);
        } else if (getDirection() == 2) {//下
            shoot = new Shoot(getX() + 20, getY() + 60, 2);
        } else if (getDirection() == 3) {//左
            shoot = new Shoot(getX(), getY() + 20, 3);
        }
        //启动线程
        shoot.start();
    }
}


3. 监听"J"+绘制子弹+子弹连续显示

MyPanel类,TankGame类
① 监听"J":在keyPressed()方法中修改,如果按下J键,就调用我方坦克发射子弹的方法
② 绘制子弹: 在paint()中修改,如果发射子弹的线程非空并且子弹存在,就绘制子弹,注意一定要验证线程是否为空,如果只判断子弹是否存在,那会报空指针异常
③ 子弹连续显示: MyPanel类实现Runnable接口,在run()中每隔100秒就重绘面板。因为按一次J,KeyPressed()被调用一次,里面的重绘也只调用一次,那么虽然子弹的坐标一直在动,但是面板上不会显示子弹连续变化,所以想到用多线程来解决
④ 在TankGame的构造器中,启动MyPanel的线程
⑤ 具体实现的分析和细节 看代码里面的注解吧

package com.wpz.tankgame;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;


//为了让面板不停重绘子弹,让面板实现Runnable接口,当作一个线程使用(在run()中写重绘)
public class MyPanel extends JPanel implements KeyListener, Runnable {
    MyTank myTank = null;//定义一个自己的坦克
    Vector enemyTanks = new Vector<>();//定义敌人的坦克,放到Vector中
    int enemyTankSize = 3;//敌人坦克的数量

    public MyPanel() {
        myTank = new MyTank(100, 100);//初始化自己的坦克
        myTank.setSpeed(5);//设置坦克移动的速度
        //初始化敌人的坦克(注意:使用循环来添加。因为敌人坦克数量多,不要一个一个add)
        for (int i = 0; i < enemyTankSize; i++) {
            //创建一个敌人的坦克
            EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0);
            //设置方向
            enemyTank.setDirection(2);
            //加入
            enemyTanks.add(enemyTank);
        }
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0, 0, 1000, 750);//绘图区域:填充矩形,默认是黑色
        //画出自己的坦克->封装到画坦克的方法中
        // - 对direction做了修改,把它放到了tank父类中(使用get()方法访问)
        drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 0);//画出我的坦克
        //画敌人的坦克->遍历集合
        for (int i = 0; i < enemyTanks.size(); i++) {
            // - 取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);
            drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
        }
        //画我方坦克的子弹
        if (myTank.shoot != null && myTank.shoot.isLive) {
            g.setColor(Color.white);
            g.fillOval(myTank.shoot.x, myTank.shoot.y, 2, 2);
        }
    }

    
    public void drawTank(int x, int y, Graphics g, int direction, int type) {
        //两种类型的坦克①我方②敌方 -> 不同类型的坦克,颜色不同
        switch (type) {
            case 0://我方
                g.setColor(Color.CYAN);
                break;
            case 1://敌方
                g.setColor(Color.YELLOW);
                break;
        }
        //四种移动方向:不同移动方向使用画笔绘制的坦克是不同的
        //direction:0:向上,1:向右,2:向下,3:向左
        switch (direction) {
            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, x + 20, y + 30);//画坦克的炮筒
                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;
        }
    }

    //事件处理方法(对按键进行监听)
    @Override
    public void keyPressed(KeyEvent e) {
        //判断事件(当按下WDSA键时进行处理)
        if (e.getKeyCode() == KeyEvent.VK_W) {//上
            myTank.moveUp();//改变坦克的坐标(将改变坐标封装到父类的moveUp()方法中)
            myTank.setDirection(0);//改变坦克的方向(将direction作为所有坦克的属性放到父类中)
        } else if (e.getKeyCode() == KeyEvent.VK_D) {//右
            myTank.moveRight();
            myTank.setDirection(1);
        } else if (e.getKeyCode() == KeyEvent.VK_S) {//下
            myTank.moveDown();
            myTank.setDirection(2);
        } else if (e.getKeyCode() == KeyEvent.VK_A) {//左
            myTank.moveLeft();
            myTank.setDirection(3);
        }
        //按下J键时,我方坦克发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J) {
            //- 出现的问题:
            // -- 按一下J,只会调用一次keyPressed(),那面板只会重绘一次
            // -- 因此按一下J,只能看到一个不会动小球
            // -- 解决方法:让面板每隔100ms,自动重绘=>用多线程(面板实现Runnable接口)
            myTank.shootEnemyTank();
        }
        //重绘
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //每隔100ms,重绘面板,使子弹移动
    // - 要while不停的循环这些内容,不然线程只执行一次就退出了
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.repaint();
        }
    }
}

package com.wpz.tankgame;

import javax.swing.*;


public class TankGame extends JFrame {
    private MyPanel mp = null;//定义面板

    public static void main(String[] args) {
        new TankGame();
    }

    public TankGame() {
        this.mp = new MyPanel();
        //启动线程mp的线程:重绘面板
        Thread thread = new Thread(mp);
        thread.start();
        this.add(mp);
        this.setSize(1000, 750);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
        this.addKeyListener(mp);//-------为mp面板添加键盘监听器
    }
}

输出效果:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/847668.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号