如何使用 '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);
}
不要使用静态变量。请注意您如何为“游戏”设置静态变量。您将 KeyListener 添加到“游戏”的第二个实例 class。所以你的 KeyListener 什么都不做,因为 Game class 的那个实例永远不会添加到框架中。
框架需要 non-resizable 在你 pack() 框架之前。
在 pack() 框架之前需要将组件添加到框架中。
不要在框架上设置首选尺寸。您的游戏 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 代码。
我目前正在使用 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);
}
不要使用静态变量。请注意您如何为“游戏”设置静态变量。您将 KeyListener 添加到“游戏”的第二个实例 class。所以你的 KeyListener 什么都不做,因为 Game class 的那个实例永远不会添加到框架中。
框架需要 non-resizable 在你 pack() 框架之前。
在 pack() 框架之前需要将组件添加到框架中。
不要在框架上设置首选尺寸。您的游戏 class 应该覆盖
getPreferredSize()
方法以 return 面板的大小。这个 class 负责绘画,所以只有它知道它喜欢的尺寸应该是多少。框架也有装饰(标题栏和边框),所以你的游戏面板不会像你期望的那样是 640x640。
Frame
class 的工作应该是简单地创建 JFrame
并将 Game
面板添加到框架中。所有其他游戏逻辑将包含在游戏和您的其他 classes 中。您的 Game
class 在 class.
查看:get width and height of JPanel outside of the class 此设计结构的示例。它基本上可以满足您的需求,只是没有 KeyListener 代码。