如何使用 'repaint()' 正确更新对象在 JPanel 中的位置?

How do I correctly update an object's location in JPanel using 'repaint()'?

我目前正在使用 Java 开发我的第一款游戏。我在石块网格中有一个红色方块(玩家)。游戏看起来像这样: https://i.stack.imgur.com/P6qSE.png

完成此操作后,我开始尝试 KeyListener。我还有一个 Player class 带有一些变量和一个 paint 函数。这是 Player class:

import java.awt.Color;
import java.awt.Graphics;

public class Player {
    int width, height;
    int x = 300;
    int y = 300;
    
    public Player() {
        width = 25;
        height = 25;
    }
    
    public void drawPlayer(Graphics g) {
        g.setColor(Color.red);
        g.fillRect(x, y, width, height);
    }
}

这里是 KeyListener:

public void keyPressed(KeyEvent e) {
    if(e.getKeyCode() == KeyEvent.VK_W) {
        player.y += 25;
        repaint();
    } else if(e.getKeyCode() == KeyEvent.VK_A) {
        player.x -= 25;
        repaint();
    } else if(e.getKeyCode() == KeyEvent.VK_S) {
        player.y -= 25;
        repaint();
    } else if(e.getKeyCode() == KeyEvent.VK_D) {
        player.x += 25;
        repaint();
    }
    
    System.out.println(player.x + ", " + player.y);
    
}

正如您在 keyPressed 函数底部看到的那样,我添加了:System.out.println(player.x + ", " + player.y); 以确保 KeyListener 运行良好(确实如此)。

现在,由于一切正常,最后一步是重新绘制 JPanel 以更新玩家位置。 这是代码:

@Override
public void paint(Graphics g) {
    super.paint(g);
    
    stone.drawStone(g);
    player.drawPlayer(g);
    
    g.setColor(Color.gray);
    for (int j = 0; j < Display.WIDTH; j += tileSize) {
        for (int i = 0; i < Display.HEIGHT; i += tileSize) {
            g.drawRect(i, j, tileSize, tileSize);
        }
    }
}

理论上,我的玩家应该更新位置(因为我已经在 KeyListener 中做了 repaint()),但事实并非如此。我的播放器不动。这会是什么?我猜它与重新绘制 JPanel 有关,但我不知道如何解决这个问题。 你有什么建议吗?提前致谢。

旁注:我有 4 个主要 classes,Player,Game(这里是 95% 的代码来源),Display (JFrame) 和 Main(绘制 JFrame 并启动 gameloop(但是我目前没有使用游戏循环))。

正如我在评论中所建议的那样,您正在到处创建 classes 的多个实例。

您的相框 class 有很多问题:

public class Frame extends JFrame {
private static Game game = new Game();

    public Frame() {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(640, 640));
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setResizable(false);
        frame.setVisible(true);
        frame.addKeyListener(new Game());
        frame.setFocusable(true);
        frame.setContentPane(game);
    }
  1. 不要使用静态变量。请注意您如何为“游戏”设置静态变量。您将 KeyListener 添加到“游戏”的第二个实例 class。所以你的 KeyListener 什么都不做,因为 Game class 的那个实例永远不会添加到框架中。

  2. 框架需要 non-resizable 在你 pack() 框架之前。

  3. 在 pack() 框架之前需要将组件添加到框架中。

  4. 不要在框架上设置首选尺寸。您的游戏 class 应该覆盖 getPreferredSize() 方法以 return 面板的大小。这个 class 负责绘画,所以只有它知道它喜欢的尺寸应该是多少。框架也有装饰(标题栏和边框),所以你的游戏面板不会像你期望的那样是 640x640。

Frame class 的工作应该是简单地创建 JFrame 并将 Game 面板添加到框架中。所有其他游戏逻辑将包含在游戏和您的其他 classes 中。您的 Game class 在 class.

的构造函数中将 KeyListener 添加到自身

查看:get width and height of JPanel outside of the class 此设计结构的示例。它基本上可以满足您的需求,只是没有 KeyListener 代码。