【问题标题】:Animate Maze Solution Painting with Java Graphics2D使用 Java Graphics2D 绘制动画迷宫解决方案
【发布时间】:2014-03-18 05:05:59
【问题描述】:

我正在从事一个计算机科学项目,并且被这个问题困扰了将近 72 小时。我已经谷歌了,重新谷歌了,第三次谷歌了。不知何故,我似乎无法让它工作。基本上,我必须绘制一个迷宫,然后为解决方案路径设置动画。这是我目前的绘画代码,绘制迷宫效果很好,我根本无法让它动画。

private class MazePanel extends JPanel
{
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        MazeApp.g = (Graphics2D) g;
        paintMaze();
    }
}
...
private static Timer timer = new Timer(1000, new ActionListener(){
    private int space = 0;
    @Override
    public void actionPerformed(ActionEvent e)
    {
        Point3d p = solutionSpaces.get(space);
        g.fillRect((int)p.x * spaceLength, (int)p.y * spaceWidth, spaceLength, spaceWidth); // g is static reference to Graphics2D, set from MazePanel
        mazePanel.repaint(); //mazePanel is instance
        space++;
    }
});
...
private void paintMaze()
{
    if (this.reader != null)
    {
        for (int col = 0; col < this.reader.getWidth(); col++)
        {
            for (int row = 0; row < this.reader.getLength(); row++)
            {
                MazeConstruct c = reader.check(row, col, floor);
                if (c == MazeConstruct.WALL)
                    g.setColor(Color.BLACK);
                else if (c == MazeConstruct.ELEVATOR)
                    g.setColor(Color.YELLOW);
                else if (c == MazeConstruct.START)
                    g.setColor(Color.CYAN);
                else if (c == MazeConstruct.FINISH)
                    g.setColor(Color.RED);
                else if (c == MazeConstruct.OPEN)
                    g.setColor(Color.GRAY);
                if (c != MazeConstruct.SOLUTION && c != MazeConstruct.TRAVERSED)
                    g.fillRect(row * spaceLength, col * spaceWidth, spaceLength, spaceWidth);
                g.setColor(Color.DARK_GRAY);
                g.drawRect(row * spaceLength, col * spaceWidth, spaceLength, spaceWidth);
            }
        }
    }
    if (solving)
    {
        timer.setInitialDelay(0);
        timer.start();
    }
}

提供的代码将立即充分绘制完整的解决方案,但我需要它以动画的形式逐步遍历每个单独的空间。请帮我。这绝对是我最后的手段!如果我需要提供任何其他信息,请告诉我。此外,欢迎建设性 批评。太感谢了。

[编辑] 我在这里发现了一个随机问题,该问题说明了一个普遍的观点,即计时器的滴答声 (actionPerformed()) 应该只更新数据的状态(例如,移动到下一个空间)而不是实际绘画,以及图形g 不应在paintComponent 调用之后保持。我将使用这些新的小信息来解决我的问题。仍然欢迎回答。

【问题讨论】:

    标签: java swing animation paintcomponent maze


    【解决方案1】:

    如果我了解您的问题(如果不了解,请纠正我),当您逐步浏览每个单独的空间时,您将无法更新屏幕。在这种情况下,您将使用 repaint();方法。

    【讨论】:

    • 我在计时器的 actionPerformed 方法中调用 repaint()。你的意思是在别处调用 repaint() 吗?需要详细说明吗?
    • 很抱歉,当我发布此回复时,由于我的互联网连接不佳,您的代码尚未加载,我的回复完全基于您给我们的文字...
    • 似乎无论我在 MazePanel paintComponent() 方法或计时器的 actionPerformed() 方法中放置什么,计时器内的任何内容都不会被绘制到面板上。这个问题要了我的命。
    • 好吧,我也被难住了......(我和新手一样新手)
    【解决方案2】:

    渲染图形时必须记住的几件事Component

    1) GraphicsGraphics2D 对象不是永久的,它是在调用 repaint() 时临时创建的,不能依赖 Graphics 对象的副本(比如将其保存为静态变量)。使用后必须丢弃。

    2) 从paintComponent() 方法内部调用所有使用Graphics 对象的方法,只使用传递给paintComponent() 方法的Graphics 对象的副本。

    这是您必须使用的模板:

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.draw...(...);
        g.fill...(...);
        yourMethod((Graphics2D)g);
        ....
        ....
    }
    private void yourMethod(Graphics2D g) {
        g.setPaint(..);
        g.draw...();
        ....
    }
    

    您正在保存 Graphics 对象并在不正确的 paintComponent() 之外使用它,并且可能导致未定义的行为。您正在调用 g.fillRect((int)p.x * spaceLength, (int)p.y * spaceWidth, spaceLength, spaceWidth); 并使用已保存的 g! 实例,这是一个严重的问题。

    要记住的另一件事是,如果 repaint() 方法已经在绘制,它可能不会调用 paintComponent()。

    上述模板适用于您的情况。但是,如果您真的对非常好的性能(完成大量绘图)感兴趣,则必须使用双缓冲(即使用单独的线程绘制到图像,然后立即将图像放在屏幕上)。

    【讨论】:

    • 感谢您的回答。我不知道 Graphics 对象的“范围”。根据我们的教授的说法,我不太担心性能,这是“最坏的情况”。是一个 25x25x10 的画幅,它可以为我即时绘画。您介意详细说明 repaint() 而不调用 paintComponent() 吗?我认为这就是 repaint 具体所做的。
    • 我认为问题在于您正在保存Graphics 对象。这可能就是为什么没有调用paintComponent() 的原因。我认为它会在开始时被调用一次,然后g 没有被释放所以它被卡住了。将g 作为参数传递给paintMaze() 方法,类似于paintMaze(Graphics g)。删除 Graphics 对象的static 定义。
    • 那么我该如何从计时器的 actionPerformed 中进行绘制呢?由于它是在 ActionLstener 中定义的方法,我不能将 Graphics 的引用传递给它吗?或者您会建议重构计时器仅更新数据状态的位置(例如要绘制哪个迷宫单元格)并相应地重写我的paintMaze?
    • 从 actionPerformed() 方法调用 repaint()。并将绘图代码放在paintComponent() 方法中,在super.paintComponent() 行之后。
    • 所以我不应该将 actionPerformed() 告诉 fillRect(),对吗?
    猜你喜欢
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2014-05-07
    • 2019-02-01
    相关资源
    最近更新 更多