【问题标题】:repaint() doesn't work outside of keylistenerrepaint() 在 keylistener 之外不起作用
【发布时间】:2018-01-30 04:15:36
【问题描述】:

在过去一周左右的时间里,我一直在开发一款游戏,你是一个试图躲避从屏幕顶部掉落的红色方块的白色方块。您只能左右移动,只有 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); }
  }
}

【问题讨论】:

    标签: java graphics jframe paint drjava


    【解决方案1】:

    Swing 使用“被动渲染”算法。这意味着 UI 将根据 RepaintManager 做出的决定不定期更新

    See Painting in AWT and Swing了解更多详情

    您需要某种方式来安排定期更新。在我讨论之前,一些基本的博弈论......

    游戏/主循环...

    大多数游戏都有“主循环”的概念。 “主循环”做了很多工作,它:

    • 更新游戏状态。这包括:
      • 根据输入的当前状态(键盘/鼠标/操纵杆)更新玩家的位置
      • 更新世界中其他物体/障碍物的位置
      • 碰撞检测
      • 在渲染之前需要更新的其他状态
    • 呈现当前状态

    虽然您可以使用 Thread 来完成此操作,但 Swing 不是线程安全的,这意味着您永远不应在事件调度线程的上下文之外更新 UI 的状态(或 UI 依赖的任何状态) . Swing 也是单线程的,因此您也不能简单地在 Event Dispatching Thread 的上下文中使用 Thread.sleep 或其他一些循环。

    最简单的解决方案是使用 Swing Timer。这是伪循环,在指定的延迟后重复调用。每次触发计时器时,您将执行“主循环”操作并调用repaint,因此,安排(大部分)定期更新 UI。

    详情请见How to use Swing Timers

    其他注意事项...

    因为游戏的状态实际上是在“主循环”内更新的,所以不能再直接响应关键事件。相反,您需要设置一系列标志来指示输入触发器的当前状态(即最简单形式的isAPressed)。

    在大多数情况下,这将调用Set 和预定义的一系列输入。我喜欢使用enum,因为它清楚地定义了接受的输入(上/下/左/右等...)。按下时,将适当的输入添加到 Set 并在释放时将其删除。它处理任何重复的状态,并消除第一次按下和重复按键事件之间延迟的奇怪现象。

    谈论关键事件。 KeyListener 有已知问题,虽然有“黑客”可以“解决”它,但它们就是“黑客”。

    监控(有限)键输入的推荐方法是使用key bindings API

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-08
      • 2015-01-31
      相关资源
      最近更新 更多