2014-09-06 47 views
2

我正在尝试创建一款适用于Android的2D游戏,目前我正在努力实现大约30帧/秒的恒定帧数(我认为30对于手机游戏足够了)。我正在使用SurfaceView和一个非常直接的游戏循环:画到画布太慢,我如何达到恒定的30 fps或更多? (Android)

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%是exceede 1000年至1030年毫秒的帧的运行:

09-07 00:00:39.069:W /超(28328):○4395out的16353
09 -1 -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 4398 out of 16389
09-07 00:00:40.299:W/Super(28328):-3 o 4399 out of 16390
09-07 00:00 :40.339:W/Super(28328):-4 o 4400out 16391
09-07 00:00:40.379:W/Super(28328):-4 o 4 401out 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 4403out 16394
09-07 00:00:40.499:W/Super(28328):-7 o 4404out 16395
09-07 00:00:40.529:W/Super(28328):-3 o 4405out 16396
09-07 00:00:40.569:W/Super(28328):-5 o 4406out 16397
09-07 00:00:40.609:W/Super(28328):-5 o 4407out 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 /超(28328):-4Ø4410out的16401

在平局,我没有那么多的位图绘制。 (大约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个不同的位图。第二个“if”指的是一个掩模图像,它在我触摸的地方创建一个光球。我使用该light_matrix来缩放与Y坐标(click事件)成比例的光球。这就是为什么我使用着色器来创建灯光效果的原因。在这里,拿上的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. 

} 
+0

这是模拟器还是设备? – 2014-09-06 21:52:17

+0

我正在使用'SurfaceView',迄今为止我制作了许多2D游戏而没有任何问题,我有一些理论知道为什么会发生这种情况,但我不太确定,如果您可以发布'update()'方法并且还描述了什么'light'和'light_matrix'是我可以发布答案 – TomTsagk 2014-09-06 23:53:30

+0

@TomTsagk 我在帖子中添加了你想要的信息。 – 2014-09-07 10:30:33

回答

2

另一个答案正在努力优化你的代码,所以我会离开这一点,谈谈使用一些不同的方法。

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

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

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

+0

setFIxedSize()帮助了我,但这还不够。不过,我相信我会在您发送给我的链接中找到解决方案。关于睡眠方法,我确信这不是正确的方法,但我找不到任何其他解决方案没有使用它。我会阅读您提供给我的这些替代方案,我会尽快提供反馈意见。谢谢。 – 2014-09-07 16:36:50

4

所有它的坏的技术来创建新的对象每一帧的第一,所以走这条线:

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

我无法停止重新创建BitmapShader,因为你已经猜到参数几乎改变了每一帧。我尝试了t1和t2,但没有收到更好的结果。不管怎么说,还是要谢谢你。 – 2014-09-07 16:25:43