Java 画到画布的速度太慢,如何实现恒定的30 fps或更高?(安卓)

Java 画到画布的速度太慢,如何实现恒定的30 fps或更高?(安卓),java,android,surfaceview,frame-rate,Java,Android,Surfaceview,Frame Rate,我正在尝试为Android创建一个2d游戏,目前我正在努力实现大约30 fps的固定fps(我认为30 fps对于一个手机游戏来说已经足够了)。我正在使用SurfaceView和一个非常直接的游戏循环: while (isOk==true) { if (!holder.getSurface().isValid()) continue; c = holder.lockCanvas(); update(); draw(c)

我正在尝试为Android创建一个2d游戏,目前我正在努力实现大约30 fps的固定fps(我认为30 fps对于一个手机游戏来说已经足够了)。我正在使用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/超级(28328):-16353人中的1人4395人
09-07 00:00:39.109:W/Super(28328):-16354人中的1人4396人
09-07 00:00:39.209:W/Super(28328):16357人中的0人4397人
09-07 00:00:40.269:W/超级(28328):-16389人中的4人4398人
09-07 00:00:40.299:W/超级(28328):-16390人中的3人4399人
09-07 00:00:40.339:W/超级(28328):-16391人中有4400人
09-07 00:00:40.379:W/Super(28328):-16392中的4401个
09-07 00:00:40.409:W/超级(28328):-16393中的5个4402
09-07 00:00:40.459:W/超级(28328):-16394人中的8人4403人
09-07 00:00:40.499:W/超级(28328):16395人中的-7人4404人
09-07 00:00:40.529:W/超级(28328):-16396人中的3人4405人
09-07 00:00:40.569:W/超级(28328):-16397人中的5人4406人
09-07 00:00:40.609:W/超级(28328):-16398中的5个4407
09-07 00:00:40.639:W/超级(28328):-16399人中的3人4408人
09-07 00:00:40.679:W/超级(28328):-16400人中的3人4409人
09-07 00:00:40.719:W/超级(28328):16401中的-4 o 4410

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

更新方法对于复杂性来说毫无意义,因此为了实现更好的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个不同的位图。第二个“如果”是指一个遮罩图像,它在我触摸的地方创建一个光球。我使用该灯光矩阵缩放灯光球,使其与(单击事件的)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.

 }

首先,在每一帧中创建新对象是一种不好的技术,因此请采用以下方法:

sh =  new BitmapShader(screen_copy,Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
并将其移动到构造器(或在您初始化游戏的位置),我不确定是否需要更改
BitmapShader
每一帧,因为屏幕拷贝会更改,但如果有办法避免每一帧都创建一个新的,那就最好了

private BitmapShader sh;

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

    sh =  new BitmapShader(screen_copy,Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
}
绘制方法的其余部分看起来很好

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

另一个答案是优化您的代码,所以我将留下来讨论使用一些不同的方法

使用画布在SurfaceView的曲面上渲染时,所有渲染都在软件中执行。随着显示器的像素越来越多,你的应用程序将不得不接触越来越多的内存,而且速度会越来越慢。您可以通过使用
setFixedSize()
为曲面设置固定大小来避免这种情况——有关示例,请参见中的“硬件定标器练习器”活动

通过使用GPU,您将获得更好的性能。对于SurfaceView曲面,这意味着使用OpenGL ES。网上有很多教程;有关使用GLES编写的2D游戏的简单示例,请参见。中的大多数示例都基于GLES2。(Grafika-GLES代码经过了更好的重用。在某种程度上,只使用开源游戏引擎是有意义的。)


在游戏循环中使用睡眠调用,并假设您将获得可靠的帧速率,通常是一个坏主意。有关替代方案的解释和建议,请参阅Android系统级图形架构文档的“”部分。关于SurfaceView生命周期的部分也可能有用。

这是在模拟器上还是在设备上?我正在使用
SurfaceView
,到目前为止,我已经制作了很多2D游戏,没有问题,我有一些理论来解释为什么会发生这种情况,但我不太确定,如果你能发布你的“更新()方法,并描述什么是“光”和“光矩阵”,我将能够发布answer@TomTsagk我在帖子中添加了你想要的信息。@MikeM。我直接在设备上运行它。我无法停止重建BitmapShader,因为正如你所猜测的,参数几乎每一帧都会改变。我尝试了t1和t2,但没有收到任何更好的结果。无论如何,谢谢。setFIxedSize()帮助了我,但这还不够。不过,我相信我会在你发给我的链接中找到解决方案。关于睡眠方法,我确信这不是正确的方法,但我找不到其他不使用它的解决方案。我将阅读您提供给我的这些备选方案,并尽快提供反馈。谢谢
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));
}