在画布上绘制可全屏绘制时的低帧频

时间:2018-11-05 15:10:19

标签: android canvas background android-drawable frame-rate

我正在开发的应用是Flappy Bird克隆。 我正在使用 surfaceView 对象,在其中有一个gameThread,并在其run方法内部在画布上绘制了游戏的各个组件。

只要我绘制矩形代表对象,一切就可以顺利进行,但是当我添加了第一个Drawables时,我注意到平滑度有所降低。如果我尝试将背景绘制为Drawable,则游戏会遭受非常明显的帧频损失。

我尝试过的事情:

  • 将png和所有不同类型的位图用作资产
  • 调整资产大小以使其完全适合画布,从而避免重新缩放

这些都没有任何实质性的影响。

基本上:

  • 如果我仅使用drawRect: 60fps
  • 如果我使用drawRect和其他组件使用drawable.draw(canvas)进行拉回: 57fps
  • 如果我使用drawable.draw(canvas)绘制所有内容(包括背景): 15fps

一些相关代码:

public class CannonView extends SurfaceView
        implements SurfaceHolder.Callback {
    private CannonThread cannonThread; // controls the game loop
    private Drawable background;

    // constructor
    public CannonView(Context context, AttributeSet attrs) {
        super(context, attrs); // call superclass constructor
        getHolder().addCallback(this);
        background= ResourcesCompat.getDrawable(getResources(), R.drawable.background, null);
    }
    public void newGame() {
        background.setBounds(0,0, getScreenWidth(),getScreenHeight());
    }
    public void drawGameElements(Canvas canvas) {
        background.draw(canvas);
    }
    public void stopGame() {
        if (cannonThread != null)
            cannonThread.setRunning(false); // tell thread to terminate
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (!dialogIsDisplayed) {
            newGame(); // set up and start a new game
            cannonThread = new CannonThread(holder); // create thread
            cannonThread.setRunning(true); // start game running
            cannonThread.start(); // start the game loop thread
        }
    }
    private class CannonThread extends Thread {
        private SurfaceHolder surfaceHolder; // for manipulating canvas
        private boolean threadIsRunning = true; // running by default

        // initializes the surface holder
        public CannonThread(SurfaceHolder holder) {
            surfaceHolder = holder;
            setName("CannonThread");
        }

        // changes running state
        public void setRunning(boolean running) {
            threadIsRunning = running;
        }
        // controls the game loop
        @Override
        public void run() {
            Canvas canvas = null; // used for drawing
            while (threadIsRunning) {
                try {
                    // get Canvas for exclusive drawing from this thread
                    canvas = surfaceHolder.lockCanvas(null);
                    synchronized(surfaceHolder) {
                        drawGameElements(canvas);
                    }
                }
                finally {
                    if (canvas != null)
                        surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }

}

1 个答案:

答案 0 :(得分:0)

很明显,低帧频的主要原因是background.draw()。切换到Bitmap可能会有所改善,这可能是因为它缓存了draw()的输出,并且因为它可以与保证不需要缩放的Canvas函数一起使用(例如,{{ 3}})

您还发现,切换为RGB_565作为中间格式会大大提高性能,大概是因为它丢弃了alpha。 (否则,我原以为这样做会慢一些,因为b / c格式必须将其转换为RGBA_8888,因为它被切成SurfaceView。)

很明显,Android不会让您超过60fps。这几乎可以肯定是因为lockCanvas()参与了drawBitmap( Bitmap, float, float, Paint)方案,该方案会限制绘图速率,以防止您提交无法显示的帧(由于设备的固定屏幕刷新率为60Hz)。

这留下了一个问题,为什么您没有获得完整的60fps,但是接近它。如果drawGameElements()每次运行都花费相同的时间,并且少于16ms,那么lockCanvas()应该限制您,并且绝对不会丢帧(连续60fps)。线程调度程序中似乎有麻烦,并且在三重缓冲方案需要triple buffering之前,CannonThread经常执行得不够快,无法提供帧。在这种情况下,必须延迟帧直到下一个屏幕刷新。您可以尝试增加CannonThread's page-flip,删除drawGameElements()中绝对不需要在CannonThread上进行的任何额外处理,或关闭设备上正在运行的其他应用。 / p>

如上所述,OpenGL是获得此类游戏最大Sprite性能的标准方法,因为它能够将许多操作转移到硬件上。您可能正在接近基于drawBitmap()的游戏的性能极限。