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

Swing中无需转换组件坐标实现2D游戏相机的正确方法

解决Swing 2D游戏相机的坐标转换与重绘问题

问题根源

你遇到的核心问题是:Swing组件的布局坐标系与绘制坐标系是分离的。Graphics.translate仅修改绘制时的坐标原点,但组件的setBounds是基于父容器的组件坐标系计算的,两者没有关联。这就导致:

  • 组件会被绘制在两个位置:原始布局位置(未translate时的默认绘制)和偏移后的位置(translate后的绘制)
  • 重绘时旧位置的内容无法被正确清除,因为组件的布局边界没有更新,Swing只会重绘组件当前的布局区域

正确实现方式

方案1:自定义绘制所有游戏元素(推荐)

放弃使用Swing组件(如JLabel)作为游戏元素,所有内容通过paintComponent直接绘制,完全掌控坐标转换逻辑。这种方式没有Swing布局系统的干扰,适合2D游戏场景。

修改后的Panel.java代码:

package regexlang;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

import javax.swing.JPanel;
import javax.swing.Timer;

@SuppressWarnings("serial")
public class Panel extends JPanel implements ActionListener, KeyListener{
    private int cameraX;
    private int cameraY;
    // 存储游戏元素的世界坐标
    private final int textWorldX = 700;
    private final int textWorldY = 0;
    private Color textBgColor;
    
    public Panel() {
        setBackground(Color.black);
        addKeyListener(this);
        setFocusable(true);
        
        Random random = new Random();
        textBgColor = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
        
        Timer timer = new Timer(1000, this);
        timer.setRepeats(true);
        timer.start();
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 应用相机偏移:将世界坐标转换为面板可视坐标
        g.translate(-cameraX, -cameraY);
        
        // 绘制游戏元素(使用世界坐标)
        g.setColor(textBgColor);
        g.fillRect(textWorldX, textWorldY, 100, 100);
        g.setColor(Color.WHITE);
        g.drawString("hola mundo", textWorldX + 10, textWorldY + 50);
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        Random random = new Random();
        textBgColor = new Color(random.nextInt(256), random.nextInt(256), random.nextInt(256));
        repaint();
    }

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {
        cameraX += 1;
        repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {}   
}

方案2:适配Swing组件的坐标转换(适合必须使用组件的场景)

如果一定要保留Swing组件,需要将组件的世界坐标实时转换为父容器的组件坐标,同时配合绘制偏移:

  1. 维护相机偏移量,在相机移动时更新组件的位置:
// 相机移动时,将组件的世界坐标转换为面板相对坐标
label.setBounds(textWorldX - cameraX, textWorldY - cameraY, 100, 100);
  1. 修正paintComponents方法,确保绘制时的坐标一致性:
@Override
protected void paintComponents(Graphics g) {
    // 拷贝Graphics对象,避免影响后续绘制
    Graphics gCopy = g.create();
    try {
        gCopy.translate(-cameraX, -cameraY);
        super.paintComponents(gCopy);
    } finally {
        gCopy.dispose();
    }
}
  1. 重绘时指定更大的区域,确保旧位置被清除:
@Override
public void repaint() {
    repaint(0, 0, getWidth() + Math.abs(cameraX), getHeight() + Math.abs(cameraY));
}

这种方式需要在相机移动时同步更新所有组件的位置,适合组件数量较少的场景。


内容的提问来源于stack exchange,提问作者Dante S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 16:25:25