繁体   English   中英

绘制到画布太慢,如何实现恒定的30 fps或更高? (机器人)

[英]Drawing to canvas is too slow , How do i achieve constant 30 fps or more ? (Android)

我正在尝试为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): - 16353的-1 4395
09-07 00:00:39.109:W / Super(28328): - 16354的-1 o 4396
09-07 00:00:39.209:W / Super(28328):0 4397out of 16357
09-07 00:00:40.269:W / Super(28328):-489 4398
09-07 00:00:40.299:W / Super(28328):-390 4390的16390
09-07 00:00:40.339:W / Super(28328):-43900的16391
09-07 00:00:40.379:W / Super(28328):-4391中的-4401
09-07 00:00:40.409:W / Super(28328): - 15394的-5402-2
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): - 16396中的-4到4405
09-07 00:00:40.569:W / Super(28328): - 16397的-540460
09-07 00:00:40.609:W / Super(28328): - 15398中的-4404
09-07 00:00:40.639:W / Super(28328): - 163 0888的4308
09-07 00:00:40.679:W / Super(28328): - 16400的-400 4409
09-07 00:00:40.719:W / Super(28328): - 16401的-410 4410

在平局中,我没有那么多的位图可供绘制。 (大约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);


}

更新方法对复杂性没有任何意义,因此在这两个函数中有一些事情要做,以便获得更好的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个不同的位图。 第二个“if”指的是一个掩模图像,它在我触摸的地方创建一个光球。 我使用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.

 }

首先,每帧创建新对象是不好的技术,所以请选择以下内容:

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

其余的绘制方法看起来很好。

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

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

使用Canvas在SurfaceView表面上渲染时,所有渲染都在软件中执行。 随着显示器获得更多像素,您的应用将不得不触及越来越多的内存,并且它会变慢。 可以通过设置一个固定的大小,用于与表面避免这种setFixedSize() -参见在“硬件缩放锻炼者”活动Grafika的一个例子。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM