为什么Java应用程序运行更流畅了它移动鼠标时?包括视频

为什么Java应用程序运行更流畅了它移动鼠标时?包括视频

问题描述:

我与教程的学习从这个网站 - 固定时间步长栏目

I'm working with tutorial from this site - "Fixed timestep" section.

这里的code - http://pastebin.com/QaHgcLaR

Here's the code - http://pastebin.com/QaHgcLaR

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class GameLoopTest extends JFrame implements ActionListener
{
   private GamePanel gamePanel = new GamePanel();
   private JButton startButton = new JButton("Start");
   private JButton quitButton = new JButton("Quit");
   private JButton pauseButton = new JButton("Pause");
   private boolean running = false;
   private boolean paused = false;
   private int fps = 60;
   private int frameCount = 0;
   public GameLoopTest()
   {
      super("Fixed Timestep Game Loop Test");
      Container cp = getContentPane();
      cp.setLayout(new BorderLayout());
      JPanel p = new JPanel();
      p.setLayout(new GridLayout(1,2));
      p.add(startButton);
      p.add(pauseButton);
      p.add(quitButton);
      cp.add(gamePanel, BorderLayout.CENTER);
      cp.add(p, BorderLayout.SOUTH);
      setSize(500, 500);
      startButton.addActionListener(this);
      quitButton.addActionListener(this);
      pauseButton.addActionListener(this);
   }
   public static void main(String[] args)
   {
      GameLoopTest glt = new GameLoopTest();
      glt.setVisible(true);
   }
   public void actionPerformed(ActionEvent e)
   {
      Object s = e.getSource();
      if (s == startButton)
      {
         running = !running;
         if (running)
         {
            startButton.setText("Stop");
            runGameLoop();
         }
         else
         {
            startButton.setText("Start");
         }
      }
      else if (s == pauseButton)
      {
        paused = !paused;
         if (paused)
         {
            pauseButton.setText("Unpause");
         }
         else
         {
            pauseButton.setText("Pause");
         }
      }
      else if (s == quitButton)
      {
         System.exit(0);
      }
   }
   //Starts a new thread and runs the game loop in it.
   public void runGameLoop()
   {
      Thread loop = new Thread()
      {
         public void run()
         {
            gameLoop();
         }
      };
      loop.start();
   }
   //Only run this in another Thread!
   private void gameLoop()
   {
      //This value would probably be stored elsewhere.
      final double GAME_HERTZ = 30.0;
      //Calculate how many ns each frame should take for our target game hertz.
      final double TIME_BETWEEN_UPDATES = 1000000000 / GAME_HERTZ;
      //At the very most we will update the game this many times before a new render.
      //If you're worried about visual hitches more than perfect timing, set this to 1.
      final int MAX_UPDATES_BEFORE_RENDER = 5;
      //We will need the last update time.
      double lastUpdateTime = System.nanoTime();
      //Store the last time we rendered.
      double lastRenderTime = System.nanoTime();
      //If we are able to get as high as this FPS, don't render again.
      final double TARGET_FPS = 60;
      final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;
      //Simple way of finding FPS.
      int lastSecondTime = (int) (lastUpdateTime / 1000000000);
      while (running)
      {
         double now = System.nanoTime();
         int updateCount = 0;
         if (!paused)
         {
             //Do as many game updates as we need to, potentially playing catchup.
            while( now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER )
            {
               updateGame();
               lastUpdateTime += TIME_BETWEEN_UPDATES;
               updateCount++;
            }
            //If for some reason an update takes forever, we don't want to do an insane number of catchups.
            //If you were doing some sort of game that needed to keep EXACT time, you would get rid of this.
            if ( now - lastUpdateTime > TIME_BETWEEN_UPDATES)
            {
               lastUpdateTime = now - TIME_BETWEEN_UPDATES;
            }
            //Render. To do so, we need to calculate interpolation for a smooth render.
            float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
            drawGame(interpolation);
            lastRenderTime = now;
            //Update the frames we got.
            int thisSecond = (int) (lastUpdateTime / 1000000000);
            if (thisSecond > lastSecondTime)
            {
               System.out.println("NEW SECOND " + thisSecond + " " + frameCount);
               fps = frameCount;
               frameCount = 0;
               lastSecondTime = thisSecond;
            }
            //Yield until it has been at least the target time between renders. This saves the CPU from hogging.
            while ( now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES)
            {
               Thread.yield();
               //This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
               //You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
               //FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
               try {Thread.sleep(1);} catch(Exception e) {}
               now = System.nanoTime();
            }
         }
      }
   }
   private void updateGame()
   {
      gamePanel.update();
   }
   private void drawGame(float interpolation)
   {
      gamePanel.setInterpolation(interpolation);
      gamePanel.repaint();
   }
   private class GamePanel extends JPanel
   {
      float interpolation;
      float ballX, ballY, lastBallX, lastBallY;
      int ballWidth, ballHeight;
      float ballXVel, ballYVel;
      float ballSpeed;
      int lastDrawX, lastDrawY;
      public GamePanel()
      {
         ballX = lastBallX = 100;
         ballY = lastBallY = 100;
         ballWidth = 25;
         ballHeight = 25;
         ballSpeed = 25;
         ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
         ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
      }
      public void setInterpolation(float interp)
      {
         interpolation = interp;
      }
      public void update()
      {
         lastBallX = ballX;
         lastBallY = ballY;
         ballX += ballXVel;
         ballY += ballYVel;
         if (ballX + ballWidth/2 >= getWidth())
         {
            ballXVel *= -1;
            ballX = getWidth() - ballWidth/2;
            ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
         }
         else if (ballX - ballWidth/2 <= 0)
         {
            ballXVel *= -1;
            ballX = ballWidth/2;
         }
         if (ballY + ballHeight/2 >= getHeight())
         {
            ballYVel *= -1;
            ballY = getHeight() - ballHeight/2;
            ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
         }
         else if (ballY - ballHeight/2 <= 0)
         {
            ballYVel *= -1;
            ballY = ballHeight/2;
         }
      }
      public void paintComponent(Graphics g)
      {
         //BS way of clearing out the old rectangle to save CPU.
         g.setColor(getBackground());
         g.fillRect(lastDrawX-1, lastDrawY-1, ballWidth+2, ballHeight+2);
         g.fillRect(5, 0, 75, 30);
         g.setColor(Color.RED);
         int drawX = (int) ((ballX - lastBallX) * interpolation + lastBallX - ballWidth/2);
         int drawY = (int) ((ballY - lastBallY) * interpolation + lastBallY - ballHeight/2);
         g.fillOval(drawX, drawY, ballWidth, ballHeight);
         lastDrawX = drawX;
         lastDrawY = drawY;
         g.setColor(Color.BLACK);
         g.drawString("FPS: " + fps, 5, 10);
         frameCount++;
      }
   }
   private class Ball
   {
      float x, y, lastX, lastY;
      int width, height;
      float xVelocity, yVelocity;
      float speed;
      public Ball()
      {
         width = (int) (Math.random() * 50 + 10);
         height = (int) (Math.random() * 50 + 10);
         x = (float) (Math.random() * (gamePanel.getWidth() - width) + width/2);
         y = (float) (Math.random() * (gamePanel.getHeight() - height) + height/2);
         lastX = x;
         lastY = y;
         xVelocity = (float) Math.random() * speed*2 - speed;
         yVelocity = (float) Math.random() * speed*2 - speed;
      }
      public void update()
      {
         lastX = x;
         lastY = y;
         x += xVelocity;
         y += yVelocity;
         if (x + width/2 >= gamePanel.getWidth())
         {
            xVelocity *= -1;
            x = gamePanel.getWidth() - width/2;
            yVelocity = (float) Math.random() * speed*2 - speed;
         }
         else if (x - width/2 <= 0)
         {
            xVelocity *= -1;
            x = width/2;
         }
         if (y + height/2 >= gamePanel.getHeight())
         {
            yVelocity *= -1;
            y = gamePanel.getHeight() - height/2;
            xVelocity = (float) Math.random() * speed*2 - speed;
         }
         else if (y - height/2 <= 0)
         {
            yVelocity *= -1;
            y = height/2;
         }
      }
      public void draw(Graphics g)
      {
      }
   }
}

运行此code后,球有一种滞后性,但仍有60 FPS。之后,我鼠标移到应用程序的窗口和随机方向移动,球被平滑移动。这种事即使窗口的应用程序不集中!怎么了?它可以被固定的吗?

After run this code, the ball has kind of lag, but there is still 60 FPS. After I move mouse over application's window and move it in random directions, the ball is moving smoothly. It happens even if window application isn't focused! What's wrong? Can it be fixed?

我在使用Ubuntu 13.04与Oracle JDK7。
我发现,它与每一个应用程序发生。类似的事情发生,即使在LWJGL应用程序,但滞后效应比Swing应用程序少得多。

I'm using Ubuntu 13.04 with Oracle JDK7. I've found that it happens with every application. Similar things happens even in LWJGL application, but effect of the "lag" is much less than in swing application.

显示我的问题,17秒的视频

17 sec video showing my problem

http://www.youtube.com/watch?v=J8SBjKncgRw

在重新绘制应该从基于Swing的被触发的 定时 ,这保证了图形用户界面更新呼吁EDT。见摇摆 并发更多的细节。

The repaints should be triggered from a Swing based Timer, which ensures that GUI updates are called on the EDT. See Concurrency in Swing for more details.