Android游戏循环:如何达到平均60帧每秒?

3
我曾经看到Airplane Madness游戏在我的HTC Wildfire S手机上以恒定平均60 FPS的速度渲染屏幕。我一直在尝试在同一部手机上模仿这个结果,但我就是不明白为什么我的程序平均只有15 FPS?
你可能会问我如何查看Airplane Madness的帧率?在设置中有一个开关FPS计数器的选项。我将其设置为启用。
这是我的代码。这是我的游戏循环。从中可以得出一些有趣的结果:
    public void run() {
        // Average FPS for Android is 15 on Test Phone is 13 through 15.
        // Max FPS is 20.
        // Occasional bursts of FPS60 may occur.
        long now, lastTime = System.nanoTime();
        double process = 0.0;
        final double NSperTick = 1000000000.0 / 60.0;
        while (running) {
            now = System.nanoTime();
            process += (now - lastTime) / NSperTick;
            lastTime = now;
            boolean ticked = false;
            while (process >= 1) {
                process -= 1;
                tick();
                ticked = true;
            }
            if (ticked) {
                render();
            }
            swap();
            try {
                Thread.sleep(2);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

有人能告诉我哪里做错了,需要改进什么,以便我可以获得平均60 FPS吗?这个游戏循环在Java中运行时(不是Android),它的运行非常好,所以我不确定哪些逻辑部分影响了我的智能手机的帧率。

提前感谢您。


编辑:看起来我需要让所有函数为人所知。

private void render() {
    synchronized (holder) {
        if (holder.getSurface().isValid()) {
            Canvas c = holder.lockCanvas();
            c.drawRGB(0x44, 0x55, 0xff);
            ball.render(c);
            holder.unlockCanvasAndPost(c);
        }
    }
}

private void swap() {
}

private void tick() {
    float[] values = {meter.X, meter.Y, meter.Z};
    ball.tick(values);
    handler.tick(ball);
}

以下是我检查游戏平均FPS的方法:

public void run() {
    // Average FPS for Android is 15 on Test Phone is 13 through 15.
    // Max FPS is 20.
    // Occasional bursts of FPS60 may occur.
    long now, lastTime = System.nanoTime();
    int frames = 0;
    double process = 0.0;
    long frameTime = System.currentTimeMillis();
    final double NSperTick = 1000000000.0 / 60.0;
    while (running) {
        now = System.nanoTime();
        process += (now - lastTime) / NSperTick;
        lastTime = now;
        boolean ticked = false;
        while (process >= 1) {
            process -= 1;
            tick();
            ticked = true;
        }
        if (ticked) {
            //render();
            frames++;
        }
        swap();
        try {
            Thread.sleep(2);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (frameTime <= System.currentTimeMillis() - 1000)
        {
            Log.d("FrameRate", Integer.toString(frames));
            frameTime += 1000;
            frames = 0;
        }
    }
}

以下是DDMS的结果:

03-12 14:29:21.008: D/FrameRate(3533): 14
03-12 14:29:22.061: D/FrameRate(3533): 15
03-12 14:29:23.048: D/FrameRate(3533): 14
03-12 14:29:24.033: D/FrameRate(3533): 14
03-12 14:29:25.018: D/FrameRate(3533): 14
03-12 14:29:26.000: D/FrameRate(3533): 14
03-12 14:29:27.056: D/FrameRate(3533): 15
03-12 14:29:28.047: D/FrameRate(3533): 14
03-12 14:29:29.026: D/FrameRate(3533): 14
03-12 14:29:29.995: D/FrameRate(3533): 14
03-12 14:29:31.037: D/FrameRate(3533): 15
03-12 14:29:32.015: D/FrameRate(3533): 14

编辑2:这里有更多的信息:

我已经在render()函数中注释掉了这段代码:

synchronized (holder)

但我只能达到14至15 FPS。这是我无法突破的限制吗?


如果您不调用render(),它会如何表现?这是您似乎忽略的明显问题,也许您的渲染需要比您拥有的时间更长。 - unwind
我们必须查看tick()、render()和swap()方法中的代码才能帮助您。 - Jason Nichols
@unwind,我已经为tick()、render()和swap()添加了一些更多的信息。我注意到在我的render()中,我调用了一个名为Ball的对象。我应该在顶部展开该函数吗? - tom_mai78101
你可能想尝试使用忙等待而不是使用 sleep() 。游戏循环是一个需要实际使用忙等待的情况,因为 sleep() 无法保证在唤醒之前睡眠的最长时间。 - TaZ
是的,这是一个相当棘手的问题。我没有想到要考虑这是在手机上执行的事实。 - TaZ
显示剩余2条评论
3个回答

2

我不明白为什么你在渲染循环中进行同步。这是不好的形式,因为如果由于任何原因它不能立即获取锁定而导致应用程序挂起,则可能会锁定您的渲染线程。获取锁还需要时间。

如果您的数据组织方式需要在渲染线程中获取锁定,则需要重构设计。

永远不要在UI线程中进行同步!


创建一个新线程,然后使用该线程来同步持有者,是否算作同步 UI 线程? - tom_mai78101
你好,我添加了一点更多的信息。移除synchronized(holder)根本不能解决问题。 :/ - tom_mai78101

0

你尝试过减少图片/纹理的渲染吗?也许你的手机有点太弱了,或者你可以尝试稍微调整一下毫秒数来休眠。


我目前正在将1个位图和1个蓝色背景渲染到SurfaceView的Canvas上。我没有运行任何可能使用OpenGL ES的东西。此外,我的手机可以以平均60 FPS的速度运行Airplane Madness,我相信它是基于OpenGL的。这意味着我的手机并不那么弱。我稍微尝试了一下Thread.sleep()函数,但这并不重要。 :/ - tom_mai78101

0
我强烈建议你使用一个游戏框架,比如libgdx。有大量的示例和教程能够为你的应用带来好处。更不用说你可以轻松地将它移植到桌面或HTML5平台上。

我必须拒绝那个提议。无论如何我都不能使用游戏框架。:/ - tom_mai78101

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接