【问题标题】:Android: Low FPS drawing many bitmaps in a surfaceviewAndroid:低 FPS 在表面视图中绘制许多位图
【发布时间】:2015-10-26 06:27:26
【问题描述】:

我正在尝试创建一个弹幕游戏,但遇到了一些麻烦。大约 500 发子弹后,我无法获得超过 17 fps 的速度。所有这些更新逻辑代码大约需要 1-4 毫秒,而渲染代码大约需要 40 毫秒

现在我的代码是

private void drawEntities(Canvas canvas) {
    for (HashMap<UUID, Spatial> h: spatialList) {
        for (Spatial spatial: h.values()) {
            spatial.render(canvas);
            if(spatial.life > 0)
                spatial.life--;
            else if (spatial.life == 0)
                engine.deleteEntity(spatial.owner);
        }
    }
}

spatialList 是一个数组列表,其中每个索引都是一个 zLevel

显示实际子弹的空间是

public void render(Canvas canvas) {
    float angle = (float) (vel.getAngle() * (180 / Math.PI));
    matrix.reset();
    matrix.setTranslate(pos.x - bullet.getWidth() / 2, pos.y - bullet.getHeight() / 2);
    matrix.postRotate(angle + 90,  pos.x, pos.y);
    canvas.drawBitmap(bullet, matrix, paint);
    canvas.drawCircle(pos.x, pos.y, col.getRadius(), paint);
}

我可以提供更多代码,但这些似乎是主要问题。我已经尝试了所有我能想到的东西,但在网上找不到太多其他东西。我能想到的唯一解决此问题的方法是从表面视图切换到 GLSurfaceview,但我真的认为有更好的方法,我只是使用了错误的代码。

编辑:我注意到我的计时器已关闭并删除了drawcircle,再次运行后我得到了40ms~大约500,这对于合理的性能来说仍然有点太低了。

TLDR; 500 个实体 = 17 fps。

【问题讨论】:

    标签: android performance surfaceview


    【解决方案1】:

    您可能会受到像素填充率的限制。您的测试设备上的显示屏有多大(以像素为单位)?

    一个简单的做法是使用setFixedSize() 来减小SurfaceView 的Surface 的大小。这将减少您触摸的像素数量。示例here,视频here,博文here

    这样做通常是个好主意,因为较新的设备似乎正在朝着荒谬的像素计数方向发展。在软件中执行所有渲染的全屏游戏将在 2560x1440 显示器上挣扎,并在 4K 时表现不佳。将游戏“限制”为 1080p 并让显示缩放器完成繁重的工作应该会有所帮助。根据游戏的性质,您可以将分辨率设置得更低,而不会明显降低质量。

    要尝试的另一件事是消除drawBitmap() 调用,检查您的时间,然后恢复它并消除drawCircle() 调用,看看其中一个或另一个是否占用了大部分时间。

    您可能会发现切换到 OpenGL ES 并不是那么糟糕。 Grafika(来自上面链接的视频)中的“硬件缩放器练习器”活动展示了一些简单的位图渲染。将drawCircle() 替换为缩放位图,您可能已经完成了大部分工作。 (请注意,该活动中的 GLES 使用 SurfaceView,而不是 GLSurfaceView。)

    【讨论】:

    • 仅使用 drawCircle 帧数在多达 750 个实体上的 fps 不会低于 30,即使是我的破烂手机也很棒。但是我注意到,即使没有绘制任何东西,渲染时间也是 16 毫秒,这很奇怪。 setFixedSize() 函数没有看到 fps 的一点点增加。你是说你可以将surfaceview用于gles?有趣
    • 可能是位图旋转。如果您只是放弃matrix.postRotate(),情况会有所改善吗? GLSurfaceView 只是 SurfaceView 的一个包装类,它处理 EGL 设置并为您管理渲染器线程。 Grafika 拥有 EGL 设置所需的一切,我猜您已经在为 SurfaceView 管理自己的线程,所以它可能很简单(需要一些 GLES 工作)。 16.7ms 的“渲染时间”可能是由于 60fps 设备上的队列填充,因为 SurfaceView 不会丢帧;参看。 source.android.com/devices/graphics/architecture.html#loops
    • Holy Moly,OpenGL 令人生畏,但由于它确实提高了速度,而且过渡似乎不太糟糕,我说你回答了它
    • 您需要的大部分内容都在 Grafika 或 Android Breakout (github.com/fadden/android-breakout) 之类的示例中——后者旨在作为一个简单但完整的 GLES 2.x 游戏的示例。我认为 Grafika 的班级作文更好。一旦你越过了最初的学习悬崖,你就只是在绘制位图。一个尴尬的领域是文本渲染,您可以使用字体纹理(如 Breakout 使用)或分数叠加,只需将其绘制在 SurfaceView 的视图部分。
    【解决方案2】:

    我遇到了同样的问题。如果你在一个位图帧缓冲区上做所有的绘制,然后将帧缓冲区绘制到画布上,这个问题将在很大程度上得到解决。如果您直接在画布上绘图,那么会有一些开销。我环顾四周,找到了这个教程 "http://www.kilobolt.com/day-6-the-android-game-framework-part-ii.html"

    查看 AndroidGraphics 和 AndroidFastRenderView 的实现,看看他如何使用 AndroidGraphics 在缓冲区中进行所有实际绘制,并将该缓冲区绘制到 AndroidFastRenderView 中的画布上。

    【讨论】:

    • 在大多数具有 2 个全屏背景图像、大约 200 个具有旋转和缩放功能的精灵以及 32 位图像的手机上,我的速度都超过了 30 fps。
    • 是的,使用帧缓冲区后,我可以在 800 左右以最小的下降进行绘制。该技术确实不容易理解,但易于实施。
    【解决方案3】:

    尝试从 render() 方法中移除旋转和/或 drawCircle 部分时的性能。它们都可能非常耗时,因为它们可能包含 sin() / cos() 计算。

    如果这有帮助,您将必须弄清楚如何用更快的东西替换它们,如果没有,那么......

    【讨论】:

    • 我已经尝试了这两种方法,虽然它们加快了速度,但旋转是必要的。图像需要每帧动态旋转以匹配发射角度
    • 我明白了,抱歉没有用。我自己不是游戏开发者,所以恐怕我能提供的只有这些了:-/
    • 我自己不是游戏开发者,但对于一个弹幕来说,250 发子弹是相当有限的。它还暗示某些事情确实拖慢了我的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    相关资源
    最近更新 更多