Android游戏循环,如何控制速度

发布于 2024-12-05 16:08:00 字数 2019 浏览 0 评论 0 原文

我已经通过 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 对于高配置和低配置来说是相同的。设备。我想要的是游戏在高配置和低配置上运行相同。设备。

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

I have created my game loop through Surfaceview.
Animations in the game run faster on powerful devices with high configuration and slower on low configuration devices.
So I tried to count FPS and if FPS is high then there should be delay, otherwise no delay for myThreadSurfaceView.onDraw(c);

Here is my code of game loop thread that I used for calculation:

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);
      }
    }
  }
}

Now this code is giving the desired output as I want sometimes FPS will be the same for high and low config. devices. What I want is that the game runs the same on both high and low config. devices.

So how can I achieve my goal? Is there any better way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦中的蝴蝶 2024-12-12 16:08:00

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

帧计数始终取决于 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 方法并发布延迟的“Runnable”消息来执行循环而不是休眠线程。

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

有关代码片段示例,请参阅 Android 文档:

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

要处理时间步更新循环,请调用 postDelayed(myRunnable, someDelay) ,在其中创建一个实现 runnable 的类。
或者,只需将 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 应用程序。 (它有点更先进,但它有助于平滑帧速率打嗝)

First: (to answer your question) Use "clock time" to adjust frame rate rather than counting the number of frames.

Frame counting is always going to be CPU power dependent - faster the hardware the more times it can call your update loop.

So you want to adjust based on the actual time it takes to execute one pass through your loop and then adjust the sleep based on the result.

The easiest way is to track how long it takes for your update code to execute (say, 10 milliseconds).

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);
}

Part II:

Also ... Perhaps consider using a Looper / Handler approach and posting delayed "Runnable" messages to execute your loop rather than sleep'ing the thread.

It's a bit more flexible ... you don't need to deal with thread sleep at all ... and you will probably want to post other messages
to your thread anyway (like pause/resume game).

See the Android documentation for an code snippet example:

The Looper is simple - it just loops waiting for messages to array until you call myLooper.quit().

To process your timestep update loop call postDelayed(myRunnable, someDelay) where you create a class implementing runnable.
Or, just put MyUpdate method in the class and call it when receive a message ID. (Runnable version is more efficient)

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)
}

The following article is a more elaborate solution with attempts to compensate for the occasional "this one timestep ran long". It's for Flash actionscript but easily applied to your Android application. (It's a bit more advanced but it helps smooth frame rate hiccups)

忆梦 2024-12-12 16:08:00

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

I have seen this on tutorials, try to sleep() after every frame, or something like that, this will allow other processes on the phone to work and it should make ur game run the same speed whatever the hardware is, sorry that i cant give u a better answer, but try surface view tutorials

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文