[PR] この広告は3ヶ月以上更新がないため表示されています。
ホームページを更新後24時間以内に表示されなくなります。

Androidプログラミング日記 (仮)  

15パズル

あなたは

人目のプログラマーだよ。

Androidプログラミング日記 (仮).

 

share
 

 

 

15パズル

画像などを4×4などに区切り1マスだけ空欄にし、バラバラになった画像を空欄を使ってもとの画像に戻すゲームです。

最初に紹介ずるゲームにこれを選んだ理由は、書籍の最初がこれだったかr・・・えー今までやってきた基本のコードの組み合わせでできるからです。

ゲーム自体は、画像さえ変えたり、分割数を変えたりすれば無限に種類はできますので、お手軽でいいですね。

アプリコード

今回からは、コードを書いてからどういうことをやっているのか順番に簡単な説明をいれていきます。

プロジェクト名「Puzzle」

使用画像
   
 
 
背景画像
パズル画像
 
 
スタートボタン(通常)
スタートボタン(押下)

 

 

ファイル名「main.xml」

01<?xml version="1.0" encoding="utf-8"?>
02<FrameLayout
05android:layout_width="fill_parent"
06android:layout_height="fill_parent"
07android:orientation="vertical" >
08 
09<and.roid.PView
10android:id="@+id/View01"
11android:layout_width="fill_parent"
12android:layout_height="fill_parent" />
13 
14</FrameLayout>
 
     
 

レイアウトするオブジェクトの修正ですね。<FrameLayout></FrameLayout>に変更し、xmlns:app="http://schemas.android.com/apk/res/and,roid"のようにパッケージパスを追加。

android:id="@+id/View01"でオブジェクトidを設定しました。

 

 

 

ファイル名「PuzzleManifest.xml」

01<?xml version="1.0" encoding="utf-8"?>
02<manifest xmlns:android="http://schemas.android.com/apk/res/android"
03package="and.roid"
04android:versionCode="1"
05android:versionName="1.0" >
06 
07<uses-sdk android:minSdkVersion="4" />
08 
09<application
10android:icon="@drawable/ic_launcher"
11android:label="@string/app_name" >
12<activity
13android:screenOrientation="portrait"
14android:name=".PuzzleActivity"
15android:label="@string/app_name" >
16<intent-filter>
17<action android:name="android.intent.action.MAIN" />
18 
19<category android:name="android.intent.category.LAUNCHER" />
20</intent-filter>
21</activity>
22</application>
23 
24</manifest>
 
     
  ここでは早速おぼえたての、回転機能を縦で固定にしています。  

 

 

ファイル名「PuzzleActivity.java」

01package and.roid;
02 
03import android.app.Activity;
04import android.os.Bundle;
05import android.view.*;
06 
07public class PuzzleActivity extends Activity {
08/** Called when the activity is first created. */
09@Override
10public void onCreate(Bundle savedInstanceState) {
11super.onCreate(savedInstanceState);
12requestWindowFeature(Window.FEATURE_NO_TITLE);
13getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
14 
15setContentView(R.layout.main);
16}
17}
 
     
  このファイルはプロジェクト作成時にすでにあるファイルです。赤字の所でフルスクリーンにするようにしています。このコードはそのままおぼえましょう。  

 

 

ファイル名「PView.ja」このファイルはView クラスをスーパークラスに持たせて作成します。

001package and.roid;
002 
003import android.content.Context;
004import android.content.res.Resources;
005import android.graphics.*;
006import android.graphics.drawable.Drawable;
007import android.util.AttributeSet;
008import android.view.*;
009import android.widget.Toast;
010 
011public class PView extends View {
012private static final int btn_x = 50; //ボタン横位置(1)
013private static final int btn_y = 770; //ボタン縦位置
014private static final int btn_w = 390; //ボタン横幅
015private static final int btn_h = 50; //ボタン縦幅
016private static final int board_x = 40; //ゲーム版の横位置
017private static final int board_y = 126; //ゲーム版の縦位置
018private static final int score_x = 60; //テキストの表示横位置
019private static final int score_y = 73; //テキストの表示縦位置
020 
021private PuzzleActivity puzzle; //PuzzleActivityクラス(ファイル)
022private PBoard board; //PBoardクラス(ファイル)
023private Drawable back,btn1,btn2; //使用するイメージ
024private boolean btn_down,isPlaying; //ボタンの状態、プレイ中の状態
025private int pressX,pressY,upX,upY; //ボタンを押した時、離した時の位置
026 
027public PView(Context context) {
028super(context);
029init(context);
030}
031 
032public PView(Context context, AttributeSet attrs){
033super(context,attrs);
034init(context);
035}
036 
037private void init(Context context){    (2)
038puzzle =(PuzzleActivity)context;
039Resources resources = context.getResources();
040back = resources.getDrawable(R.drawable.back1);
041btn1 = resources.getDrawable(R.drawable.start1);
042btn1.setBounds(btn_x,btn_y,btn_x+btn_w,btn_y+btn_h);
043btn2 = resources.getDrawable(R.drawable.start2);
044btn2.setBounds(btn_x,btn_y,btn_x+btn_w,btn_y+btn_h);
045 
046Bitmap img = BitmapFactory.decodeResource(resources, R.drawable.image1);
047board = new PBoard(board_x,board_y,img);
048btn_down = false;
049isPlaying = false;
050}
051 
052protected void onDraw(Canvas c){  (3)
053c.drawColor(Color.BLACK);
054int w = this.getWidth();
055int h = this.getHeight();
056back.setBounds(0, 0, w, h);
057back.draw(c);
058board.draw(c);
059if(btn_down){
060btn2.draw(c);
061}else{
062btn1.draw(c);
063}
064Paint p = new Paint();
065p.setTextSize(30f);
066p.setColor(Color.WHITE);
067c.drawText("count:"+board.count, score_x, score_y, p);
068}
069 
070public boolean onTouchEvent(MotionEvent event){ (4)
071int action = event.getAction();
072int x = (int)event.getX();
073int y = (int)event.getY();
074switch(action){
075case MotionEvent.ACTION_DOWN:
076pressX = x;
077pressY = y;
078if (isIn(x,y,btn1.getBounds())){
079btn_down = true;
080isPlaying = true;
081board.init();
082Toast toast = Toast.makeText(puzzle, "スタート!",Toast.LENGTH_LONG);
083toast.show();
084}
085break;
086case MotionEvent.ACTION_UP:
087btn_down = false;
088upX = x;
089upY = y;
090if(isPlaying) checkMove();
091break;
092}
093invalidate();
094return true;
095}
096 
097public boolean isIn(int x, int y, Rect rect){ (5)
098return x>rect.left && x<rect.right && y>rect.top && y<rect.bottom;
099}
100 
101public void checkMove(){ (6)
102int dx = upX - pressX;
103int dy = upY - pressY;
104if(dx < -100) board.move(PBoard.WEST);
105if(dx > 100) board.move(PBoard.EAST);
106if(dy < -100) board.move(PBoard.NORTH);
107if(dy > 100) board.move(PBoard.SOUTH);
108if(board.checkFinish()){
109isPlaying = false;
110Toast toast = Toast.makeText(puzzle, "完成!", Toast.LENGTH_LONG);
111toast.show();
112}
113}
114 
115}
 
     
 

このファイルクラスでは主に全体の画面や初期設定など、アプリの心臓部となるクラスです。

(1) private static final int で 設定した変数に数値を代入すると、その数値の変更ができなくなり、固定値のようになります。

(2) private void init(Context context) の「init」は初期化を意味します。初期化するクラスをここで作り、画像の取り込みや初期位置情報などを設定しています。

(3) protected void onDraw(Canvas c)はいろいろ表示するクラスです。画面全体を黒で塗りつぶし、画面の横幅縦幅を知り、その座標を使い背景画像をセット。のちに作る別ファイルクラスにある表示クラスを呼び出し、ボタン画像を表示。ここでボタンが押されているかいないかで、ボタンの画像を変えて表示。ここからはテキスト表示のためにPaintオブジェクトを生成し、テキストサイズを設定。色を設定して変数countを設定座標に表示しています。

(4) ここはタッチパネルのイベント処理です。MotionEvent eventでタッチした時にアクションイベント値が自動的にeventにはいります。それをint action = event.getAction();で受け取り、次で座標も受け取ります。switchでさきほど受け取ったタッチイベントによって処理を変えます。

(5) public boolean isIn(int x, int y, Rect rectの説明を先に。座標と短形情報をうけとり、渡された座標(今回はタッチ座標)と短形座標(今回はボタン座標)をくらべて、短形エリアでタッチされたかいないか(boolean)をtrue、falseで返します。

MotionEvent.ACTION_DOWN:はタッチした時です。まずタッチした時の座標をpressX、Yに入れます。isInに値を渡し(上記(5)参照)それが真なら(短形エリアでタッチされたら)btn_down = true(押された)、isPlaying = true(スタート開始ok)にし、のちに設定するちがうファイルのボード(動かす写真)クラスの初期化を飛び出し、最後に下部にテキストトーストを表示します。

MotionEvent.ACTION_UP:はタッチしたあとにタッチが離れた時の処理です。ボタン上であればタッチは離れるわけですから元にもどします(btn_down = false)。つまり押した時だけボタン画像を変更させて、あとはfalseの状態にしておくということです。そのご、タッチを離した時の座標をupX、Yに入れ、if(isPlaying) checkMove();でゲーム中なら checkMove()クラスを呼び出しゲーム中でないならそのまま下に移動。

最後に画面を更新(invalidate();)してreturn trueでこのクラスを出ます。

(6) public void checkMove()はタッチを話した時に呼び出されるクラスです。ここではパネルを移動させるための条件が整っていれば、違うファイルのパネルを動かすクラスを呼び出す処理を最初にしています。タッチon、off時にそれぞれ座標を格納しているので、タッチした時の座標からタッチが離れた時の座標を差し引く事で、どれだけタッチしたまま移動して離れたかを出します。その差が100ドット以上の差があり、xならプラス100かマイナス100か、yも同じ感じで調べてそれぞれ違う数値を渡して呼び出しています。。

最後にこの処理が終わった瞬間に、ゲーム終了かどうかのチェックをして、終了ならテキストトーストを表示しています。

 

 

 

ファイル名「PBoard.java」

01package and.roid;
02 
03import java.util.*;
04 
05import android.graphics.*;
06 
07public class PBoard {
08public static final int CENTER = 0; //移動方向を示す値
09public static final int NORTH = 1; //移動方向を示す値
10public static final int SOUTH = 2; //移動方向を示す値
11public static final int EAST = 3; //移動方向を示す値
12public static final int WEST = 4; //移動方向を示す値
13private Bitmap image; //表示イメージ
14private int x,y; //ボードの表示位置
15private int[] data; //表示位置データを管理する配列
16public int place; //現在の空きスペース位置
17public static final int row = 6; //ピースの縦の個数
18public static final int col = 4; //ピースの横の個数
19public static final int pW = 100; //ピースの横幅
20public static final int pH = 100; //ピースの縦幅
21public int count = 0; //動かした回数
22 
23public PBoard(int x, int y, Bitmap image){ (1)
24super();
25this.x = x;
26this.y = y;
27this.image = image;
28data = new int[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,-1};
29place = 23;
30}
31 
32public void init(){ (2)
33for(int i=0;ii++){
34Random r = new Random(new Date().getTime());
35int a = r.nextInt(data.length);
36int b = r.nextInt(data.length);
37int val = data[a];
38data[a] = data[b];
39data[b] = val;
40}
41for(int i=0;i<data.length;i++)
42if(data[i] == -1) place = i;
43count = 0;
44}
45 
46public void draw(Canvas canvas){ (3)
47int n = 0;
48for(int i=0;i<row;i++){
49for(int j=0;j<col;j++){
50int c = data[n] % col;
51int r = (int)(data[n]/col);
52if(data[n] != -1){
53canvas.drawBitmap(image, new Rect(x+c*pW, y+r*pH, x+c*pW+pW, y+r*pH+pH),
54new Rect(x+j*pW, y+i*pH, x+j*pW+pW, y+i*pH+pH), new Paint());
55}
56n++;
57}
58}
59}
60 
61public void move(int move){ (4)
62int c = place % col;
63int r = (int)(place / col);
64int c2 = c;
65int r2 = r;
66switch(move){
67case NORTH:
68if(r2 < row-1) r2++;
69break;
70case SOUTH:
71if(r2 > 0) r2--;
72break;
73case WEST:
74if(c2 < col-1) c2++;
75break;
76case EAST:
77if(c2 > 0) c2--;
78break;
79}
80int n = data[r * col + c];
81data[r * col + c] = data[r2 * col + c2];
82data[r2 * col + c2] = n;
83for(int i=0;i<data.length;i++)
84if(data[i] == -1) place = i;
85count++;
86}
87 
88public boolean checkFinish(){ (5)
89boolean flg = true;
90for(int i=0;i<data.length-1;i++)
91if(data[i] != i) flg = false;
92return flg;
93}
94}
 
     
 

このクラスは表示するパネルに関する処理が書かれています。

(1) PBoard(int x, int y, Bitmap image)はこのファイルの主クラスとなります。このクラスを使用する時には、座標とイメージをこのクラスに渡すようにしてます。このクラスの座標メンバxyに渡された座標を入れ、パネルを分割するための準備としてdataの配列を作成し、中にに24個の数値を襦袢に1から入れ、最後24個目を-1(空欄)と設定します。空欄パネル数の場所をplaceに入れます。

(2) public void init() でパネルの初期化処理をしています。簡単にいいますと、ランダムな数値0から24を2つ作り、3つの変数を使い(例えばA=B B=C C=A)としながら入れ替える処理を200回繰り返します。

Random r = new Random(new Date().getTime());はランダム数値を使うための準備です。

int a = r.nextInt(data.length);は下のコードbと同じで、ランダム値をdata.lengthのdataの大きさ、ここでは24で作成しています。そして3つの変数を使用して数値を入れ替えています。

最後にfor文でdataの中にある配列数分だけ中の数値をチェックし、その数値が-1であれば空欄を表すplaceに-1を入れます。そしてcountを0に設定。

(3) public void draw(Canvas canvas) ではパネル表示を画面に表示させる処理をしているクラスです。ここはほとんどが数学の応用を使用してますね。

(4) public void move(int move) ではパネルの移動処理をしています。渡された値によって処理を変えます。最後に空欄を探してplaceに-1を入れます。

(5) public boolean checkFinish() はパネルの絵が元の絵に戻ったかどうかの判定処理です。最初の設定で元の画像が0から22まで順番に並んでいます。for分を成分iも順番に0から増えていく処理をして、元絵の数値と比べておなじゅ数値に並んでいれば完成というわけです。最初のboolean flg = true;は完成したかどうかのフラグで、完成と一度設定しています。そのわけは逆の処理をするよりこちらのほうが簡単な処理ができるからです。(たぶん)

 
     
 

これで完成ですが、エミュレータを色々試したらわかると思いますがXperiaでしか表示がしっかりしません。

     

Xperiaでしかちゃんと表示されないのは問題です。ちょっと改造します。

 

ファイル名「PuzzleActivity.java」

01package and.roid;
02 
03import android.app.Activity;
04import android.os.Bundle;
05import android.view.*;
06 
07public class PuzzleActivity extends Activity {
08public float disp_w,disp_h;
09/** Called when the activity is first created. */
10@Override
11public void onCreate(Bundle savedInstanceState) {
12super.onCreate(savedInstanceState);
13requestWindowFeature(Window.FEATURE_NO_TITLE);
14Window window = getWindow();
15window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
16WindowManager manager = window.getWindowManager();
17Display disp = manager.getDefaultDisplay();
18disp_w = disp.getWidth();
19disp_h = disp.getHeight();
20 
21setContentView(R.layout.main);
22}
23}
 
     
     

 

 

ファイル名「PView.java」

001package and.roid;
002 
003import android.content.Context;
004import android.content.res.Resources;
005import android.graphics.*;
006import android.graphics.drawable.Drawable;
007import android.util.AttributeSet;
008import android.view.*;
009import android.widget.Toast;
010 
011public class PView extends View {
012private static final float XPERIA_W = 480;
013private static final float XPERIA_H = 854;
014private float btn_x = 50; //ボタン横位置
015private float btn_y = 770; //ボタン縦位置
016private float btn_w = 390; //ボタン横幅
017private float btn_h = 50; //ボタン縦幅
018private float board_x = 40; //ゲーム版の横位置
019private float board_y = 126; //ゲーム版の縦位置
020private float score_x = 60; //テキストの表示横位置
021private float score_y = 73; //テキストの表示縦位置
022 
023private PuzzleActivity puzzle; //PuzzleActivityクラス(ファイル)
024private PBoard board; //PBoardクラス(ファイル)
025private Drawable back,btn1,btn2; //使用するイメージ
026private boolean btn_down,isPlaying; //ボタンの状態、プレイ中の状態
027private int pressX,pressY,upX,upY; //ボタンを押した時、離した時の位置
028 
029public PView(Context context) {
030super(context);
031init(context);
032}
033 
034public PView(Context context, AttributeSet attrs){
035super(context,attrs);
036init(context);
037setPuzzleSize();
038}
039 
040private void init(Context context){
041puzzle =(PuzzleActivity)context;
042btn_down = false;
043isPlaying = false;
044}
045private void setPuzzleSize(){
046float w = puzzle.disp_w;
047float h = puzzle.disp_h;
048float dw = w / XPERIA_W;
049float dh = h / XPERIA_H;
050 
051btn_x = btn_x * dw;
052btn_y = btn_y * dh;
053btn_w = btn_w * dw;
054btn_h = btn_h * dh;
055board_x = board_x * dw;
056board_y = board_y * dh;
057score_x = score_x * dw;
058score_y = score_y * dh;
059 
060Resources resources = puzzle.getResources();
061back = resources.getDrawable(R.drawable.back1);
062back.setBounds(0, 0, (int)w, (int)h);
063btn1 = resources.getDrawable(R.drawable.start1);
064btn1.setBounds((int)btn_x,(int)btn_y,(int)(btn_x+btn_w),(int)(btn_y+btn_h));
065btn2 = resources.getDrawable(R.drawable.start2);
066btn2.setBounds((int)btn_x,(int)btn_y,(int)(btn_x+btn_w),(int)(btn_y+btn_h));
067 
068Bitmap img = BitmapFactory.decodeResource(resources, R.drawable.image1);
069board = new PBoard(board_x,board_y,dw,dh,img);
070}
071 
072protected void onDraw(Canvas c){
073c.drawColor(Color.BLACK);
074back.draw(c);
075board.draw(c);
076if(btn_down){
077btn2.draw(c);
078}else{
079btn1.draw(c);
080}
081Paint p = new Paint();
082p.setTextSize(30f);
083p.setColor(Color.WHITE);
084c.drawText("count:"+board.count, score_x, score_y, p);
085}
086 
087public boolean onTouchEvent(MotionEvent event){
088int action = event.getAction();
089int x = (int)event.getX();
090int y = (int)event.getY();
091switch(action){
092case MotionEvent.ACTION_DOWN:
093pressX = (int)event.getX();
094pressY = (int)event.getY();
095if (isIn(x,y,btn1.getBounds())){
096btn_down = true;
097isPlaying = true;
098board.init();
099Toast toast = Toast.makeText(puzzle, "スタート!",Toast.LENGTH_LONG);
100toast.show();
101}
102break;
103case MotionEvent.ACTION_UP:
104btn_down = false;
105upX = (int)event.getX();
106upY = (int)event.getY();
107if(isPlaying) checkMove();
108break;
109case MotionEvent.ACTION_MOVE:
110break;
111}
112invalidate();
113return true;
114}
115 
116public boolean isIn(int x, int y, Rect rect){
117return x>rect.left && x<rect.right && y>rect.top && y<rect.bottom;
118}
119 
120public void checkMove(){
121int dx = upX - pressX;
122int dy = upY - pressY;
123if(dx < -100) board.move(PBoard.WEST);
124if(dx > 100) board.move(PBoard.EAST);
125if(dy < -100) board.move(PBoard.NORTH);
126if(dy > 100) board.move(PBoard.SOUTH);
127if(board.checkFinish()){
128isPlaying = false;
129Toast toast = Toast.makeText(puzzle, "完成!", Toast.LENGTH_LONG);
130toast.show();
131}
132}
133 
134}

 

 
     
     

 

 

ファイル名「PBoard.java」

01package and.roid;
02 
03import java.util.*;
04 
05import android.graphics.*;
06 
07public class PBoard {
08public static final int CENTER = 0; //移動方向を示す値
09public static final int NORTH = 1; //移動方向を示す値
10public static final int SOUTH = 2; //移動方向を示す値
11public static final int EAST = 3; //移動方向を示す値
12public static final int WEST = 4; //移動方向を示す値
13private Bitmap image; //表示イメージ
14private float x,y; //ボードの表示位置
15private int[] data; //表示位置データを管理する配列
16public int place; //現在の空きスペース位置
17public static final int row = 6; //ピースの縦の個数
18public static final int col = 4; //ピースの横の個数
19private float pW = 100f; //ピースの横幅
20private float pH = 100f; //ピースの縦幅
21public int count = 0; //動かした回数
22 
23public PBoard(float x, float y, float dw, float dh, Bitmap image){
24super();
25this.x = x;
26this.y = y;
27pW *= dw;
28pH *= dh;
29this.image = image;
30data = new int[]{0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,-1};
31place = 23;
32}
33 
34public void init(){
35for(int i=0;ii++){
36Random r = new Random(new Date().getTime());
37int a = r.nextInt(data.length);
38int b = r.nextInt(data.length);
39int val = data[a];
40data[a] = data[b];
41data[b] = val;
42}
43for(int i=0;i<data.length;i++){
44if(data[i] == -1) place = i;
45count = 0;
46}
47}
48 
49public void draw(Canvas canvas){
50int n = 0;
51for(int i=0;i<row;i++){
52for(int j=0;j<col;j++){
53int c = data[n] % col;
54int r = (int)(data[n]/col);
55if(data[n] != -1){
56canvas.drawBitmap(image,
57new Rect((int)(x+c*pW), (int)(y+r*pH), (int)(x+c*pW+pW), (int)(y+r*pH+pH)),
58new Rect((int)(x+j*pW), (int)(y+i*pH), (int)(x+j*pW+pW), (int)(y+i*pH+pH)),
59new Paint());
60}
61n++;
62}
63}
64}
65 
66public void move(int move){
67int c = place % col;
68int r = (int)(place / col);
69int c2 = c;
70int r2 = r;
71switch(move){
72case NORTH:
73if(r2 < row-1) r2++;
74break;
75case SOUTH:
76if(r2 > 0) r2--;
77break;
78case WEST:
79if(c2 < col-1) c2++;
80break;
81case EAST:
82if(c2 > 0) c2--;
83break;
84}
85int n = data[r * col + c];
86data[r * col + c] = data[r2 * col + c2];
87data[r2 * col + c2] = n;
88for(int i=0;i<data.length;i++)
89if(data[i] == -1) place = i;
90count++;
91}
92 
93public boolean checkFinish(){
94boolean flg = true;
95for(int i=0;i<data.length-1;i++)
96if(data[i] != i) flg = false;
97return flg;
98}
99}

 

 

 
     
     
   
     
     

 

変更場所は赤字にしました。

Xperiaの画面に合わせているので、実際の画面との縮尺を計算するための変更です。

初めて使うメソッドなど出てきても、日本語でゆっくり考えてみると理解しやすいとおもいます。

javaはオブジェクト思考プログラミングということで、処理をクラスごとに細かく分けてパーツを組み合わせる感じです。

最初に作成されるファイルはそれがないと動かないファイルという感じで覚えて、携帯端末機自体のオブジェクトに、ゲームの背景やボタン、テキストを設定、パネルの処理だけ別のオブジェクトとしてクラス作成をする。という感じでしょうか。

オブジェクト思考はパーツごとに細かく分かれていて、ゆっくり調べることができ、見やすいのがいいですよね。

 

 

Androidプログラミング日記 (仮) | サイトマップ | 個人情報保護方針 | 応援メールテヘペロ | ©2012 Japan  相互リンク大募集中です