You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何单独旋转单个坦克而非全部?GUI坦克项目代码优化及结构疑问

坦克GUI开发问题解答

问题描述

  • 如何实现单个坦克独立旋转,而非两个坦克一起旋转?
  • 现有代码冗余较多,需要优化方案。
  • 当前在Tank类中调用KeyListeners,是否应该移到Board类的doDrawing方法中?

现有代码

private void doDrawing(Graphics g)
{
    final double rads = Math.toRadians(120);
    final double sin = Math.abs(Math.sin(rads));
    final double cos = Math.abs(Math.cos(rads));
    final int w = (int) Math.floor(tank1.getX() * cos + tank1.getX() * sin);
    final int h = (int) Math.floor(tank1.getY() * cos + tank1.getY() * sin);

    Graphics2D g2d = (Graphics2D) g;
    g2d.translate(w, h);
    g2d.rotate(rot, tank1.getX(), tank1.getY());

    AffineTransform backup = g2d.getTransform();
    AffineTransform trans = new AffineTransform();

    g2d.setTransform(backup);
    //g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);

    trans.setToIdentity();
    trans.rotate(rot, h, w);
    trans.translate(h, w);
    trans.setTransform(backup);

    g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);

    //g2d.drawImage(tank1.getImage(), tank1.getX(), tank1.getY(), this);
    g2d.drawImage(tank2.getImage(), tank2.getX(), tank2.getY(), this);

    List<Missile> missiles = tank1.getMissiles();

    for (Missile missile : missiles)
    {
        //trans.rotate(Math.toRadians(rads), w/2, h/2);
        g2d.drawImage(missile.getImage(), missile.getX(), missile.getY() - 7, this);
        //g2d.rotate(rot, missile.getX(), missile.getY() - 7);
    }
}

解决方案

1. 实现单个坦克独立旋转

核心是给每个Tank对象添加专属的旋转角度属性,绘制时对单个坦克做局部变换,绘制后恢复Graphics2D状态,避免影响其他对象:

// 在Tank类中添加旋转角度属性及getter/setter
public class Tank {
    private int x, y;
    private int rotation; // 单位:角度
    private Image image;
    // 其他属性和方法...

    public int getRotation() { return rotation; }
    public void setRotation(int rotation) { this.rotation = rotation; }
    public int getWidth() { return image.getWidth(null); }
    public int getHeight() { return image.getHeight(null); }
}

// 在Board的doDrawing中修改绘制逻辑
private void doDrawing(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;

    // 绘制tank1
    AffineTransform original = g2d.getTransform();
    // 平移到坦克中心,旋转后再平移回去,保证旋转中心正确
    g2d.translate(tank1.getX() + tank1.getWidth()/2, tank1.getY() + tank1.getHeight()/2);
    g2d.rotate(Math.toRadians(tank1.getRotation()));
    g2d.translate(-tank1.getWidth()/2, -tank1.getHeight()/2);
    g2d.drawImage(tank1.getImage(), 0, 0, this);
    // 恢复原变换,不影响后续绘制
    g2d.setTransform(original);

    // 绘制tank2,使用自身的rotation属性
    original = g2d.getTransform();
    g2d.translate(tank2.getX() + tank2.getWidth()/2, tank2.getY() + tank2.getHeight()/2);
    g2d.rotate(Math.toRadians(tank2.getRotation()));
    g2d.translate(-tank2.getWidth()/2, -tank2.getHeight()/2);
    g2d.drawImage(tank2.getImage(), 0, 0, this);
    g2d.setTransform(original);

    // 绘制导弹
    for (Missile missile : tank1.getMissiles()) {
        g2d.drawImage(missile.getImage(), missile.getX(), missile.getY() - 7, this);
    }
}

2. 代码优化

  • 职责分离:把坦克的绘制逻辑封装到Tank类的draw(Graphics2D g2d)方法中,Board只负责遍历调用,减少冗余:
    // Tank类中添加draw方法
    public void draw(Graphics2D g2d) {
        AffineTransform original = g2d.getTransform();
        g2d.translate(x + getWidth()/2, y + getHeight()/2);
        g2d.rotate(Math.toRadians(rotation));
        g2d.translate(-getWidth()/2, -getHeight()/2);
        g2d.drawImage(image, 0, 0, null);
        g2d.setTransform(original);
    }
    
    // Board的doDrawing简化为:
    private void doDrawing(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        tank1.draw(g2d);
        tank2.draw(g2d);
        for (Missile missile : tank1.getMissiles()) {
            g2d.drawImage(missile.getImage(), missile.getX(), missile.getY() -7, this);
        }
    }
    
  • 移除代码中无用的变量(比如原代码中的rads、sin、cos、w、h以及无效的trans操作)和注释代码,保持代码整洁。

3. KeyListeners的位置

绝对不要把KeyListeners放在doDrawing方法里!输入事件处理和绘制逻辑要分离:

  • 将KeyListener添加到Board类中,因为Board是GUI容器,负责接收用户输入。
  • 在KeyListener的回调方法中,根据按键更新对应坦克的位置或旋转角度,比如:
    // Board类中添加KeyListener
    public class Board extends JPanel implements KeyListener {
        private Tank tank1, tank2;
    
        public Board() {
            addKeyListener(this);
            setFocusable(true);
            // 初始化坦克...
        }
    
        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            // 控制tank1旋转
            if (key == KeyEvent.VK_A) {
                tank1.setRotation(tank1.getRotation() - 5);
            }
            if (key == KeyEvent.VK_D) {
                tank1.setRotation(tank1.getRotation() + 5);
            }
            // 控制tank2移动/旋转...
            repaint(); // 触发重绘
        }
    
        // 实现keyReleased、keyTyped方法(空实现即可)
        @Override
        public void keyReleased(KeyEvent e) {}
        @Override
        public void keyTyped(KeyEvent e) {}
    }
    

内容的提问来源于stack exchange,提问作者Aytida

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 22:45:29