坦克大战初步框架

本文介绍了如何使用Java进行图形绘制和事件处理,构建坦克大战游戏的基础框架。首先讲解了Java绘图坐标体系,包括坐标系、像素概念以及绘图入门。接着详细阐述了Java的事件处理机制,包括键盘控制小球移动的基本原理。最后,文章展示了如何让坦克在屏幕上动起来,为坦克大战游戏的1.0版打下基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

坦克大战初步框架

1. java绘图坐标体系

1.1 坐标体系介绍

下图说明了java坐标系。坐标原点位于左上角,以像素为单位。在java坐标系中,第一个是x坐标,便是当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。

1.2 坐标体系–像素

  1. 绘图还必须要搞清一个非常重要的概念–像素一个像素等于多少厘米?
  2. 计算机在屏幕上显示的内容都是由屏幕上的每一个像素组成的。例如,计算机显示器的分辨率是800x600,表示计算机屏幕上的每一行由800个点组成,共有600行整个计算机屏幕共有480 000个像素。
  3. 所以像素是一个密度单位,而厘米是长度单位,两者无法比较。

1.3 绘图快速入门

  1. 在面板上画一个小圆
  2. 代码如下
//如何在面板上画圆形
//JFrame对应窗口 可以理解成一个画框
public class DrawCircle extends JFrame {
    //定义一个面板
    private MyPanel mp = null;

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

    public DrawCircle() {
        //初始化面板
        mp = new MyPanel();
        //把面板放入到窗口
        this.add(mp);
        //设置窗口大小
        this.setSize(400, 300);
        //当点击窗口的 × , 程序完全退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //可以显示
        this.setVisible(true);
    }
}

//1. 先定义一个MyPanel,继承JPanel类,画图形就在面板上画
//panle 面板
class MyPanel extends JPanel {

    //1. MyPanel 对象是一个画板
    //2. Graphics g 把 g 理解成一支画笔
    //3. Graphics 提供了很多画图的方法
    @Override
    public void paint(Graphics g) {//绘图方法
        super.paint(g);//调用父类的方法完成初始化
        //调式 paint 方法被调用的情况
        System.out.println("paint方法被调用");
        //设置圆左上角的坐标(10,10),
        //圆的宽度为100 ,圆的高度为 100
        g.drawOval(10, 10, 100, 100);
    }
}
  1. 运行后生成界面如下

1.4 绘图原理

  1. Component类提供了两个和绘图相关最重要的方法:
    1. paint(Graphicsg)绘制组件的外观
    2. repaint()刷新组件的外观。
  2. 当组件第一次在屏幕显示的时候,程序会自动的调用paint()方法来绘制组件
  3. 在以下情况paint()将会被调用:
  4. 窗口最小化,再最大化
  5. 窗口的大小发生变化
  6. repaint方法被调用

1.5 Graphics类

  1. Graphics类可以理解为画笔,为我们提供了各种绘制图形的方法:[参考jdk帮助文档]
    1. 画直线 drawLine(int x1,int y1,int x2,int y2)
    2. 画矩形边框 drawRect(int xint yint widthint height)
    3. 画椭圆边框 drawOval(int xint yint widthint height)
    4. 填充矩形 fillRect(intxint yint width,int height)
    5. 填充椭圆 fillOval(int xint y int width, int height)
    6. 画图片drawlmage(lmage img, int xint y.)
    7. 画字符串 drawString(String str,int xint y)
    8. 设置画笔的字体 setFont(Font font)
    9. 设置画笔的颜色 setColor(Color c)
  2. 下面简单的进行代码演示
//如何在面板上画圆形
//JFrame对应窗口 可以理解成一个画框
public class DrawCircle extends JFrame {
    //定义一个面板
    private MyPanel mp = null;

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

    public DrawCircle() {
        //初始化面板
        mp = new MyPanel();
        //把面板放入到窗口
        this.add(mp);
        //设置窗口大小
        this.setSize(800, 600);
        //当点击窗口的 × , 程序完全退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //可以显示
        this.setVisible(true);
    }
}

//1. 先定义一个MyPanel,继承JPanel类,画图形就在面板上画
//panle 面板
class MyPanel extends JPanel {

    //1. MyPanel 对象是一个画板
    //2. Graphics g 把 g 理解成一支画笔
    //3. Graphics 提供了很多画图的方法
    @Override
    public void paint(Graphics g) {//绘图方法
        super.paint(g);//调用父类的方法完成初始化
        //调式 paint 方法被调用的情况
        System.out.println("paint方法被调用");
        //设置圆左上角的坐标(10,10),
        //圆的宽度为100 ,圆的高度为 100
        g.drawOval(10, 10, 100, 100);

        //演示各种绘制图形的方法
        //1. 画直线 drawLine(int x1,int y1,int x2,int y2)
        g.drawLine(10, 10, 100, 100);

        //2. 画矩形边框 drawRect(int xint yint widthint height)
        g.drawRect(10, 10, 100, 100);

        //3. 画椭圆边框 drawOval(int xint yint widthint height)
        //4. 填充矩形 fillRect(intxint yint width,int height)
        // 先设置填充的颜色
        g.setColor(Color.yellow);//黄色
        g.fillRect(10, 10, 100, 100);

        //5. 填充椭圆 fillOval(int xint y int width, int height)
        // 先设置填充的颜色
        g.setColor(Color.red);//红色
        g.fillOval(10, 10, 100, 100);

        //6. 画图片drawlmage(lmage img, int xint y.)
        //第一步:获取图片资源  /mht.png 表示在该项目的根目录去获取 mht.png 图片资源
        Image image = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/mht.png"));
        //第二步:进行图片位置,大小设置
        g.drawImage(image, 100, 100, 362, 330, this);

        //7. 画字符串 drawString(String str,int xint y) //就是写字
        //第一步:先设置颜色和字体
        g.setColor(Color.black);
        g.setFont(new Font("宋体", Font.BOLD, 50));
        //第二步:进行写字
        //位置(100,100)为字符串的左下角
        g.drawString("java", 100, 100);

        //8. 设置画笔的字体 setFont(Font font)
        //9. 设置画笔的颜色 setColor(Color c)
    }
}

1.6 坦克初步绘图

  1. 先创建一个父类坦克;
public class Tank {
    private int x;//坦克的横坐标
    private int y;//坦克的纵坐标

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

    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;
    }
}
  1. 然后创建自己的坦克类
public class MyTank extends Tank {
    public MyTank(int x, int y) {
        super(x, y);
    }
}
  1. 游戏面板设置 MyTankGame01类
public class MyTankGame01 extends JFrame {
    //定义 MyPanel
    MyPanel mp = null;

    public static void main(String[] args) {
        MyTankGame01 myTankGame01 = new MyTankGame01();
    }

    public MyTankGame01() {
        //初始化
        mp = new MyPanel();
        //面板(游戏的绘图区域)
        this.add(mp);
        //面板大小
        this.setSize(1000, 750);
        //当点击窗口的 × , 程序完全退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置显示
        this.setVisible(true);
    }
}
  1. 进行坦克的初步绘图
public class MyPanel extends JPanel {
    //定义我的坦克
    MyTank myTank = null;

    public MyPanel() {
        //初始化自己的坦克
        myTank = new MyTank(100, 100);
    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        //设置填充矩形,默认黑色
        g.fillRect(0, 0, 1000, 750);
        
        //我们的坦克:青色
        drawTank(getX() + 100, getY() + 100, g, 0, 0);
        //敌人的坦克:黄色
        drawTank(getX() + 50, getY() + 200, g, 0, 1);
        drawTank(getX() + 150, getY() + 200, g, 0, 1);
    }
    //编写方法,画出坦克

    /**
     * @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://我们的坦克
                g.setColor(Color.cyan);
                break;
            case 1://敌人的坦克
                g.setColor(Color.yellow);
                break;
        }
        //根据坦克的方向,来绘制坦克
        switch (direction) {
            case 0://默认方向向上
                // 先画第一个矩形 大小 10*60
                // 定点(x,y) -> (10,10)
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                // 定点(x+10,y+10) -> (20,20)
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                // 定点(x+30,y) -> (40,10)
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                // 定点(x+10,y+20) -> (20,30)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                // 定点1 (x+20,y) -> (30,10)
                // 定点2 (x+20,y+30) -> (30,40)
                g.drawLine(x + 20, y, x + 20, y + 30);
                break;
            default:
                System.out.println("暂时不作处理");
        }
    }
}
  1. 初步生成的图形:

2. java事件处理机制

2.1 怎么让一个小球通过键盘控制,能上下左右移动;

public class BallMove extends JFrame {
    //演示小球通过键盘控制移动 -> 讲解Java的事件控制
    MyPanel mp = null;

    public static void main(String[] args) {
        BallMove ballMove = new BallMove();
    }

    public BallMove() {
        mp = new MyPanel();
        this.add(mp);
        this.setSize(400, 300);
        //窗口 Jframe 对象可以监听键盘事件
        this.addKeyListener(mp);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

}

//面板,可以画出小球
//KeyListener 是监听器,可以监听键盘操作
class MyPanel extends JPanel implements KeyListener {
    //为了让小球可以移动,我们把他的左上角坐标设置为变量
    //(x,y)
    int x = 10;
    int y = 10;

    @Override
    public void paint(Graphics g) {
        super.paint(g);

        //画出填充的小球,默认黑色
        g.fillOval(x, y, 20, 20);
    }

    //有字符输出时,该方法就会触发
    @Override
    public void keyTyped(KeyEvent e) {

    }

    //当某个键按下,该方法会触发
    @Override
    public void keyPressed(KeyEvent e) {
        //System.out.println((char) e.getKeyCode()+"被按下...");

        //根据用户按下的不同键,来处理小球的移动
        if (e.getKeyCode() == KeyEvent.VK_DOWN) {//向下
            y++;
        } else if (e.getKeyCode() == KeyEvent.VK_UP) {//向上
            y--;
        } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {//向左
            x--;
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {//向右
            x++;
        }

        //让面板重绘
        this.repaint();
    }

    //当某个键释放(松开),该方法会触发
    @Override
    public void keyReleased(KeyEvent e) {

    }
}

2.2 java事件处理机制基本说明

  1. java事件处理是采取"委派事件模型"。
  2. 当事件发生时,产生事件的对象,会把此"信息"传递给"事件的监听者"处理;
  3. 这里所说的"信息"实际上就是java.awt.event事件类库里某个类所创建的对象,把它称为"事件的对象"。
  4. 示意图如下:

2.3 事件处理机制深入理解

  1. 前面我们提到几个重要的概念:事件源、事件、事件监听器;下面来全面的介绍它们:
  2. 事件源:事件源是一个产生事件的对象,比如按钮,窗口等。
  3. 事件:事件就是承载事件源状态改变时的对象,比如当键盘事件、鼠标事件、窗口事件等等,会生成一个事件对象,该对象保存着当前事件很多信息,比如KeyEvent对象含有被按下键的Code值。
  4. java.awt.event包 和javax.swing.event包中定义了各种事件类型;
  5. 事件类型:查阅jdk文档

  1. 事件监听器接口:
    1. 当事件源产生一个事件,可以传送给事件监听者处理
    2. 事件监听者实际上就是一个类,该类实现了某个事件监听器接口
      1. 比如前面我们案例中的MyPanel就是一个类,它实现了KeyListener接口,
      2. 它就可以作为一个事件监听者,对接受到的事件进行处理
    3. 事件监听器接口有多种,不同的事件监听器接口可以监听不同的事件,一个类可以实现多个监听接口
    4. 这些接口在java.awt.event包和javax.swing.event包中定义。
    5. 还有很多常用的事件监听器接口,可以查阅查看jdk 文档;

3. 坦克大战游戏(1.0版)

3.1 让坦克动起来

现在我们学习java事件处理机制和java绘图技术,试试看怎么让坦克通过按键控制上下左右动起来(wsad)。

  1. 初始框架
public class MyTankGame02 extends JFrame {
    //定义 MyPanel
    MyPanel mp = null;

    public static void main(String[] args) {
        MyTankGame02 myTankGame01 = new MyTankGame02();
    }

    public MyTankGame02() {
        //初始化
        mp = new MyPanel();
        //面板(游戏的绘图区域)
        this.add(mp);
        //面板大小
        this.setSize(1000, 750);
        //添加键盘监听
        this.addKeyListener(mp);
        //当点击窗口的 × , 程序完全退出
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //设置显示
        this.setVisible(true);
    }
}
  1. 父类坦克
public class Tank {
    private int x;//坦克的横坐标
    private int y;//坦克的纵坐标

    //坦克的方向 0向上 1向右 2向下 3向左
    private int direction;
    //坦克的速度
    private int speed = 1;

    public int getSpeed() {
        return speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    //添加上下左右移动方法
    //向上
    public void moveUp(){
        y-=speed;
    }
    //向下
    public void moveDown(){
        y+=speed;
    }
    //向左
    public void moveLeft(){
        x-=speed;
    }
    //向右
    public void moveRight(){
        x+=speed;
    }

    public int getDirection() {
        return direction;
    }

    public void setDirection(int direction) {
        this.direction = direction;
    }

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

    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;
    }
}
  1. 自己的坦克
public class MyTank extends Tank {
    public MyTank(int x, int y) {
        super(x, y);
    }
}
  1. 敌人的坦克
public class EnemyTank extends Tank{

    public EnemyTank(int x, int y) {
        super(x, y);
    }
}
  1. 坦克绘图区域
public class MyPanel extends JPanel implements KeyListener {
    //定义我的坦克
    MyTank myTank = null;
    //定义敌人坦克,放入到 Vector 
    Vector<EnemyTank> enemyTanks = new Vector<>();
    int enemyTanksize = 3;

    public MyPanel() {
        //初始化自己的坦克
        myTank = new MyTank(100, 100);
        //初始化敌人的坦克
        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);

        myTank.setSpeed(5);
        //画出坦克-封装方法
        //自己的坦克
        drawTank(myTank.getX(), myTank.getY(), g, myTank.getDirection(), 1);

        //敌人的坦克
        for (int i = 0; i < enemyTanksize; i++) {
            EnemyTank enemyTank = enemyTanks.get(i);
            drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 0);
        }
    }
    //编写方法,画出坦克

    /**
     * @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://敌人的坦克
                g.setColor(Color.cyan);
                break;
            case 1://我们的坦克
                g.setColor(Color.yellow);
                break;
        }
        //根据坦克的方向,来绘制坦克
        switch (direction) {//0向上 1向右 2向下 3向左
            case 0://默认方向向上
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                // 定点(x,y)
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                // 定点(x+10,y+10)
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                // 定点(x+30,y)
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                // 定点(x+10,y+20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                // 定点1 (x+20,y)
                // 定点2 (x+20,y+30)
                g.drawLine(x + 20, y, x + 20, y + 30);
                break;
            case 1://默认方向向右
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x + 60, y + 20, x + 30, y + 20);
                break;
            case 2://默认方向向下
                // 先画第一个矩形 大小 10*60
                //坦克左边轮子
                g.fill3DRect(x, y, 10, 60, false);
                // 第二个矩形 大小 20*40
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 20, 40, false);
                // 第三个矩形 大小 10*60
                //坦克右边轮子
                g.fill3DRect(x + 30, y, 10, 60, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 10, y + 20, 20, 20);
                // 最后的炮管
                g.drawLine(x + 20, y + 60, x + 20, y + 30);
                break;
            case 3://默认方向向左
                // 先画第一个矩形 大小 60*10
                //坦克上边轮子
                g.fill3DRect(x, y, 60, 10, false);
                // 第二个矩形 大小 40*20
                //坦克身体
                g.fill3DRect(x + 10, y + 10, 40, 20, false);
                // 第三个矩形 大小 10*60
                //坦克下边轮子
                g.fill3DRect(x, y + 30, 60, 10, false);
                // 上面的圆盖子 大小 (20,20)
                g.fillOval(x + 20, y + 10, 20, 20);
                // 最后的炮管
                g.drawLine(x, y + 20, x + 30, y + 20);
                break;
            default:
                System.out.println("暂时不作处理");
        }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //处理 wsad 按下的情况
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_W) {
            //改变坦克的方向
            myTank.setDirection(0);
            myTank.moveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_S) {
            myTank.setDirection(2);
            myTank.moveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_A) {
            myTank.setDirection(3);
            myTank.moveLeft();
        } else if (e.getKeyCode() == KeyEvent.VK_D) {
            myTank.setDirection(1);
            myTank.moveRight();
        }
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

}
  1. 运行后生成的界面如下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dominator945

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值