【问题标题】:Android game loop, how to control speedAndroid游戏循环,如何控制速度
【发布时间】:2011-09-22 10:44:35
【问题描述】:

我已经通过 Surfaceview 创建了我的游戏循环。 游戏中的动画在高配置的功能强大的设备上运行得更快,而在低配置的设备上运行得更慢。 所以我尝试计算 FPS,如果 FPS 高则应该有延迟,否则myThreadSurfaceView.onDraw(c);没有延迟@

这是我用于计算的游戏循环线程代码:

private long mLastTime;     

/** Variables for the counter */
private int frameSamplesCollected = 0;
private int frameSampleTime = 0;
private int fps = 0;

@Override
public void run() {    
  while (myThreadRun) {
    Canvas c = null;
    try {
      c = myThreadSurfaceHolder.lockCanvas(null);
      synchronized (myThreadSurfaceHolder) 
      {                                                                                
        long now = System.currentTimeMillis();

        if (mLastTime != 0) {

          //Time difference between now and last time we were here
          int time = (int) (now - mLastTime);
          frameSampleTime += time;
          frameSamplesCollected++;

          //After 10 frames
          if (frameSamplesCollected == 10) 
          {
            //Update the fps variable
            fps = (int) (10000 / frameSampleTime);

            //Reset the sampletime + frames collected
            frameSampleTime = 0;
            frameSamplesCollected = 0;
          }
        }
        mLastTime = now;
      }

      if(fps>25)
      {
        try {
          myThreadSurfaceView.onDraw(c);
          TimeUnit.MILLISECONDS.sleep(35);
          //Thread.sleep(35);
        } 

        catch (InterruptedException e) 
        {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }

      else
      {
        myThreadSurfaceView.onDraw(c);
      }
    }
    finally {
      // do this in a finally so that if an exception is thrown
      // during the above, we don't leave the Surface in an
      // inconsistent state
      if (c != null) {
        myThreadSurfaceHolder.unlockCanvasAndPost(c);
      }
    }
  }
}

现在这段代码给出了我想要的输出,有时 FPS 对于高配置和低配置是相同的。设备。我想要的是游戏在高配置和低配置上运行相同。设备。

那么我怎样才能实现我的目标呢?有没有更好的方法来做到这一点?

【问题讨论】:

标签: android canvas surfaceview frame-rate


【解决方案1】:

首先:(回答您的问题)使用“时钟时间”来调整帧速率,而不是计算帧数。

帧计数始终取决于 CPU 功率 - 硬件越快,它调用更新循环的次数就越多。

因此,您希望根据执行一次循环所需的实际时间进行调整,然后根据结果调整睡眠。

最简单的方法是跟踪执行您的更新代码所需的时间(例如,10 毫秒)。

private static final long ourTarget_millisPerFrame = 33; // ~30 FPS

public void MyUpdate() {

    long startTime = SystemClock.uptimeMillis();

    // ... Do your stuff here ...

    long stopTime = SystemClock.uptimeMillis();

    // How much time do *we* require to do what we do (including drawing to surface, etc)?
    long howLongItTakesForUsToDoOurWork = stopTime - startTime;

    // So, say we took 10 millis
    // ... that leaves (33 - 10 =) 23 millis of "time to spare"
    // ... so that's how long we'll sleep

    long timeToWait = ourTarget_millisPerFrame - howLongItTakesForUsToDoOurWork;

    // But apply a minimum wait time so we don't starve the rest of the system
    if ( timeToWait < 2 )
        timeToWait = 2;

    // Lullaby time    
    sleep(timeToWait);
}

第二部分:

另外...也许考虑使用 Looper / Handler 方法并发布延迟的“可运行”消息来执行您的循环,而不是让线程休眠。

它更灵活...您根本不需要处理线程睡眠...您可能想要发布其他消息 无论如何到你的线程(比如暂停/恢复游戏)。

有关代码 sn-p 示例,请参阅 Android 文档:

Looper 很简单 - 它只是循环等待消息排列,直到您调用 myLooper.quit()。

要处理您的时间步更新循环调用 postDelayed(myRunnable, someDelay),您可以在其中创建一个实现可运行的类。 或者,只需将 MyUpdate 方法放在类中,并在收到消息 ID 时调用它。 (可运行版本效率更高)

class MyUpdateThread extends Thread {
    public static final int MSGID_RUN_UPDATE = 1;
    public static final int MSGID_QUIT_RUNNING = 2;

    public MyMessageHandler mHandler;
    private MyUpdateRunnable myRunnable = new MyUpdateRunnable();

    private boolean mKeepRunning;

    // The *thread's* run method
    public run() {
        // Setup the Looper
        Looper.prepare();

        // Create the message handler
        // .. handler is auto-magically attached to this thread
        mHandler = new MyMessageHandler();

        // Prime the message queue with the first RUN_UPDATE message
        this.mKeepRunning = true;
        mHandler.post(myRunnable);

        // Start the Looper
        Looper.loop();
    } // end run fun (of MyThread class)    

    class MyMessageHandler extends Handler {
        @Override
        public void handleMessage(final Message msg) {
            try {
                // process incoming messages here
                switch (msg.what) {
                case MSGID_RUN_UPDATE:
                    // (optional)
                    // ... could call MyUpdate() from here instead of making it a runnable (do either/or but not both)
                    break;
                case MSGID_QUIT_RUNNING:
                    mKeepRunning = false; // flag so MyRunnable doesn't repost
                    Looper.myLooper().quit();
                    break;
                 }
             } catch ( Exception ex ) {}

    }


    class MyUpdateRunnable implements Runnable {
        public void run() {
            try {

                // ---- Your Update Processing ----
                // -- (same as above ... without the sleep() call) --

                // Instead of sleep() ... we post a message to "ourself" to run again in the future
                mHandler.postDelayed ( this, timeToWait );

            } catch ( Exception ex ) {}
        } // end run fun (my runnable)
    } // end class (my runnable)
}

下面的文章是一个更详细的解决方案,它试图弥补偶尔出现的“这个时间步长过长”。它适用于 Flash 动作脚本,但很容易应用于您的 Android 应用程序。 (它有点高级,但它有助于平滑帧率打嗝)

【讨论】:

    【解决方案2】:

    我在教程上看到过这个,在每一帧之后尝试 sleep(),或者类似的东西,这将允许手机上的其他进程工作,它应该让你的游戏运行相同的速度,无论硬件是什么,抱歉我不能给你更好的答案,但试试表面视图教程

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-02
      • 1970-01-01
      • 1970-01-01
      • 2012-03-03
      • 1970-01-01
      • 1970-01-01
      • 2017-10-12
      • 2012-06-08
      相关资源
      最近更新 更多