Androidアプリ開発開始/SurfaceView
今週はcocos2dによる様々なActionに関する続きを書く予定だったのですが、Androidアプリを作成することになった影響で、現在絶賛Android勉強中につき、そのことについて書きます。
Androidでゲームを作成する際、通常のViewでは描画回数(FPS)が物足りないということで、SurfaceViewでいろいろいじくりまわしております。Objective-Cに慣れていた影響で、Javaに違和感を覚えていましたが、ここ1週間でだいぶよくなりました。
環境構築やおすすめサイトなど
のインストール方法については、
Android Developers 公式ドキュメントの日本語化バージョン(非公式)は、
Android 開発ガイド - ソフトウェア技術ドキュメントを勝手に翻訳 をご参考に!
■ その他おすすめサイト
→ Logの出力方法やボタンの作成方法など、基本的なことが簡潔に書いてあります。
・ 私が参考にしたAndroidアプリ開発情報をまとめてみました
→ このサイトさえみればAndroidの初級者が知るべき情報のほぼすべてが揃っています!
SurfaceView
1、SurfaceViewはViewのサブクラス
java.lang.Object
↳ android.view.View
↳ android.view.SurfaceView
という構造。
2、別スレッドで動くため高速描画が可能
View関連のUI部品などは、メインスレッドで動くいているので、画面のタッチ、キー入力など、UIに関するイベントも同時に処理しなければならないため、描画処理だけに集中できないみたいなのですが、SurfaceViewは別スレッドで動いているため描画が速くなる。
3、SurfaceViewHolder.Callback と Ruunable インタフェースを実装し、下記のmethodをoverrideする
public class SampleSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable { public void sufaceCreated(SurfaceHolder holder) { } public void surfaceChanged(SurfaceHolder holder) { } public void surfaceDestroyed(SurfaceHolder holder) { } public void run() { //別スレッドで動かす、ゲームのメインループを記述(描画もここ) }
*View,SurfaceView,GLSurfaceViewについては、ここ(英語)をご覧ください。
*SurfaceViewのサンプルアプリは「Lunar Lander」がオススメ。
実際にSurfaceViewで画像を描画してみる
の第1章 Part3 で紹介されているサンプルアプリをいろいろいじってみたのをご紹介します。
(サンプルはこちら)
*package は省略 import android.app.Activity; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Vibrator; import android.view.Window; public class ShoootActivity extends Activity { //背景画像 static Bitmap bgimage; //背景画像の上に表示される画像 static Bitmap icon; @Override // Activityのライフサイクルのメソッド。生成時に呼ばれる public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // タイトルバーを削除 requestWindowFeature(Window.FEATURE_NO_TITLE); // SurfaceViewを登録 setContentView(new ShoootSurfaceView(this)); } @Override // Activityのライフサイクルのメソッド public void onResume(){ super.onResume(); // リソースから画像ファイルを読み込む Resources res = getResources(); // リソースの取得 bgimage = BitmapFactory.decodeResource(res, R.drawable.bg); icon = BitmapFactory.decodeResource(res, R.drawable.icon); } @Override // Activityのライフサイクルのメソッド public void onPause(){ super.onPause(); finish(); // onDestroy()が呼ばれる } @Override // Activityのライフサイクルのメソッド。終了時に呼ぶ public void onDestroy() { super.onDestroy(); } }
*package は省略 import android.content.Context; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.MotionEvent; public class ShoootSurfaceView extends SurfaceView{ private ShoootSurfaceHolderCallback cb = null; public ShoootSurfaceView(Context context) { super(context); // TODO Auto-generated constructor stub SurfaceHolder holder = getHolder(); cb = new ShoootSurfaceHolderCallback(); holder.addCallback(cb); } @Override // SurfaceViewのサイズを取得 protected void onSizeChanged(int w, int h, int oldw, int oldh){ cb.vieww = w; cb.viewh = h; } }
import java.util.Iterator; import java.util.LinkedList; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.Paint.Style; import android.view.MotionEvent; import android.view.SurfaceHolder; public class ShoootSurfaceHolderCallback implements SurfaceHolder.Callback, Runnable { private SurfaceHolder holder = null; private Thread thread = null; private boolean isThreadrunning = true; int vieww, viewh; // SurfaveViewの幅と高さ // コンストラクタ public ShoootSurfaceHolderCallback(){ } // コールバック関数 public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) { vieww = width; viewh = height; } // コールバック関数 public void surfaceCreated(SurfaceHolder holder) { // 各種の初期処理。スレッドの開始前に this.holder = holder; // スレッドの開始 thread = new Thread(this); thread.start(); } // コールバック関数 public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub isAttached = false; // スレッドの終了 boolean retry = true; synchronized (this.holder) { isThreadrunning = false; } while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { } } thread = null; } public void run() { // TODO Auto-generated method stub // ゲームのメインループ while (isThreadrunning) { synchronized (holder) { // 描画の処理 Canvas canvas = holder.lockCanvas(); // ロックをかける drawOnCanvas(canvas); holder.unlockCanvasAndPost(canvas); // ロックを解除 // FPS処理 time1forfps = System.currentTimeMillis(); if(time1forfps - time2forfps > 1000){ framerate = framecount; framecount = 0; time2forfps = time1forfps; } framecount++; } } // 描画メソッド public void drawOnCanvas(Canvas canvas){ // 背景描画 //draw background image canvas.drawBitmap(ShoootActivity.bgimage, bgimagex, 0, null); //draw icon image canvas.drawBitmap(ShoootActivity.icon, 93, 185, null); // FPS表示 //canvas.drawText("FPS : " + String.valueOf(framerate), 1, 40, painthit); } }
これで、背景とicon画像がViewを用いたときよりも高速に描画されるはず。
サンプルアプリはシューティングゲームを実装しているため、特にゲームのメインループ内がとても複雑になってしまっていますが、画像を表示させるだけならこれでも動きます。
月曜からの課題
1、サンプルコードのAPI Demosがうまく起動しない。
実行させてもエラーとなってしまいます。。。
一度だけ起動できたんだけどな。。。
とりあえず、このサンプルが一番参考になるみたいっすな。
2、SurfaceViewで表示させて画像動作の数式を考える
いろいろなサンプルを参考にしながらゲームループないでうまく動かす方法を探ってみます。