当前位置: 首页 > 面试题库 >

Java 2D游戏:repaint(); 使窗口变灰

奚晟
2023-03-14
问题内容

我正在尝试用Java制作2D游戏,但是当我在线程中调用repaint()方法时,会有一个奇怪的仅灰色窗口。

这是我到目前为止的源代码:

  1. Spaceshooter.java
        package spaceshooter;

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    public class Spaceshooter extends JFrame implements KeyListener, Runnable {

    private Player player = new Player(5, 186, this);
    private boolean up, down;

    public Spaceshooter(String title) {
        super(title);
        this.setFocusable(true);
        this.addKeyListener(this);
    }

    @Override
    public void paint(Graphics gr) {
        super.paint(gr);

        gr.setColor(Color.BLACK);
        gr.fillRect(0, 0, 800, 500);

        player.paintPlayer(gr);
    }

    public static void main(String[] args) {
        Spaceshooter shooter = new Spaceshooter("Spaceshooter");
        new Thread(shooter).start();
        shooter.setSize(800,500);
        shooter.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        shooter.setVisible(true);
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == 38) {
            up = true;
            down = false;
        } else if (e.getKeyCode() == 40) {
            down = true;
            up = false;
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
        down = false;
        up = false;
    }

    @Override
    public void run() {
        while(true) {
            if (up) {
                player.moveUp();
            } else if (down) {
                player.moveDown();
            }
            repaint();
        try {
            Thread.sleep(20);
        } catch (InterruptedException ex) {
            Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
        }
        }
    }
    }
  1. 播放器
         package spaceshooter;

     import java.awt.Component;
     import java.awt.Graphics;
     import java.awt.Toolkit;

     public class Player {

    private int x, y;
    private Component comp;

    public Player(int x, int y, Component comp) {
        this.x = x;
        this.y = y;
        this.comp = comp;
    }

    public void moveUp() {
        y -= 5;
    }

    public void moveDown() {
        y += 5;
    }

    public void paintPlayer(Graphics gr) {
        gr.drawImage(Toolkit.getDefaultToolkit().getImage("images/player.png"), x, y, comp);
    }

    }

预先感谢您的答复!


问题答案:

什么是EDT?

Swing事件处理代码在称为事件调度线程的特殊线程上运行。大多数调用Swing方法的代码也都在此线程上运行。这是必需的,因为大多数Swing对象方法都不是“线程安全的”。所有与GUI相关的任务,必须在EDT上进行绘画过程的同时对GUI进行任何更新,这涉及将请求包装在事件中并将其处理到上EventQueue。然后,将事件从队列中的同一队列中逐一分派,即先入先出。就是说,如果也就是说,如果
Event A排队到EventQueue之前,Event B则事件B将不会在事件A之前分派。

执行的所有任务可能需要一段时间,可能会阻塞EDT,不会发生调度,不会进行任何更新,因此您的应用程序将冻结。您将不得不杀死它以摆脱这种冻结状态。

在您的程序中,除了创建您的代码JFrame并使之从主线程可见(我们也不应该这样做):

    while(true) {
        if (up) {
            player.moveUp();
        } else if (down) {
            player.moveDown();
        }
        repaint();
    try {
        Thread.sleep(20);
    } catch (InterruptedException ex) {
        Logger.getLogger(Spaceshooter.class.getName()).log(Level.SEVERE, null, ex);
    }
    }

您正在repaint()从该线程发布请求,该线程紧随其后发送到睡眠状态。

SwingUtilities.invokeLater():

Swing提供了一个很好的功能SwingUtilities.invokeLater(new Runnable(){})repaint可以将请求发布到EDT。您要做的就是写:

    SwingUtilities.invokeLater(new Runnable()
       {
         public void run()
         {
               repaint();
         }
   });

现在要提些其他事情:

  1. 我们不应该使用来实现GUI组件Runnable。创建另一个类Runnable,在其中进行计算,然后用于SwingUtilities发布组件更新请求。
  2. 我们不应该JFrame直接在其上进行定制绘画。JFrame是顶级组件。它更像是一个包含整个应用程序的容器。如果要自定义绘画,请使用自定义组件MyCanvas extends JComponent
  3. 我们不应该覆盖paint()功能。相反,paintComponent(g)它将很好地满足我们自定义绘画的目的。
  4. 学习将Swing Timer类用于及时重复的GUI渲染任务。

教程资源和参考:

  1. 事件调度线程
  2. 事件队列
  3. 如何使用摇摆计时器
  4. 课程:执行自定义绘画


 类似资料:
  • 我正在写一个简单的mandelbrot渲染器。在程序计算并绘制所有内容后,它将保存曲面并进行blits。这很好,但在我添加了更新显示功能后,它只显示一个黑屏。我需要更新每次迭代,因为我将添加一些文本和其他内容。我只包括了节目的重要部分。有人知道为什么会这样吗?

  • 我正在开发一个基于2D图块的游戏,其中所有图块旋转45度。 我想这样做: 原因是为了在2D游戏中创建不同的视角。 有相当多的基于瓷砖的游戏教程,但我不知道有像这样的旋转瓷砖。 所以我的问题是:如何创建旋转45度的瓷砖?我会感谢任何代码或链接。 编辑:忘了说我用的是Java Graphics2D,不是OpenGL

  • 我正在用pyplay写一个蛇游戏,但我只是提示一个黑色的窗口没有任何错误。 我检查了类似的问题,他们错误地使用了screen对象。然而,这里的情况似乎不是这样,因为它只创建了一次? 知道我做错了什么吗?因为我没有收到任何错误信息,所以我不明白为什么只显示一个黑屏。按键输入事件工作正常。

  • 卡洛斯·巴罗斯 编辑:也许我应该这么说,但这个游戏是一个类似反击或英雄联盟的游戏,我没有做这个游戏。

  • 我试图允许调整这个应用程序的大小,我把可调整大小的标志,但当我试图调整大小,它搞砸了!试试我的代码。 这是一个网格程序,当窗口调整大小时,我希望网格也调整大小/缩小。 请告诉我怎么了,谢谢。