[英]repaint() doesn't work outside of keylistener
在过去一周左右的时间里,我一直在开发一款游戏,其中您是一个白色方块,试图躲避从屏幕顶部掉落的红色方块。 您只能左右移动,只能移动3个位置(左,中,右),并且随着游戏的进行,红色方块开始下降得更快。 当我移动播放器(白色方块)时,一切都可以重新绘制,但是当红色方块的位置更新时,它根本不会重新绘制。 假设红场每秒移动一次; 如果我等待一秒钟,则重新粉刷不会执行任何操作。 但是,如果我等待一秒钟(或任意时间)然后移动播放器,则两个方块会立即跳至新位置。 在看了一个小时的代码之后,我仍然找不到任何问题,而且我对编码还很陌生,这无济于事,这可能意味着这是一个非常明显的错误。
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class boxface extends JComponent implements KeyListener {
private static int x=0, y=0;
static int counter = 0;
static Thread t = new Thread();
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()== KeyEvent.VK_RIGHT)
moveRight();
else if(e.getKeyCode()== KeyEvent.VK_LEFT)
moveLeft(); }
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
static Rectangle player = new Rectangle(x, 400, 50, 50);
Rectangle bg = new Rectangle(0, 0, 700, 750);
static int x2 = ((int)((Math.random()*3)))*50;
static Rectangle enemy = new Rectangle(x2, y, 50, 50);
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLACK);
g2.fill(bg);
g2.setColor(Color.WHITE);
g2.fill(player);
g2.setColor(Color.RED);
g2.fill(enemy); }
public void moveLeft() {
if(x > 0) {
x -= 50;
player.setLocation(x, 400);
repaint();}}
public void moveRight() {
if(x < 100) {
x += 50;
player.setLocation(x, 400);
repaint();} }
public void enemyDown() {
if(enemy.getBounds().y == 400) {
x2=((int)((Math.random()*3)))*50;
y=0;
enemy.setLocation(x2, y);
counter++;
repaint(); }
else {
y = y + 50;
enemy.setLocation(x2, y);
repaint();}}
public boxface(){
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false); }
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setBounds(400, 200, 156, 479);
f.setMinimumSize(new Dimension(156, 0));
f.setResizable(false);
f.getContentPane().add(new boxface());
f.setVisible(true); }
});
boxface exec = new boxface();
int t = 20;
for(long i = 0; i < 21; i++) {
if(i == t) {
exec.enemyDown();
i = 0; }
if(counter == 10) t = 15;
if(counter == 25) t = 10;
if(counter == 50) t = 7;
if(counter == 100) t = 5;
if(counter == 150) t = 3;
if(counter == 225) t = 1;
if(enemy.intersects(player))
break;
System.out.println(i); }
}
}
Swing使用“被动渲染”算法。 这意味着,UI将根据RepaintManager
决定不定期地更新
有关更多详细信息, 请参见在AWT和Swing中绘画
您需要某种方式来安排定期更新。 在我讨论之前,有一些基本的博弈论...
大多数游戏都有“主循环”的概念。 “主循环”执行许多工作,它是:
尽管可以使用Thread
来完成此操作,但Swing并不是线程安全的,这意味着,切勿在事件调度线程的上下文之外更新UI的状态(或UI依赖的任何状态)。 Swing也是单线程的,因此您不能在Event Dispatching Thread的上下文中简单地使用Thread.sleep
或其他循环。
最简单的解决方案是使用Swing Timer
。 这是伪循环,在指定的延迟后重复调用。 每次触发计时器时,您都将执行“主循环”操作并调用repaint
,从而安排对UI的(主要是)定期更新。
有关更多详细信息,请参见如何使用Swing计时器 。
因为游戏状态实际上是在“主循环”中更新的,所以您不能再直接响应按键事件。 相反,您需要设置一系列标志来指示输入触发器的当前状态(即, isAPressed
的最简单形式)。
在大多数情况下,这将调用Set
和预定义的一系列输入。 我喜欢使用一个enum
因为它明确定义了可接受的输入(上/下/左/右等)。 按下后,可以将适当的输入添加到Set
并在释放时将其删除。 它处理任何重复的状态,并消除了第一次按下和重复的按键事件之间的延迟。
谈论关键事件。 KeyListener
存在已知问题,尽管存在“ hacks”来“解决”它,但仅仅是“ hacks”。
监视(有限)键输入的推荐方法是使用键绑定API
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.