【问题标题】:Drawing to canvas is too slow , How do i achieve constant 30 fps or more ? (Android)画到画布太慢,我如何达到恒定的 30 fps 或更高? (安卓)
【发布时间】:2014-09-06 21:16:00
【问题描述】:

我正在尝试为 Android 创建一个 2d 游戏,目前我正在努力实现大约 30 fps 的恒定 fps(我认为 30 对于手机游戏来说已经足够了)。我正在使用 SurfaceView 和一个非常简单的游戏循环:

 while (isOk==true)
    {
        if (!holder.getSurface().isValid()) continue;

        c = holder.lockCanvas();

        update();
        draw(c);
        holder.unlockCanvasAndPost(c);

        t2= System.currentTimeMillis();
        all++;
        if ( mspf /* miliseconds per frame, and it's set to 1000/30 */ -(t2-t1)  > 0 )
        {
            try {
                t.sleep(mspf-(t2-t1));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        else
                Log.w("Super",String.valueOf(mspf-(t2-t1))+ " o " + String.valueOf(++total)+ "out of " +String.valueOf(all));

        t1=Math.max( t1+mspf, t2);
    }

这是游戏循环。您可能已经注意到,我使用该 Log.w 来查看在多少帧中我还有一些剩余时间到 Thread.sleep。结果不是那么好。我有大约 10-25% 的帧要运行超过 1000/30 毫秒:

09-07 00:00:39.069: W/Super(28328): -1 o 4395out of 16353
09-07 00:00:39.109: W/Super(28328): -1 o 4396out of 16354
09-07 00:00:39.209: W/Super(28328): 0 o 4397out of 16357
09-07 00:00:40.269: W/Super(28328): -4 o 4398out of 16389
09-07 00:00:40.299: W/Super(28328): -3 o 4399out of 16390
09-07 00:00:40.339: W/Super(28328): -4 o 4400out of 16391
09-07 00:00:40.379: W/Super(28328): -4 o 4401out of 16392
09-07 00:00:40.409: W/Super(28328): -5 o 4402out of 16393
09-07 00:00:40.459: W/Super(28328): -8 o 4403out of 16394
09-07 00:00:40.499: W/Super(28328): -7 o 4404out of 16395
09-07 00:00:40.529: W/Super(28328): -3 o 4405out of 16396
09-07 00:00:40.569: W/Super(28328): -5 o 4406out of 16397
09-07 00:00:40.609: W/Super(28328): -5 o 4407out of 16398
09-07 00:00:40.639: W/Super(28328): -3 o 4408out of 16399
09-07 00:00:40.679: W/Super(28328): -3 o 4409out of 16400
09-07 00:00:40.719: W/Super(28328): -4 o 4410out of 16401

在 draw 中,我没有太多要绘制的位图。 (大约 15 个位图,包括一个着色器、一个用于某些效果的透明蒙版和一个缩放)。所有位图都从资源(仅 PNG 文件)的 create 方法中加载。

public void draw(Canvas c)
{


    c.drawBitmap(background,0,0,null);
    c.drawBitmap(m_image.get(m_cn),400 , 100, test);


    c.drawBitmap(start_menu,10 ,10 , test);
    c.drawBitmap(inventory_menu, 200 , 10, test);
    c.drawBitmap(shop_menu,400  , 10, test);
    c.drawBitmap(options_menu, 10 , 300, test);
    c.drawBitmap(facebook_menu,200 , 300, test);





    light.drawColor(Color.TRANSPARENT, Mode.CLEAR);


    light_matrix.setScale(scale_factor, scale_factor);
    light_matrix.postTranslate(light_x-mask_aux.getWidth()/2*scale_factor, light_y-mask_aux.getHeight()/2*scale_factor);
    light.drawBitmap(mask_aux, light_matrix, test);


    copy_the_screen.drawBitmap(buffer.get(b_activ),0,0,test);
    sh =  new BitmapShader(screen_copy,Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    pnt.setShader(sh);

    c.drawARGB(200, 0, 0,0);
    c.drawBitmap(mask,0,0 ,pnt);


}

update 方法对复杂性没有任何意义,因此在这两个函数中需要做一些事情以实现更好的 fps。帮助我,请原谅我的英语不好。

好的。更新功能是这样的:

     public void update()
{
    m_time++;
    if (m_time==2)
    {
        m_time=0;
        m_cn++;
        if (m_cn==4)m_cn=0; 
    }

    if (touched==true)
    {
        light_x=t_x;
        light_y=t_y;
        scale_factor=t_y/150;
    }
}

它只修改我从精灵中选择的位图。我没有一个包含所有字符位置的大位图,而是 4 个不同的位图。第二个“如果”指的是一个蒙版图像,它在我触摸的地方创建一个光球。我使用该 light_matrix 来缩放与 Y 坐标(点击事件的)成比例的光球。这就是我使用着色器来创建灯光效果的原因。在这里,看看 OnTouchEvent :

   public boolean onTouchEvent(MotionEvent event) {
    // TODO Auto-generated method stub

    //I put this sleep method here because I noticed it was the lagging the game if i let it run 
    // without a sleep.
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
     // after sleep , i only check for events.

 }

【问题讨论】:

  • 这是在模拟器还是设备上?
  • 我正在使用SurfaceView,到目前为止,我已经制作了许多 2D 游戏而没有问题,我有一些关于为什么会发生这种情况的理论,但我不太确定,如果你可以发布你的'update()' 方法并描述什么是 'light' 和 'light_matrix' 我将能够发布答案
  • @TomTsagk 我在帖子中添加了您想要的信息。
  • @MikeM。我直接在设备上运行它。

标签: java android surfaceview frame-rate


【解决方案1】:

首先,每帧都创建新对象是一种不好的技术,所以采取以下做法:

sh =  new BitmapShader(screen_copy,Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

并将其移动到构造函数(或在您初始化游戏的地方),我不确定您是否需要更改 BitmapShader 每帧因为 screen_copy 更改,但如果有办法避免制作每一帧都是新的,这是最好的。

private BitmapShader sh;

@Override
public void surfaceCreated(SurfaceHolder holder)
{
    //...

    sh =  new BitmapShader(screen_copy,Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}

draw 方法的其余部分看起来不错。

onTouchEvent 看起来很奇怪,我不确定你为什么会在那里使用Thread.sleep(int),但我怀疑这是个大问题。

现在我仔细查看了您的主循环,它看起来有点奇怪,尤其是这一行

t1=Math.max( t1+mspf, t2);

我认为这就是一切发生的原因,让我重新编写你的主循环,你可以尝试并告诉我结果

while (isOk) /*you can skip the "==true" part*/
{
    if (!holder.getSurface().isValid()) continue;

    //Start Frame
    t1 = System.currentTimeMillis();

    //Canvas and drawing
    c = holder.lockCanvas();

    update();
    draw(c);
    holder.unlockCanvasAndPost(c);

    //End Frame
    t2= System.currentTimeMillis();
    all++;
    if ( t2 -t1 > mspf )
    {
        try {t.sleep(mspf-(t2-t1));} 
            catch (InterruptedException e) {e.printStackTrace();}
    }
    else
        Log.w("Super",String.valueOf(mspf-(t2-t1))+ " o " + String.valueOf(++total)+ "out of " +String.valueOf(all));
}

【讨论】:

  • 我无法停止重新创建 BitmapShader,因为正如您所猜测的那样,参数几乎每一帧都会发生变化。我用 t1 和 t2 尝试过,但没有收到更好的结果。无论如何,谢谢。
【解决方案2】:

另一个答案是努力优化您的代码,所以我将离开它并讨论使用一些不同的方法。

当您使用 Canvas 在 SurfaceView 的表面上进行渲染时,所有渲染都在软件中执行。随着显示器的像素越来越多,您的应用程序将不得不接触越来越多的内存,并且它会变得越来越慢。您可以通过使用setFixedSize() 为表面设置固定大小来避免这种情况——例如,请参阅Grafika 中的“硬件缩放器练习器”活动。

使用 GPU 可以获得更好的性能。对于 SurfaceView 表面,这意味着使用 OpenGL ES。网上有很多教程;有关使用 GLES 编写的 2D 游戏的简单示例,请参阅Android BreakoutGrafika 中的大多数示例都是基于 GLES2 的。 (Grafika GLES 代码更适合重用。在某些时候,只使用开源游戏引擎是有意义的。)

在游戏循环中使用睡眠调用,并假设您将获得可靠的帧速率,这通常是一个坏主意。有关替代方案的解释和建议,请参阅 Android System-Level Graphics Architecture 文档的“game loops”部分。 SurfaceView 生命周期部分也可能有用。

【讨论】:

  • 那个 setFIxedSize() 帮助了我,但这还不够。不过,我确信我会在您发送给我的链接中找到解决方案。关于 sleep 方法,我确信这不是正确的方法,但我找不到任何其他不使用它的解决方案。我会阅读您提供给我的这些替代方案,我会尽快提供反馈。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多