2014-09-06 40 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