SlideShare a Scribd company logo
日本Androidの会 日高 正博




   日本Androidの会/日高正博   2010/10/10   1
   日高 正博(ひだか まさひろ)
    ◦ 日本Androidの会/関西支部
   Android開発情報サイト Tech Booster 運営
    http://techbooster.jpn.org/
    65記事を超えて週に3本ペースで増殖中!




Twitter
Account
@mhidaka



                         日本Androidの会/日高正博   2010/10/10   2
1. Androidとゲーム
• 概要/スレッド構成/注意点

2. ゲームでの描画
• SurfaceView/時系列処理

3. メモリ管理
• GC/OOM/確保の方法

4. まとめ

                      日本Androidの会/日高正博   2010/10/10   3
日本Androidの会/日高正博   2010/10/10   4
ユーザ体験:“たのしい!”のために。
• AndroidマーケットでGameは2番目の人気
• より高速に、より小さく!大きいゲームはアンインストールされやすい

ユーザレスポンス
• 処理には案外時間がかかるものも多い。見せ方を工夫。
 • 例:処理中であればスピナー/プログレスバーで通知する
 • ネットワークアクセス(ライセンスサーバとの通信など)
 • スコアの保存やデータのバックアップなど



                       日本Androidの会/日高正博   2010/10/10   5
デーモンスレッド&
 シングル・スレッド・モデル
                             ユーザスレッド

• Mainスレッド(UI Thread   • ユーザスレッド:
 • Activityに関する処理        処理が終わる(return)まで
• ライフサイクル                プログラムは終了できない

 • onCreate/OnPause    • デーモンスレッド:
                         プログラム終了時にスレッ
• 各UIパーツ(Viewなど)         ドの実行終了を待たない
  の描画処理



                       日本Androidの会/日高正博   2010/10/10   6
描画・ロジック・UIでスレッド分割
• 描画/CPU処理などで応答遅延を防ぐ
                                             UI
• タスクの占有はANRの対象(Application                  • 入力、レスポンス

  Not Responding)                            • Activityの処理


• リアルタイムゲームであれば                画面への                               ユーザ入力
  30FPSが理想                     フィードバック                            TimerEvent
• 将棋などCPUがんばる系のゲーム
  logic大事(Android2.2のJIT?)   グラフィック                          logic
                             • 画像処理、エフェクト                    • ゲームの論理構造
• タッチイベント飛び過ぎ                • SurfaceView                   • 無限ループ
  onTouchEventの中でSleepする
                                                  画像処理

                               日本Androidの会/日高正博      2010/10/10            7
   AsyncTask                メソッド名                             内容
                             onPreExecute()                    事前準備の処理を記述する
    ◦ 非同期処理                  doInBackground(Params...)         バックグラウンドで行う処理を記述する
                             onProgressUpdate(Progress...)     進捗状況をUIスレッドで表示する処理を記述する

                             onPostExecute(Result)             バックグラウンド処理が完了し、UIスレッドに反映す
                                                               る処理を記述する


    public class MonochromeTask extends AsyncTask<Bitmap, Integer, Bitmap> {

               @Override
               protected Bitmap doInBackground(Bitmap... bitMap) {
                            Bitmap outBitMap = bitMap[0].copy(Bitmap.Config.ARGB_8888, true);




   Runnable,Thread
    ◦ Javaマルチスレッド処理(同期、優先度制御etc)が可能
    ◦ Runnableインターフェイス、Threadクラス。あとで解説します


                                                         日本Androidの会/日高正博    2010/10/10         8
Android端末のインターフェイス
• キーボード、タッチパネル、センサー、ハードキー

機種が多いので想定が難しい
                                      タッチパ           セン
• スマートフォン? TV ? タブレット?                 ネル            サー

   おすすめはインターフェイスの変              キー                       ハード
                                 ボード          インター        キー
    換部を持つこと                                   フェイス
      開発が進んでからでOK                             変換

      ユーザ・機器に合わせた設定
                                                     input
       変更が簡単に
                                             logic

                         日本Androidの会/日高正博   2010/10/10          9
日本Androidの会/日高正博   2010/10/10   10
Canvas
• 描画にかかわる処理をすべてUIスレッドで行う
• 以下の2つに比べると低速だけど簡単

SurfaceView
• 演算を別スレッドで実施してUIスレッドで描画
• CanvasとSurfaceViewはCPU処理

GLSurfaceView
• OpenGLを使った描画処理を行う。非常に高速。
• ハードウェア処理、機種依存が多少あるので注意。地獄。



                             日本Androidの会/日高正博   2010/10/10   11
SurfaceView
• 画面(Surface)を描画する専用スレッドを提供する
  (資源ロックが発生)
• 描画には、SurfaceHolderというインターフェイスを利用

実装
• 描画処理はSurfaceHolderのコールバックとして実装する
  • SurfaceHolder.Callback.surfaceCreated()
  • SurfaceHolder.Callback.surfaceChanged()
  • SurfaceHolder.Callback.surfaceDestroyed()


                                  日本Androidの会/日高正博   2010/10/10   12
ActivityにViewを設定
   public class surfaceViewActivity extends Activity {
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new sampleSurfaceView(this));
     }
   }

 SurfaceViewのコンストラクタ
   private Bitmap mImage;
   //コンストラクタ
   public sampleSurfaceView(Context context) {
             super(context);

           getHolder().addCallback(this);
           mImage = BitmapFactory.decodeResource(getResources(),
   R.drawable.bakeneko);
   }
                                           日本Androidの会/日高正博   2010/10/10   13
SurfaceView生成logic
  //SurfaceView生成時に呼び出される
  public void surfaceCreated(SurfaceHolder holder) {
      //初期描画(生成タイミングで描画する必要があるもの)
      //Canvasの取得(マルチスレッド環境対応のためLock)
      Canvas canvas = holder.lockCanvas();
      Paint paint = new Paint();
      paint.setTextSize(24);
      paint.setColor(Color.WHITE);


      //描画処理(Lock中なのでなるべく早く)
      canvas.drawBitmap(mImage, 0, 0, paint);
      canvas.drawText("TechBooster",0,200,paint);

      //LockしたCanvasを解放、ほかの描画処理スレッドがあればそちらに。
      holder.unlockCanvasAndPost(canvas);
  }



                                                 日本Androidの会/日高正博   2010/10/10   14
Runnableインターフェイス
  • ユーザスレッドの作成。別スレッドで時間、移動量を決定
   public class sampleSurfaceView extends SurfaceView
   implements SurfaceHolder.Callback, Runnable{
             private Thread mLooper;
        (省略)
   }


//SurfaceView変更時に呼び出される
   //SurfaceView生成時に呼び出される
public void surfaceChanged(SurfaceHolder holder, int{format, int width, int height) {
   public void surfaceCreated(SurfaceHolder holder)
         //スレッド処理を開始
         if(mLooper != null ){
             //スレッドの生成
             mHolder = holder;
                   mLooper.start();
         } mLooper = new Thread(this);
} }



                                               日本Androidの会/日高正博   2010/10/10        15
   RunnableインターフェイスのRunメソッド
//スレッドによるSurfaceView更新処理
public void run() {
           while (mLooper != null) {

                     //描画処理
                     doDraw();

                     //位置更新処理
                     //処理落ちによるスローモーションをさけるため現在時刻を取得
                     long delta = System.currentTimeMillis() - mTime;
                     mTime      = System.currentTimeMillis();

                     //次の描画位置
                     int nextPosition = (int)( ( delta / 1000.0 ) * 200 ); //1秒間に200px動くとして

                     //描画範囲の設定
                     if(mPositionTop + nextPosition < mHeight ){
                                mPositionTop += nextPosition;
                     }
                     (省略)
          }
}

                                                    日本Androidの会/日高正博   2010/10/10             16
   代表的なレイアウトは4つ




                   日本Androidの会/日高正博   2010/10/10   17
   FrameLayoutで上から描画を重ねる

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    FrameLayout frameLayout = new FrameLayout(this);
    setContentView(frameLayout);

    frameLayout.addView(new sampleSurfaceView(this),new
ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));

     //サンプル表示用のボタンを作成
     Button button = new Button(this);
     button.setText("Start Button");
     frameLayout.addView(button, new
ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
   }



                                                    日本Androidの会/日高正博   2010/10/10   18
日本Androidの会/日高正博   2010/10/10   19
ガベージコレクション
• JavaHeapが足りなくなるとメモリ確保のためGCが走る
• 一度GCすると100~200msはゲームが止まる




05-14 17:43:25.916: DEBUG/dalvikvm(51):
                      GC freed 637 objects / 29528 bytes in 86ms
                                    日本Androidの会/日高正博   2010/10/10   20
Activityがメモリを食いつぶす
 • 利用可能なメモリは24MB。かならずこの範囲に収める

OutOfMemoryError
 • GCによるメモリ・アロケーション時。
   JavaHeap内で、要求サイズ分の空きが無ければ発生

05-14 17:16:45.035: INFO/ActivityManager(51): Config changed:
{ scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/2 nav=3/1
orien=1 layout=18}
05-14 17:16:45.075: ERROR/dalvikvm-heap(187): 2457600-byte external
allocation too large for this process.
05-14 17:16:45.075: ERROR/(187): VM won't let us allocate 2457600
bytes…省略…
05-14 17:16:45.204: ERROR/AndroidRuntime(187):
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
                                     日本Androidの会/日高正博   2010/10/10    21
   Androidのメモリは潤沢ではない
    ◦ 自分が使うメモリ量を計算する
   リアルタイムゲームでは
    開始前に全部のデータを読み込むといい。
    ◦ マップデータ、シナリオ、音声、キャラクターなどなど。
   メモリリークに気をつける
    ◦ 意図せずオブジェクトが解放されなくてメモリを握ったままに
      なることも。
    ◦ 手っ取り早いのはDDMSでメモリ量を確認。
      パフォーマンス計測はLog.dやTraceViewもおすすめ。



                       日本Androidの会/日高正博   2010/10/10   22
日本Androidの会/日高正博   2010/10/10   23
ユーザインターフェイス
• レスポンスを大事に。入力デバイスを選べるように。
• ゲームロジックを分割してマルチスレッド化

View
• 描画はゲーム特性に合わせて最適なものを。
• ボードゲーム:Canvas、リアルタイム系:OpenGL/SurfaceView

メモリ管理
• GCが走るとゲームは止まる
• メモリ使用量を把握する、リークはDDMS/MATなどで確認

                         日本Androidの会/日高正博   2010/10/10   24
   ご清聴ありがとうございました




                 日本Androidの会/日高正博   2010/10/10   25

More Related Content

Android gameprogramming

  • 1. 日本Androidの会 日高 正博 日本Androidの会/日高正博 2010/10/10 1
  • 2. 日高 正博(ひだか まさひろ) ◦ 日本Androidの会/関西支部  Android開発情報サイト Tech Booster 運営 http://techbooster.jpn.org/ 65記事を超えて週に3本ペースで増殖中! Twitter Account @mhidaka 日本Androidの会/日高正博 2010/10/10 2
  • 3. 1. Androidとゲーム • 概要/スレッド構成/注意点 2. ゲームでの描画 • SurfaceView/時系列処理 3. メモリ管理 • GC/OOM/確保の方法 4. まとめ 日本Androidの会/日高正博 2010/10/10 3
  • 5. ユーザ体験:“たのしい!”のために。 • AndroidマーケットでGameは2番目の人気 • より高速に、より小さく!大きいゲームはアンインストールされやすい ユーザレスポンス • 処理には案外時間がかかるものも多い。見せ方を工夫。 • 例:処理中であればスピナー/プログレスバーで通知する • ネットワークアクセス(ライセンスサーバとの通信など) • スコアの保存やデータのバックアップなど 日本Androidの会/日高正博 2010/10/10 5
  • 6. デーモンスレッド& シングル・スレッド・モデル ユーザスレッド • Mainスレッド(UI Thread • ユーザスレッド: • Activityに関する処理 処理が終わる(return)まで • ライフサイクル プログラムは終了できない • onCreate/OnPause • デーモンスレッド: プログラム終了時にスレッ • 各UIパーツ(Viewなど) ドの実行終了を待たない の描画処理 日本Androidの会/日高正博 2010/10/10 6
  • 7. 描画・ロジック・UIでスレッド分割 • 描画/CPU処理などで応答遅延を防ぐ UI • タスクの占有はANRの対象(Application • 入力、レスポンス Not Responding) • Activityの処理 • リアルタイムゲームであれば 画面への ユーザ入力 30FPSが理想 フィードバック TimerEvent • 将棋などCPUがんばる系のゲーム logic大事(Android2.2のJIT?) グラフィック logic • 画像処理、エフェクト • ゲームの論理構造 • タッチイベント飛び過ぎ • SurfaceView • 無限ループ onTouchEventの中でSleepする 画像処理 日本Androidの会/日高正博 2010/10/10 7
  • 8. AsyncTask メソッド名 内容 onPreExecute() 事前準備の処理を記述する ◦ 非同期処理 doInBackground(Params...) バックグラウンドで行う処理を記述する onProgressUpdate(Progress...) 進捗状況をUIスレッドで表示する処理を記述する onPostExecute(Result) バックグラウンド処理が完了し、UIスレッドに反映す る処理を記述する public class MonochromeTask extends AsyncTask<Bitmap, Integer, Bitmap> { @Override protected Bitmap doInBackground(Bitmap... bitMap) { Bitmap outBitMap = bitMap[0].copy(Bitmap.Config.ARGB_8888, true);  Runnable,Thread ◦ Javaマルチスレッド処理(同期、優先度制御etc)が可能 ◦ Runnableインターフェイス、Threadクラス。あとで解説します 日本Androidの会/日高正博 2010/10/10 8
  • 9. Android端末のインターフェイス • キーボード、タッチパネル、センサー、ハードキー 機種が多いので想定が難しい タッチパ セン • スマートフォン? TV ? タブレット? ネル サー  おすすめはインターフェイスの変 キー ハード ボード インター キー 換部を持つこと フェイス  開発が進んでからでOK 変換  ユーザ・機器に合わせた設定 input 変更が簡単に logic 日本Androidの会/日高正博 2010/10/10 9
  • 11. Canvas • 描画にかかわる処理をすべてUIスレッドで行う • 以下の2つに比べると低速だけど簡単 SurfaceView • 演算を別スレッドで実施してUIスレッドで描画 • CanvasとSurfaceViewはCPU処理 GLSurfaceView • OpenGLを使った描画処理を行う。非常に高速。 • ハードウェア処理、機種依存が多少あるので注意。地獄。 日本Androidの会/日高正博 2010/10/10 11
  • 12. SurfaceView • 画面(Surface)を描画する専用スレッドを提供する (資源ロックが発生) • 描画には、SurfaceHolderというインターフェイスを利用 実装 • 描画処理はSurfaceHolderのコールバックとして実装する • SurfaceHolder.Callback.surfaceCreated() • SurfaceHolder.Callback.surfaceChanged() • SurfaceHolder.Callback.surfaceDestroyed() 日本Androidの会/日高正博 2010/10/10 12
  • 13. ActivityにViewを設定 public class surfaceViewActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new sampleSurfaceView(this)); } } SurfaceViewのコンストラクタ private Bitmap mImage; //コンストラクタ public sampleSurfaceView(Context context) { super(context); getHolder().addCallback(this); mImage = BitmapFactory.decodeResource(getResources(), R.drawable.bakeneko); } 日本Androidの会/日高正博 2010/10/10 13
  • 14. SurfaceView生成logic //SurfaceView生成時に呼び出される public void surfaceCreated(SurfaceHolder holder) { //初期描画(生成タイミングで描画する必要があるもの) //Canvasの取得(マルチスレッド環境対応のためLock) Canvas canvas = holder.lockCanvas(); Paint paint = new Paint(); paint.setTextSize(24); paint.setColor(Color.WHITE); //描画処理(Lock中なのでなるべく早く) canvas.drawBitmap(mImage, 0, 0, paint); canvas.drawText("TechBooster",0,200,paint); //LockしたCanvasを解放、ほかの描画処理スレッドがあればそちらに。 holder.unlockCanvasAndPost(canvas); } 日本Androidの会/日高正博 2010/10/10 14
  • 15. Runnableインターフェイス • ユーザスレッドの作成。別スレッドで時間、移動量を決定 public class sampleSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable{ private Thread mLooper; (省略) } //SurfaceView変更時に呼び出される //SurfaceView生成時に呼び出される public void surfaceChanged(SurfaceHolder holder, int{format, int width, int height) { public void surfaceCreated(SurfaceHolder holder) //スレッド処理を開始 if(mLooper != null ){ //スレッドの生成 mHolder = holder; mLooper.start(); } mLooper = new Thread(this); } } 日本Androidの会/日高正博 2010/10/10 15
  • 16. RunnableインターフェイスのRunメソッド //スレッドによるSurfaceView更新処理 public void run() { while (mLooper != null) { //描画処理 doDraw(); //位置更新処理 //処理落ちによるスローモーションをさけるため現在時刻を取得 long delta = System.currentTimeMillis() - mTime; mTime = System.currentTimeMillis(); //次の描画位置 int nextPosition = (int)( ( delta / 1000.0 ) * 200 ); //1秒間に200px動くとして //描画範囲の設定 if(mPositionTop + nextPosition < mHeight ){ mPositionTop += nextPosition; } (省略) } } 日本Androidの会/日高正博 2010/10/10 16
  • 17. 代表的なレイアウトは4つ 日本Androidの会/日高正博 2010/10/10 17
  • 18. FrameLayoutで上から描画を重ねる public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FrameLayout frameLayout = new FrameLayout(this); setContentView(frameLayout); frameLayout.addView(new sampleSurfaceView(this),new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); //サンプル表示用のボタンを作成 Button button = new Button(this); button.setText("Start Button"); frameLayout.addView(button, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } 日本Androidの会/日高正博 2010/10/10 18
  • 20. ガベージコレクション • JavaHeapが足りなくなるとメモリ確保のためGCが走る • 一度GCすると100~200msはゲームが止まる 05-14 17:43:25.916: DEBUG/dalvikvm(51): GC freed 637 objects / 29528 bytes in 86ms 日本Androidの会/日高正博 2010/10/10 20
  • 21. Activityがメモリを食いつぶす • 利用可能なメモリは24MB。かならずこの範囲に収める OutOfMemoryError • GCによるメモリ・アロケーション時。 JavaHeap内で、要求サイズ分の空きが無ければ発生 05-14 17:16:45.035: INFO/ActivityManager(51): Config changed: { scale=1.0 imsi=310/260 loc=en_US touch=3 keys=2/1/2 nav=3/1 orien=1 layout=18} 05-14 17:16:45.075: ERROR/dalvikvm-heap(187): 2457600-byte external allocation too large for this process. 05-14 17:16:45.075: ERROR/(187): VM won't let us allocate 2457600 bytes…省略… 05-14 17:16:45.204: ERROR/AndroidRuntime(187): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 日本Androidの会/日高正博 2010/10/10 21
  • 22. Androidのメモリは潤沢ではない ◦ 自分が使うメモリ量を計算する  リアルタイムゲームでは 開始前に全部のデータを読み込むといい。 ◦ マップデータ、シナリオ、音声、キャラクターなどなど。  メモリリークに気をつける ◦ 意図せずオブジェクトが解放されなくてメモリを握ったままに なることも。 ◦ 手っ取り早いのはDDMSでメモリ量を確認。 パフォーマンス計測はLog.dやTraceViewもおすすめ。 日本Androidの会/日高正博 2010/10/10 22
  • 24. ユーザインターフェイス • レスポンスを大事に。入力デバイスを選べるように。 • ゲームロジックを分割してマルチスレッド化 View • 描画はゲーム特性に合わせて最適なものを。 • ボードゲーム:Canvas、リアルタイム系:OpenGL/SurfaceView メモリ管理 • GCが走るとゲームは止まる • メモリ使用量を把握する、リークはDDMS/MATなどで確認 日本Androidの会/日高正博 2010/10/10 24
  • 25. ご清聴ありがとうございました 日本Androidの会/日高正博 2010/10/10 25