Viewのまとめなのでまじめな匠によるAndroid講座第4回です。 今回はsurfaceviewを使ったゲームアプリの例を作っていきたいです。 始めにプロジェクトを作成します あの楽器にする前にsurfaceviewのひな形を作ります。 頑張った例 そしてsurfaceviewとゲームループをつかった例の雛形 import android.view.SurfaceHolder; import android.view.SurfaceView; public class MyView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private Canvas canvas; // スレッドクラス private Thread thread; private SurfaceHolder holder; public boolean tread_flag; //コンストラクタ public MyView(Context context) { super(context); tread_flag = true; // サーフェイスホルダーの作成 holder = getHolder(); holder.addCallback(this); holder.setFixedSize(getWidth(), getHeight()); } //ディスプレイタッチ時に呼ばれる @Override public boolean onTouchEvent(MotionEvent event) { return true; } public void surfaceCreated(SurfaceHolder holder) { thread = new Thread(this); //ここでrun()が呼ばれる thread.start(); } public void run() { while (tread_flag) { UpDate(); Draw(); } } private void UpDate(){ //TODO 更新内容 } public void Draw() { canvas = getHolder().lockCanvas(); //TODO 描画処理 holder.unlockCanvasAndPost(canvas); } public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { } public void surfaceDestroyed(SurfaceHolder holder) { tread_flag = false; } } このMyviewクラスには public void surfaceCreated(SurfaceHolder holder) { thread = new Thread(this); //ここでrun()が呼ばれる thread.start(); } のthread.start();で新たなスレッドとして呼ばれるrun(自動生成) while (tread_flag) { UpDate(); Draw(); } で無限ループさせてゲームの数値達を更新させるUpDate(自作) これを元にしてアプリを作っていきます。 まずAndroidとは関係ないあの楽器のエフェクト達の座標を管理するクラスを作ります。 円 直線 三角 四角 星 です。 Class Shapes{ public int shapeID;//図形の種類を保存 public int rad;//現在の図形の角度 public int size;//現在の図形の大きさ public int x;//タッチした座標(x) public int y;//タッチした座標(y) public boolean flag;//現在使われているかどうか public int Alpha;//透明度 private int wid;//画面サイズ(x) private int hei;//画面サイズ(y) public point[] point = new point[10];//図形の座標を保存 //コンストラクタ画面サイズを代入 public Shapes(int w, int h) { wid = w; hei = h; for (int i = 0; i < 10; i++) { point[i] = new point(); } } public class point { double x; double y; } } 次に新たに図形が作られたとき(画面がタッチされたとき)に呼ぶようにする public void set(int setx, int sety) { flag = true; // Randomクラスのインスタンス化 Random rnd = new Random(); shapeID = rnd.nextInt(5); rad = rnd.nextInt(180); size = 50; x = setx; y = sety; Alpha = 255 * 2 / 3; switch (shapeID) { case 0: // ☆の初期化 break; case 1: // 四角形の初期化 break; case 2: // 三角形の初期化 break; case 3: // 直線の初期化 } } Switchでかかれた所に各図形の初期化を書いていきます。 まず画面固定になる直線エフェクトの初期化を書きます。 直線を描くためには始点と終点が必要なので求めます。 タッチした点を通る 傾きは乱数で決めた値 にそって求めます。 基本の y = ax + b これよりx = 0 or y = 0 の場所が始点となり それらをプログラムにすると以下のようになりset()での直線の初期化に書きます point[0].x = 0; point[0].y = y - Math.tan(rad) * x; if (point[0].y < 0) { point[0].x = (-point[0].y) / Math.tan(rad); point[0].y = 0; } point[1].x = wid; point[1].y = Math.tan(rad) * wid + y - Math.tan(rad) * x; if (point[1].y > hei) { point[1].x = (hei - y + Math.tan(rad) * x) / Math.tan(rad); point[1].y = hei; } となります。 次に三角の初期化 今回の条件は 中心はタッチした点 だんだん拡大 正三角形 回転する です *******************間違った例です********************** †point[0].x = x; point[0].y = y + size * 2 * triangle; point[1].x = x + size; point[1].y = y - size * 2 * triangle; point[2].x = x - size; point[2].y = y - size * 2 * triangle; // 回転 point temp = new point(); for (int i = 0; i < 3; i++) { temp = point[i]; temp.x -= x; temp.y -= y; double trad = -Math.toRadians(rad); point[i].x = temp.x * Math.cos(trad) - temp.y * Math.sin(trad); point[i].y = temp.x * Math.sin(trad) + temp.y * Math.cos(trad); point[i].x += x; point[i].y += y; } *******************間違った例です********************** †各点は中心より3等分に角度が分けられ距離はsizeとすると x = X + size * cosθ y = Y + size * sinθ となります *******************正しい例です********************** †double trad; for (int i = 0; i < 3; i++) { trad = Math.toRadians(rad + i * 120); point[i].x = x + (size * 2 * triangle) * Math.cos(trad); point[i].y = y + (size * 2 * triangle) * Math.sin(trad); } *******************正しい例です********************** †ちなみにこれは足す角度とループ回数を変えることにより多角形になり 毎フレームごとに呼ばれる図形の更新についてです package aokai.app.touchTest; import java.util.Random; public class Shapes { public int shapeID; public int rad; public int size; public int x; public int y; public boolean flag; public int Alpha; private int wid; private int hei; public point[] point = new point[10]; public Shapes(int w, int h) { wid = w; hei = h; for (int i = 0; i < 10; i++) { point[i] = new point(); } } public void set(int setx, int sety) { flag = true; // Randomクラスのインスタンス化 Random rnd = new Random(); shapeID = rnd.nextInt(5); rad = rnd.nextInt(180); size = 50; x = setx; y = sety; Alpha = 255 * 2 / 3; switch (shapeID) { case 0: // ☆の初期化 star_math(); break; case 1: // 四角形の初期化 rect_math(); break; case 2: // 三角形の初期化 triangle_math(); break; case 3: // 直線の初期化 point[0].x = 0; point[0].y = y - Math.tan(rad) * x; if (point[0].y < 0) { point[0].x = (-point[0].y) / Math.tan(rad); point[0].y = 0; } point[1].x = wid; point[1].y = Math.tan(rad) * wid + y - Math.tan(rad) * x; if (point[1].y > hei) { point[1].x = (hei - y + Math.tan(rad) * x) / Math.tan(rad); point[1].y = hei; } break; } } public boolean update() { if (flag == true) { size += 10; if (shapeID != 3) { rad += 6; if (rad > 360) { rad -= 360; } } Alpha -= 6; if (Alpha < 0) { flag = false; return false; } switch (shapeID) { case 0: star_math(); break; case 1: rect_math(); break; case 2: triangle_math(); break; } return true; } else { return false; } } private void star_math() { double trad; for (int i = 0; i < 10; i++) { trad = Math.toRadians(rad + i * 36); if (i % 2 == 0) { point[i].x = x + (size * 0.8) * Math.cos(trad); point[i].y = y + (size * 0.8) * Math.sin(trad); } if (i % 2 == 1) { point[i].x = x + (size * 0.8 * 0.4) * Math.cos(trad); point[i].y = y + (size * 0.8 * 0.4) * Math.sin(trad); } } } private void rect_math() { double trad; for (int i = 0; i < 4; i++) { trad = Math.toRadians(rad + i * 90); point[i].x = x + (size) * Math.cos(trad); point[i].y = y + (size) * Math.sin(trad); } } private void triangle_math() { double trad; for (int i = 0; i < 3; i++) { trad = Math.toRadians(rad + i * 120); point[i].x = x + (size * 2 ) * Math.cos(trad); point[i].y = y + (size * 2 ) * Math.sin(trad); } } public class point { double x; double y; } } あとはsurfaceviewを継承したクラスを作り package aokai.app.touchTest; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.MotionEvent; public class TouchView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private Canvas canvas; // スレッドクラス private Thread thread; private SurfaceHolder holder; public boolean tread_flag; public Shapes[] shapes; private int objectmax = 10; private int pointerCount; private Paint paint = new Paint(); public TouchView(Context context) { super(context); // setBackgroundColor(Color.WHITE); setFocusable(true); tread_flag = true; pointerCount = 0; paint.setAntiAlias(true); paint.setStrokeCap(Paint.Cap.ROUND); shapes = new Shapes[objectmax]; // サーフェイスホルダーの作成 holder = getHolder(); holder.addCallback(this); holder.setFixedSize(getWidth(), getHeight()); } public void run() { for (int i = 0; i < objectmax; i++) { shapes[i] = new Shapes(getWidth(), getHeight()); } while (tread_flag) { Draw(); } } public void Draw() { canvas = getHolder().lockCanvas(); // 背景の描画 canvas.drawColor(Color.BLACK); Path path = new Path(); for (int i = 0; i < objectmax; i++) { if (shapes[i].update()) { paint.setStyle(Paint.Style.FILL); paint.setColor(Color.argb(shapes[i].Alpha, 20, 255, 1)); switch (shapes[i].shapeID) { case 0: // ☆形の描写 paint.setStrokeWidth(20); paint.setStyle(Paint.Style.STROKE); path.moveTo((int) shapes[i].point[0].x, (int) shapes[i].point[0].y); // 始点 for (int j = 1; j < 10; j++) { path.lineTo((int) shapes[i].point[j].x, (int) shapes[i].point[j].y); } path.lineTo((int) shapes[i].point[0].x, (int) shapes[i].point[0].y); // 終点から始点へパ スをつなぐ canvas.drawPath(path, paint); break; case 1: // 四角形の描写 paint.setStrokeWidth(20); paint.setStyle(Paint.Style.STROKE); path.moveTo((int) shapes[i].point[0].x, (int) shapes[i].point[0].y); // 始点 path.lineTo((int) shapes[i].point[1].x, (int) shapes[i].point[1].y); path.lineTo((int) shapes[i].point[2].x, (int) shapes[i].point[2].y); path.lineTo((int) shapes[i].point[3].x, (int) shapes[i].point[3].y); path.lineTo((int) shapes[i].point[0].x, (int) shapes[i].point[0].y); // 終点から始点へパ スをつなぐ canvas.drawPath(path, paint); break; case 2: // 三角形の描写 paint.setStrokeWidth(20); paint.setStyle(Paint.Style.STROKE); path.moveTo((int) shapes[i].point[0].x, (int) shapes[i].point[0].y); // 始点 path.lineTo((int) shapes[i].point[1].x, (int) shapes[i].point[1].y); path.lineTo((int) shapes[i].point[2].x, (int) shapes[i].point[2].y); path.lineTo((int) shapes[i].point[0].x, (int) shapes[i].point[0].y); // 終点から始点へパ スをつなぐ canvas.drawPath(path, paint); break; case 3: // 直線の描写 paint.setStrokeWidth(15); canvas.drawLine((float) shapes[i].point[0].x, (float) shapes[i].point[0].y, (float) shapes[i].point[1].x, (float) shapes[i].point[1].y, paint); break; case 4: // 円の描写 paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(30); canvas.drawCircle(shapes[i].x, shapes[i].y, shapes[i].size, paint); break; } } } holder.unlockCanvasAndPost(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { for (int i = pointerCount; i < event.getPointerCount(); i++) { for (int j = 0; j < objectmax; j++) { if (shapes[j].flag == false) { shapes[j].set((int) event.getX(i), (int) event.getY(i)); break; } } } pointerCount = event.getPointerCount(); if (event.getAction() == MotionEvent.ACTION_UP) { pointerCount = 0; } return true; } public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { // TODO 自動生成されたメソッド・スタブ } public void surfaceCreated(SurfaceHolder holder) { thread = new Thread(this); thread.start(); } public void surfaceDestroyed(SurfaceHolder holder) { // TODO 自動生成されたメソッド・スタブ } } これでMainの package aokai.app.touchTest; import android.app.Activity; import android.os.Bundle; public class Main extends Activity { TouchView view; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); view = new TouchView(this); setContentView(view); } } これが実行結果です。 今回で説明しきれなかったので多分動くがアプリを終了したときエラると思います。
|