概ねタイトル通りです。
今回はViewの中でもかなり特殊なView、「SurfaceView」を使ってみます。
どこらへんが特殊かというと、スレッドを別にできるというところらしいんですが…。
なんだか難しそうです(;^ω^)
百聞は一見にしかず、サンプルを見ながら説明してみます。
【参考にした書籍とサイト様】
・愚鈍なプログラマーの独り言 「グラフィックス(2)-SurfaceViewによる描画」
・@IT 「SurfaceViewならAndroidで高速描画ゲームが作れる」
やさしいAndroidプログラミング (やさしいシリーズ)
SurficeViewを継承したクラス「SampleSurficeView」を作成
ではさっそく、SurficeViewを作成しましょう。
SurficeViewを継承したクラスを用意します。
今回は「「SampleSurficeView」」という名前にしてみましょう。
新規->クラス->名前「SampleSurficeView」に設定->完了
SampleSurficeView.java
import android.content.Context;
import android.view.SurfaceView;
public class SampleSurficeView extends SurfaceView{
	public SampleSurficeView(Context context) {
		super(context);
	}
}
まだ中身は空っぽですが、これがSurficeViewそのものになります。
SurficeHolderインターフェースを実装したクラス「SampleHolderCallBack」を作成、無限ループの開始と停止を作る
SurficeViewの中で、SurficeHolderインターフェースを実装したクラスを用意します。今回は「SampleHolderCallBack」という名前で作成してみます。
このクラスには
・SruficeHolderインターフェース
・Runnableインターフェース
の2つのインターフェースを作ります。
Eclipseで関数を自動生成すると楽ですね。
・SampleHolderCallBack.java
import android.view.SurfaceHolder;
public class SampleHolderCallBack implements SurfaceHolder.Callback, Runnable{
	private SurfaceHolder holder = null;
	private Thread thread = null;
	private boolean isAttached = true;
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,	int height) {
		// TODO 自動生成されたメソッド・スタブ
		// 使わない(´・ω・`)
	}
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		// TODO 自動生成されたメソッド・スタブ
		this.holder = holder;
		thread = new Thread(this);
		thread.start(); //スレッドを開始
	}
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO 自動生成されたメソッド・スタブ
	       isAttached = false;
	       thread = null; //スレッドを終了
	}
	@Override
	public void run() {
		// TODO 自動生成されたメソッド・スタブ
		// メインループ(無限ループ)
		while( isAttached ){
			Log.w("テスト", "ループなう");
		}
	}
}
2つのインターフェースの関数には
| SruficeHolderインターフェース | surficeCreated関数 | 
|---|---|
| surficeChanged関数 | |
| surficeDestroyed関数 | |
| Runnableインターフェース | run関数 | 
役割ととしては、
run関数 : 別スレッドで動き続ける、無限ループを作る
surficeCreated関数 :スレッドを開始
surficeDestroyed関数:スレッドを停止
surficeChanged関数 :今回使わない(´・ω・`)
という感じになります。
このクラスを、先ほどのSampleSurficeViewを修正してコールバックに登録します。
・SampleSurficeView.java
public class SampleSurficeView extends SurfaceView{
	private SampleHolderCallBack cb;
	public SampleSurficeView(Context context) {
		super(context);
		SurfaceHolder holder = getHolder();
		cb = new SampleHolderCallBack();
		holder.addCallback(cb);
	}
}
メインのアクティビティでSampleSurficeViewをセットしてさっそく動かしてみましょう。
【実行結果】

何も表示されていませんが、
ログでちゃんとrun関数がループしているのが確認できますね(^ω^)
無限ループ内でCanvasを描画する
無限ループの中に
画面内を動きまわる丸を描画するプログラムを作ってみます。
描画処理は holder.lockCanvas() を使って取得したCanvasに対して行います。
描画処理が終わったら holder.unlockCanvasAndPost でCanvasを開放します。
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.util.Log;
import android.view.SurfaceHolder;
public class SampleHolderCallBack implements SurfaceHolder.Callback, Runnable{
	private SurfaceHolder holder = null;
	private Thread thread = null;
	private boolean isAttached = true;
	  private float dx = 10, dy = 10;
      private float width, height;
      private int   circle_x, circle_y;
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,	int height) {
		// TODO 自動生成されたメソッド・スタブ
		this.width = width;
		this.height = height;
	}
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		// TODO 自動生成されたメソッド・スタブ
		this.holder = holder;
		thread = new Thread(this);
		thread.start(); //スレッドを開始
	}
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO 自動生成されたメソッド・スタブ
	       isAttached = false;
	       thread = null; //スレッドを終了
	}
	@Override
	public void run() {
		// TODO 自動生成されたメソッド・スタブ
		// メインループ(無限ループ)
		while( isAttached ){
			//丸の表示位置を動かす
			if( circle_x < 0 || circle_x > this.width ){
				dx = -dx;
			}
			if( circle_y < 0 || circle_y > this.height ){
				dy = -dy;
			}
			circle_x += dx;
			circle_y += dy;
			//描画処理を開始
			Canvas canvas = holder.lockCanvas();
			canvas.drawColor(0,PorterDuff.Mode.CLEAR );
			Paint paint = new Paint();
	        paint.setColor(Color.WHITE);
	        canvas.drawCircle( circle_x, circle_y, 50, paint);
			//描画処理を終了
			holder.unlockCanvasAndPost(canvas);
		}
	}
}
【実行結果】

スリープ処理を入れてゲームの速度を調整する
run関数での無限ループを速度を調整しないと、
スマートフォンの性能によりゲーム内の時間がまちまちになってしまいます。
そこで、一秒間でループが60回繰り返すようにスリープ処理を入れます。
多くのゲームでは1ループを「1フレーム」と呼び、
1秒間あたり60フレーム(60fps)で動くように調整されています。
・SampleHolderCallBack.java
private long t1 = 0, t2 = 0; // スリープ用変数
	@Override
	public void run() {
		// TODO 自動生成されたメソッド・スタブ
		// メインループ(無限ループ)
		while( isAttached ){
			t1 = System.currentTimeMillis();
			//丸の表示位置を動かす
			if( circle_x < 0 || circle_x > this.width ){
				dx = -dx;
			}
			if( circle_y < 0 || circle_y > this.height ){
				dy = -dy;
			}
			circle_x += dx;
			circle_y += dy;
			//描画処理を開始
			Canvas canvas = holder.lockCanvas();
			canvas.drawColor(0,PorterDuff.Mode.CLEAR );
			Paint paint = new Paint();
	        paint.setColor(Color.WHITE);
	        canvas.drawCircle( circle_x, circle_y, 50, paint);
			//描画処理を終了
			holder.unlockCanvasAndPost(canvas);
			// スリープ
			t2 = System.currentTimeMillis();
			if(t2 - t1 < 16){ // 1000 / 60 = 16.6666
				try {
					Thread.sleep(16 - (t2 - t1));
				} catch (InterruptedException e) {
				}
			}
		}
	}
これでゲームプログラムに必要な基幹部分は大体できました。
大雑把な説明ですみません。
次の記事ではプレイヤーからの入力の処理を入れてゲームっぽいサンプルを作ってみます。
  				  			
ピンバック: 【Android】SurfaceViewを使ってゲームっぽいアプリを作ってみる(入力編) | 桜花満開
ピンバック: 【Android】カメラ機能を使ってプレビューをSurfaceViewに表示する方法 | 桜花満開
ピンバック: 【Android】SurfaceViewを使ってゲームっぽいアプリを作ってみる(ゲーム内容編) | 桜花満開