【问题标题】:Swing timer - time fluctuating摆动计时器 - 时间波动
【发布时间】:2011-10-12 23:25:38
【问题描述】:

我在我的游戏中使用了 Swing Timer,但是当游戏运行时,它似乎有运行平稳的时刻和减慢速度的时刻。

为什么时间会波动?

我该如何解决?

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main extends JFrame {

public Main() {
    super("JFrame");

    // you can set the content pane of the frame
    // to your custom class.

    setContentPane(new ImagePanel());

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    setSize(800, 400);
    setResizable(false);
    setVisible(true);

}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    new Main();

}

class ImagePanel extends JPanel {

    Timer movementtimer;

    int x, y;

    public ImagePanel() {

        x = 0;
        y = 0;

        movementtimer = new Timer(12, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                long timstarted = System.currentTimeMillis();
                moveImage();
                repaint();
                long timefinished = System.currentTimeMillis() - timstarted;
                System.out.println(timefinished + " to run");
            };

        });

        movementtimer.start();

    }

    public void moveImage() {

        x++;
        y++;

        if (x > 800) {
            x = 0;
        }
        if (y > 400) {
            y = 0;
        }

    }

    public void paintComponent(Graphics g) {

        super.paintComponent(g);
        g.setColor(Color.RED);
        g.fillRect(0, 0, 800, 400);
        g.setColor(Color.BLUE);
        g.fillRect(x, y, 50, 50);

    }

}

}

这是我的代码示例。在我的实际程序中,我正在绘制图像而不仅仅是一个矩形。还有很多碰撞检测和其他小计算发生。

另外,这里是游戏 Jar 文件的链接,您可以运行它并(希望)明白我的意思。 http://dl.dropbox.com/u/8724803/Get%20To%20The%20Chopper%201.3.jar

谢谢

汤姆

【问题讨论】:

  • 顺便说一句 - 我无法理解您的计时计算逻辑。将 long timstarted; 设为 ActionListener 类的属性,您可能会看到我所看到的 - 一致的 12s 和 13s。

标签: java swing time timer


【解决方案1】:

由于渲染很简单,我发现您的示例的这种变体非常流畅。渲染时间远低于半毫秒,因此 12 毫秒周期 (~83 Hz) 是完成一帧的大量 时间,通常占用不到 10% 的内核。随着渲染时间的增长,计时器线程变得饱和,并且事件被合并。由于渲染与垃圾收集和外部处理需求竞争,这种效果在单个内核上被放大了。 Java 不是实时系统,并不是所有的调度器都是平等的。

您肯定希望按照here 的建议分析您的实际代码,以查看与波动性能的任何相关性。另一种方法是延长周期(降低频率)以满足您的渲染截止日期,并在moveImage() 中使用更大的增量来获得相同的速度。

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Main extends JFrame {

    private static final int W = 800;
    private static final int H = 400;

    public Main() {
        super("JFrame");
        this.add(new ImagePanel());
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.pack();
        setSize(W, H);
        this.setLocationRelativeTo(null);
        setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Main();
            }
        });
    }

    class ImagePanel extends JPanel {

        Timer movementTimer;
        int x, y;

        public ImagePanel() {
            x = 0;
            y = 0;
            movementTimer = new Timer(12, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    moveImage();
                    repaint();
                }
            });
            movementTimer.start();
        }

        public void moveImage() {
            x++;
            y++;
            if (x > W) {
                x = 0;
            }
            if (y > H) {
                y = 0;
            }
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            long start = System.nanoTime();
            g.setColor(Color.RED);
            g.fillRect(0, 0, W, H);
            g.setColor(Color.BLUE);
            g.fillRect(x, y, 50, 50);
            double delta = (System.nanoTime() - start) / 1000000d;
            g.drawString(String.format("%1$5.3f", delta), 5, 15);
        }
    }
}

【讨论】:

  • @Andrew Thompson 我同意你删除的帖子 (trashgod+1)
  • @mKorbel 这在某种意义上是准确的,因为如果您每秒运行Timer 进行 86400 次迭代,它将“漂移”而不是 24 小时。但我认为垃圾神发现了生涩动画的真正问题 - 核心饱和度。
  • @Andrew:如果您恢复已删除的帖子(+1,顺便说一句),您可能会引用 Clock Qualityjavax.swing.Timer 不是很准确,但它在现代平台上具有有用的精确 resolution
【解决方案2】:

Swing Timer 因其不准确而臭名昭著。改用其他东西。


在提示下,我决定取消删除此帖子。 OTOH 值得恢复的大部分额外信息来自垃圾神,所以我只会引用/解释他们的 cmets。

我认为它相当准确但容易饱和。

然后垃圾神继续添加单独的评论:

(I) 可能会引用Clock Qualityjavax.swing.Timer 不是很准确,但在现代平台上它具有有用的精确 resolution

【讨论】:

    【解决方案3】:

    在面板构造函数中做:

    setBackground(Color.RED);
    

    那么你不需要擦除背景,因为你正在调用 super.paintComponent。

    通常根据实际经过的时间 (System.nanoTime()) 计算位置,并且不依赖于计时器帧。那里有几个游戏框架,所以也许值得看看他们的解决方案。我喜欢这个样本。

    【讨论】:

    • 好主意,但这似乎更适合移动组件。
    • 我不能这样做,因为在我的实际程序中,背景是不断移动的。
    猜你喜欢
    • 2021-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    相关资源
    最近更新 更多