自学内容网 自学内容网

[Java]Swing版坦克大战小游戏项目开发(2)——熟悉paint方法和监听器


highlight: xcode

theme: vuepress

本节概述

在本节,你会了解到 Swing 的 Frame 类的 paint 方法以及键盘监听器。paint 方法可以绘制窗口的内部的图形,通常窗口启动时自动调用。键盘监听器可以监听你按下键盘的事件然后做出一定的处理。

绘制一个矩形

TankFrame 类中,重写 paint 方法:

java @Override public void paint(Graphics g) { g.fillRect(100, 100, 50, 50); }

Graphics 对象是 Swing 库提供的画笔对象,通过这个“画笔”,我们可以绘制图形。g.fillRect(100, 100, 50, 50) 方法表示在 $(100, 100)$ 坐标位置绘制一个长宽均为 $50$ px 矩形。

试着运行一下:

image.png

可以看到窗口的左上方出现了一个矩形黑块。学过笛卡尔坐标系的同学可能有点疑惑,为什么是在左上角而不是左下角?因为通常在 GUI 开发领域中,$(0, 0)$ 坐标是从左上角开始的,用一个简单的图表示一下:

image.png

这就不难理解为什么矩形块出现在左上角了。

定义Tank类与窗口解耦

试想一下,如果全都在 TankFrame 中实现坦克的全部逻辑的话,那么代码必然会变得十分臃肿。所以我们需要把所有关于坦克的逻辑单独封装到一个类中,这个类我们就定义为 Tank

```java package org.codeart;

import java.awt.*;

public class Tank {

// X 轴坐标
private int x;

// Y 轴坐标
private int y;

// 方向的枚举类
private Dir dir;

// 默认的速度 10 像素
private static final int SPEED = 10;

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

public Dir getDir() {
    return dir;
}

public void setDir(Dir dir) {
    this.dir = dir;
}

public void paint(Graphics g) {
    g.fillRect(x, y, 50, 50);
    switch (dir) {
        case LEFT:
            x -= SPEED;
            break;
        case RIGHT:
            x += SPEED;
            break;
        case UP:
            y -= SPEED;
            break;
        case DOWN:
            y += SPEED;
            break;
    }
}

} ```

方向的枚举类 Dir,分别表示左、上、右、下:

```java public enum Dir {

LEFT, UP, RIGHT, DOWN;

} ```

在坦克类中,我们重写了 paint 方法用于实现 TankFrame 类的 paint 方法,这才是真的实现了绘制逻辑的代码。

重构TankFrame类

到这一步为止,坦克的逻辑就已经初步成型了。下面就要重写 TankFrame 的逻辑了,定义 tank 成员变量然后初始化赋值:

```java public class TankFrame extends Frame {

private final Tank tank;

public TankFrame() throws HeadlessException {
    setVisible(true);
    setSize(800, 600);
    setResizable(false);
    setTitle("War of Tank");
    setLocationRelativeTo(null);
    addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
            System.exit(0);
        }
    });

    // 初始化坦克对象
    this.tank = new Tank(0, 0, Dir.DOWN);
}

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

} ```

这样就实现了窗口对象与坦克对象的解耦。

自定义键盘监听器

下面我们需要让窗口监听到键盘事件,这就需要自定义键盘监听器类。定义 IListener 类继承 KeyAdapter 类:

```java // 目前在 TankFrame 中定义键盘监听器内部类,后续再处理耦合问题 private class IListener extends KeyAdapter {

// 是否向左
boolean bL;

// 是否向上
boolean bU;

// 是否向右
boolean bR;

// 是否向下
boolean bD;

// 监听键盘按下的事件
@Override
public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode();
    switch (key) {
        case KeyEvent.VK_LEFT:
            bL = true;
            break;
        case KeyEvent.VK_UP:
            bU = true;
            break;
        case KeyEvent.VK_RIGHT:
            bR = true;
            break;
        case KeyEvent.VK_DOWN:
            bD = true;
            break;
        default:
            break;
    }
    setDir();
}

// 监听键盘释放的事件
@Override
public void keyReleased(KeyEvent e) {
    int key = e.getKeyCode();
    switch (key) {
        case KeyEvent.VK_LEFT:
            bL = false;
            break;
        case KeyEvent.VK_UP:
            bU = false;
            break;
        case KeyEvent.VK_RIGHT:
            bR = false;
            break;
        case KeyEvent.VK_DOWN:
            bD = false;
            break;
        default:
            break;
    }
    setDir();
}

// 重新设置方向
private void setDir() {
    if (bL) {
        tank.setDir(Dir.LEFT);
    }
    if (bU) {
        tank.setDir(Dir.UP);
    }
    if (bR) {
        tank.setDir(Dir.RIGHT);
    }
    if (bD) {
        tank.setDir(Dir.DOWN);
    }
}

} ```

在TankFrame中添加监听器

下面在 TankFrame 中需要添加我们自己定义的监听器:

java public TankFrame() throws HeadlessException { setVisible(true); setSize(800, 600); setResizable(false); setTitle("War of Tank"); setLocationRelativeTo(null); // 添加自定义监听器 addKeyListener(new IListener()); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); this.tank = new Tank(0, 0, Dir.DOWN); }

让坦克动起来

在一切准备就绪之后,我们就可以考虑让坦克动起来了。在一个死循环中每隔 200ms 重绘一次窗口,这样就可以实现动画效果:

```java public class FrameDemo {

public static void main(String[] args) throws InterruptedException {
    TankFrame frame = new TankFrame();
    while (true) {
        Thread.sleep(200);
        frame.repaint();
    }
}

} ```

试着运行一下:

坦克.gif


原文地址:https://blog.csdn.net/weixin_45254062/article/details/140623008

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!