簡體   English   中英

repaint()在keylistener之外不起作用

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM