如何单独旋转单个坦克而非全部?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
相关产品推荐
相关产品推荐

