2015/09/11

図形の描画

Androidで四角形を描画する場合には、drawRect(Rect rect , Paint paint)を使用するのですが、
Rectを設定・渡すのが面倒です。

そこで、drawRect(int x , int , y , int w , int h)のような関数を作成して、
比較的容易に四角形を書けるようにします。

public class drawObj
{
    private Paint           paint;

    public drawObj()
    {
        paint = new Paint();
    }

    public void drawRect(Canvas canvas , int x , int y , int w , int h , int col)
    {
        Rect rect = new Rect(x , y , x + w , y + h);
        if (canvas != null)
        {
            paint.setStyle(Paint.Style.STROKE);
            paint.setColor(col);
            canvas.drawRect(rect , paint);
        }
    }

}




最後の引数に色を指定できるようにすると、関数を呼び出すだけで色のついた四角形を表示することができます。

同じように、関数を用意して円を描くようにします。



    public void drawCircleFill(Canvas canvas , int x , int y , int w , int col)
    {
        if (canvas != null)
        {
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(col);
            canvas.drawCircle((float)x , (float)y , (float) w, paint);
        }
    }

今回のiReversiでは、デバッグの段階で配列を文字列で表示させて動作確認を行い、
四角形と円を書いて表示された状態での動作確認、石をイメージで作成してアニメーションさせる…
と言った手順を踏んでいました。

これらの関数は他でも使い回しが効きますので、このままプロジェクトに保存しておきます。
こうやって、段々とプロジェクトが肥大化していくわけです…


2015/09/07

Androidのsleep

ゲームのアプリを作る時には、アニメーションさせることが多々あります。
例えば、 / → | → \ → | → / を連続的に表示させると
棒が回転しているように見えます。
これが、アニメーションの基本です。

こんなことはご存知だと思いますが、ただ単にアニメーションさせると
早すぎて回転しているように見えず、縦棒(|)だけが見えるかもしれません。
縦棒が他のものに比べて表示が多いため、そのようにみえる可能性があります。

これを防ぐために、100ms(0.1秒)とか200ms(0.2秒)の間、処理を停止させると
綺麗に棒が回転しているようにみえるのですが、
処理を停止させるためにsleep命令を使用します。

ただし、AndroidのSleep命令では、処理停止中に割り込み処理が発生した場合に
sleep命令から例外が発生してしまうため、try{} によりsleepを囲む必要があります。

    try
    {
        Thread.sleep(100);
    }
    catch (Exception ex)
    {
    }

ただ、毎回毎回これを書くのは面倒ですから、関数化しておいたほうが楽になります

    public static void Sleep(int msec)
    {
        try
        {
            Thread.sleep(msec);
        }
        catch (Exception ex)
        {
        }
    }

こうしておけば、呼び出しも楽になります。
こういう小さいことでも関数化しておくと後で役に立つことがあります。
今回のiReversiでも、挟まれた石の反転を行う際など、様々な場面でSleep()として呼び出しています。

2015/08/28

やっと公開

次のゲームがやっと出来上がり、公開にこぎつけました。
今回は画面表示周りでちょっと苦労しました。

今回はリバーシを作ろうと思っており、コンピュータでしかできないことをやりたいなぁと思い、
盤面サイズを無限大(メモリの続く限り)にしようと…
本当に無限大だとゲームが終わりませんので、100個の石でという制限をつけました。

こうすると、今までのリバーシの定石が通用しなくて
CPU相手にレベル最強、CPU白手順(後攻)だと、未だに1勝もできていません。
全然勝てる気がしないのです。

話は変わって、今回は設定画面にシークバーを使用しました。
今まで使用したことがなかったので、どうするのか悩んでいましたが、
以下のようにしました。

ダイアログでは AlertDialog.Builderを使用しているので、xmlファイルを読み込むためには
一工夫必要になります。

(1) ダイアログ用xmlファイルの作成。ここにシークバーの設定も行う。
(2) ダイアログ用xmlファイルの読み込み
(3) ダイアログ表示時にシークバーへの設定を行う
 (1) xmlファイルの内容 (res/layout/setting_dialog.xml)
    <SeekBar
            android:id="@+id/seekbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10sp">
    </SeekBar>

(2) ダイアログ用xmlファイルの読み込み
        View view = getActivity().getLayoutInflater().inflate(R.layout.setting_dialog, null);
        builder.setView(view);

(3) シークバーへの設定
        SeekBar seekbar = (SeekBar)view.findViewById(R.id.seekbar);
        seekbar.setMax(GL.MAX_LEVEL);
        seekbar.setProgress(ProgressValue);

        seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                ProgressValue = progress;
            }

            public void onStartTrackingTouch(SeekBar seekBar) {
                // シークバー トラッキング開始
            }

            public void onStopTrackingTouch(SeekBar seekBar) {
                // シークバー トラッキング終了
                seekBar.setSecondaryProgress(seekBar.getProgress());
            }
        });

これで、 ProgressValueにシークバーの値(0~GL.MAX_LEVEL)が入るようになります。
開始が0で、これは変更ができません。

2015/08/21

開発再開中

人並みにお盆でも…
ということで、暫くの間、開発を停止していましたが、
涼しくなってきましたので、開発を再開いたしました。

それで、今度もパズルゲームです。
あまり難しくないものをということで、リバーシでも作ろうかと思っています。

巷にはありふれているゲームですので、どこで差別化を図るのかが問題になります。
さて、どうしたものでしょう…

今までと同じく、Androidアプリとなるわけですが、
作っていて気がついたことがありました。

そういえば、JavaにReallocないよなぁ。

reallocを実行するときと言うのは、領域をどんどん拡張していきたいときに使うのですが、
今回のリバーシで必要になりました。

なぜ、リバーシで領域を拡張していく必要があるのでしょうかねぇ?

それはさておき、なければ作るだけ。作るのは簡単です。
ついでに、元の領域から新しい領域へデータもコピーしちゃいましょう。

(1) 元の領域を渡す
(2) 新しい領域を作る
(3) 新しい領域へ元の領域のデータをコピーする
(4) 元の領域を解放する
(5) 新しい領域を返す

うん、簡単ですね。

    // 領域の拡張
    public static int[][] array_extend(int array[][])
    {
        int     new_array[][] = new int[array.length + 1][array[0].length + 1];

        // 領域の初期化
        for (int y = 0 ; y < new_array.length ; y++) {
            for (int x = 0 ; x < new_array[0].length ; x++) {
                array[y][x] = 0;
            }
        }

        // 領域を拡張してコピー
        for (int iy = 0; iy < array.length; iy++) {
            for (int ix = 0 ; ix < array[0].length ; ix++) {
                new_array[iy][ix] = array[iy][ix];
            }
        }

        // 古い領域の解放
        array = null;

        // 新しい領域を返す
        return new_array;
    }

int型の2次元配列を1ずつ拡張して返却します。

領域の初期化もしてあげると、後々に使い回しが効くと思われます。

とりあえず、今回はここまで。

2015/08/08

開発停止中

暑くて開発出来る状態じゃないんです。
考えることもできないぐらいの暑さで…

今日はいつもより暑さも和らいだので、夜になって少しだけ考えたりしましたが、
昼間ですと、暑くて暑くて…

北海道の住宅にはエアコンが無いのですよね。
例年だと扇風機があれば、ごまかしながらでも暮らせるのですが、
今年は全然通用しません。

次は何を作ろうか、大体決まりましたので、
近いうちに作成に入ると思います。

でも、お盆が近いのですよね。
お墓参りに行くことになるのかなぁ…

2015/07/25

HAXMはamdでは動きません

なんか体調がすぐれないというか、どうも書きかけて全て消去するという日が続いていました。

それだけで終わらしてしまっては仕方がありませんので、何を書こうかなと…

家には開発に使ったり、ベッドサイドに置いているノートPC
(これを書いているのも、ノートPCです)
他に、ゲームで使ったり、ビデオ編集に使用しているメインPC
(メーカ製ではありませんので、自作PCというやつですね)
CentOSを無理やり入れてサーバとして動かしているMacMiniがあります。

メインPCに開発環境を入れようとしたら、HAXMが動かなくて
「なんでかなぁ?」と思っていたら、CPUがamdでしたというオチだったりしました。
(HAXMはintelCPUでしか動きません)

自作PCをintelCPUで作りなおさないといけないのかなぁ
とか考えたりしています。

2015/07/13

プログラム開発の時間帯、環境

アプリの開発をするのにいい時間なんて、本当に人それぞれですね。
これを言っちゃうとそれが全てなんですが…

私の場合で書いてみます。

会社勤めをしていた時は、大体、1日2回ぐらい「調子がいい時」がありました。

出勤後1時間~2時間の間の1時間。
ご飯を食べ終わって、15時~16時くらいの1時間。


出勤した直後は地下鉄や電車の中で揺られ続け、駅から会社まで歩いて、それこそ「何もしたくない」状態でした。
1時間もあると落ち着いてきて、「さて、そろそろ真面目にやりますか」ってな感じでした。
午前中に会議をしなければいけない時には、本当は9:00~10:00くらいが良かったです。
会議で頭をつかうことなんて、ほとんどありませんでしたから。
10:00以降になると、「さっさと会議終わって、仕事やらせろよ」な感じです。
(もちろん、言葉にはしませんが)

午後はご飯を食べ終わって、13:00~14:00ぐらいまでは、もう眠たくて仕方がありません。
14:00~15:00くらいで眠気が覚めてきます。
覚めてくるだけで、まだ頭は働いていません。
15:00位からが本番です。
「ここらへんから仕事しないと、今日、帰れなくなるしなぁ…」とか考えている状態です。

どちらも、1時間ぐらいしか継続しません。
それ以降は惰性で仕事をしていますね。

残業時間中は、途中のインターセプトが入らなければ、1時間おきくらいに調子が良くなりました。
多分、余計な邪魔が入らないからなんでしょうね。

上の2回の調子がいい時にインターセプトが入ると、残業確定です。
残業したほうが、調子がいいのだから仕方がないです。

Androidアプリを開発しているときは、いつでも残業状態と一緒ですから、開発が面白いように進みます。
開発中は他のことで邪魔しない環境って大事なんですよね。

そういえば、以前にいた会社で、ヘッドホンで音楽を聞きながら開発をしていた時があり、その時も、面白いように開発が進みました。

開発中は邪魔されないというのが大切なのかもしれません。

乱数生成

乱数の生成については、Math.random()を使う方法と、Randomクラスを使う方法があります。

Drops、HexaDropsでは特に悩まずMath.random()を使用していますが、多分どちらでも問題はありません。

Math.random()を使用する場合には、これで返される値が 0~1までのdouble値が返されるということです。
0.002154とか0.9999とかですね。

使用したい値は整数でしたので、100倍して整数部分を使用しています。
また、得たい値は0~6までの値でしたので、上記の値の余りを取得します。
以下は乱数取得のサンプルです。

    public int rand(int num)
    {
        return ((int)(Math.random() * 100) % num);
    }

呼び出し側では rand(7)とすれば、0~6までのランダム値を取得できます。

2015/07/12

HexaDropsリリース

HexaDropsをリリースします。

繋がり判定は解説した内容を元に作成されています。

公開準備中ですので、公開されたらリンクも設定します。


(2015/07/12 追記)リンク追加しました

繋がり判定(2)

突然旅に出て、ようやく今日(もう日付が変わっているので昨日ですが…)帰ってきました。

どこまで書いたのかを覚えていなかったので、前々回の投稿を参照して…
つながり判定の部分でした。

再帰処理での判定方法ですが、そんなに難しくありません。
今現在の時点が呼び出し元と同じ色かを判定し、違う場合には処理(現在の呼び出し処理)を終了します。
すると、呼び出し元の処理へ戻るわけですね。

同じ色だった場合には、上下と斜めに隣接する部分を判定するように呼び出す感じです。

まぁ、こんな感じ…

 1:    private void check(int x , int y , int color)
 2:    {
 3:        if ((y >= 0) && (y < GAME_SCREEN_Y) && (x >= 0) && (x < GAME_SCREEN_X) &&
 4:            (screen_data[y][x] == color))
 5:        {
 6:            check(x , y + 1 , color);   // 下方向のチェック
 7:            check(x , y - 1 , color);   // 上方向のチェック
 8:
 9:            check(x + 1 , y , color);   // 右方向のチェック
10:            check(x - 1 , y , color);   // 左方向のチェック
11:            if (x % 2 == 1)
12:            {
13:                check(x - 1 , y + 1 , color);   // 左下のチェック
14:                check(x + 1 , y + 1 , color);   // 右下のチェック
15:            }
16:            else
17:            {
18:                check(x - 1 , y - 1 , color);   // 左上のチェック
19:                check(x + 1 , y - 1 , color);   // 右上のチェック
20:            }
21:        }
22:    }

前回の図と説明を参照してください。
(0,0)の場合にチェックする部分は、(0,1)→下方向、(1,0)→右方向です。

範囲のチェックは呼び出された側で行うようにしておくと、呼び出す側は座標を渡すだけで良くなります。
範囲チェックは3行目で行っています。
同じ色かの判定は4行目で行っており、異なる色の場合には再帰処理も終了しています。

下方向への呼び出しは6行目で実行されており、ここからまた1行目が呼び出されますが、1つ下の石に対しての判定となっています。
上方向への呼び出しは下方向へのチェックが終了後に呼び出されますが、この時は(0,-1)に対しての判定となりますが、呼び出された先の範囲チェックにより、すぐに呼び出し元へ帰ることになります。

11行目からの部分では、 (0,1)と(1,1)の横方向へのチェックを考えてみるとわかります。

(0,1)の場合にチェックするのは、 (-1,0)、(-1,1)、(1,0)、(1,1)が対象です。
これは、相対位置で考えると(-1,-1)、(-1,0)、(1,-1)、(1,0)となります。
(-1,0)、(1,0)の座標は9行目および10行目でチェックしていますので、(-1,-1)、(1,-1)のチェックを行うために、18行目および19行目を処理します。

今度は(1,1)ですね…(う~ん、長いなぁ)
(1,1)の場合にチェックするのは、 (0,1)、(0,2)、(2,1)、(2,2)が対象です。
これは、相対位置で考えると(-1,0)、(-1,1)、(1,0)、(1,1)となります。
(-1,0)、(1,0)の座標は9行目および10行目でチェックしていますので、(-1,1)、(1,1)のチェックを行うために、13行目および14行目を処理します。

11行目は、X座標の位置が偶数か奇数かを判定しています。
偶数の場合には18行目、19行目を処理し、奇数の場合には13行目、14行目を処理しています。

このようにして、繋がりの判定を行っています。

2015/06/27

旅に出ます

日本国内ですが、本当に旅に出ます。
1週間ぐらいですが、その間、更新が止まると思います。

ちょっとリフレッシュと言うことで…

2015/06/26

繋がり判定(1)

HexaDropsで繋がりを判定するには、隣接する配列が同じ値かを確認していきます。
言うだけなら簡単ですが、実際には手間がかかります。

配列は2次元配列で画面のサイズと同一ですが、見た目上は半分ずれている状態です。
見た目がずれているだけで、配列上はやはり隣接しています。
下のような感じです。
例えば、(X:0,Y:0)の部分は表示位置としては同じような感じとなりますが、
(X:1,Y:0)の部分は表示上は半分ずれた状態となります。

隣接判定としては、(0,0)と(1,0)は表示上も隣接していることになりますので、
これが同じかどうかで判断できます。
上下に対してはズレがありませんので、こちらはそのまま(0:1)が同じ色なのかで判定できます。

今度は(1,0)の場合で考えてみます。
上下はズレがないので問題ありません。
左右は…ズレていますので、隣接判定は (0,0)、(2,0)、(0,1)、(2,1)の4箇所となります。
(1,0)の部分で考えた場合には合計6箇所ですが、同じ色と判定された箇所があった場合には、
更にその部分から隣接判定を行う必要があります。
同じような隣接判定ですので、隣接判定から隣接判定を呼び出すと上手くいきそうです。
これは再帰呼び出しを行うとできそうだと判断できます。

続きは、次回に…





Drops → HexaDrops

Dropsは四角形キャラクタで、縦・横・斜めに3つ以上並ぶと消えるコラムス型パズルゲームでした。

このキャラクタを六角形にしたものがHexaDropsになります。

六角形にした場合、横に3つ以上並ぶと消えるという場合には見難く直感的に並んだというのがわかりません。
ですので、HexaDropsでは、辺で3つ以上繋がった場合には消えるというルールに変更しました。
こうすると、繋がった判定はDropsのままでは使用できません。

繋がる先としては、上下と斜め方向になります。
Dropsと比較した場合には横方向がなくなりますので、一見すると楽になるように感じるかもしれません。


(画面は開発中のもので、Dropsの背景を使用しているのでずれて表示されています)

上記の画面では、一番下段の青3つが繋がっているように判定されるべきでしょう。
この時、3つが繋がると消えるでは、簡単に3つ繋げることができるので、
繋がったら消えるときの数も調整する必要があります。

これが、ゲームの難易度に直結します。

また、ゲーム画面では半分ずれて表示されています。
しかし配列で0.5というのはありません。
配列の数を倍にすると整数で扱うこともできますが、半分の配列が未使用状態になります。

考え方が古いので、こんな無駄は勿体無くて許せません。

偶数と奇数で表示位置をずらせば無駄にはなりませんが、今度は繋がる判定が面倒になるでしょう。

どうやって判定するのかは、次回に…

キャラクタの表示

Dropsのキャラクタは四角ですので、全角文字の四角(■)で終わらせることもできなくはありませんが、
グラデーションを付けたりするには、絵を作って表示させる必要があります。

絵を作るにはWindowsのペイントでもいいのですが、きれいなグラデーションを付けるのは結構苦労します。

GIMPを使うと簡単にグラデーションを付けた四角を作れますので、
これで作ってみましょう。

GIMPを起動して 46x46 サイズの画像を作成し、全範囲を選択して、ブレンドによりグラデーションの四角を作成します。(作ったものは、選択範囲の「角を丸める」により丸めています)



この画像をエクスポートにより、PNGファイルで保存します。
保存場所は プロジェクトの中の、以下のフォルダにすると、後々作業が行いやすくなります。
   \app\src\main\res\drawable

プログラムからこの画像を呼び出すために、以下の様な関数を作成しておきます。


public Bitmap read(Context context , String name)
{
    int ID = context.getResources().getIdentifier(name , "drawable" , context.getPackageName());
    return BitmapFactory.decodeResource(context.getResources(), ID);
}
 
これで、MainActivityから

private Bitmap char_bmp;
char_bmp = read(this , 画像ファイル名);

char_bmpに画像ファイルの内容が読み込まれます。
画像ファイル名で指定している部分は、拡張子を指定しません。
(例:画像ファイルがfile.pngの場合には、 "file" とだけ指定します)

これで、キャラクタの読み込みは終わりましたので、
表示するときには drawBitmap により表示できることとなります。

2015/06/23

miwについて

ランキングサーバ作成で使用した miw というファイラーですが、
結構全角文字を使用している部分が多くて、途中であっさりと挫折してしまいました。
(ソースを見るのが久々で、ソースを追えなくなっているのも大きいですが…)

C言語って、あんなに面倒だったっけ?

ビット演算、シフト演算、三項式を使いまくる使いまくる。

でも、defineはCの方がいいなぁとか思ったり…
こんなのとか…
#define     StrCpy(__TO , __FROM)   \
            {\
                char *__CP1 , *__CP2;\
                __CP1 = (char *)__TO;\
                __CP2 = (char *)__FROM;\
                while ((*__CP1++ = *__CP2++) != '\0') ;\
            }
こんなのとか…
#ifdef Debug
#define DebugPrint fprintf
#else
#define DebugPrint
#endif


次回作

昨日から色々と考えています。

RPGなんか面白そうだけど、作るのは大変だよなぁとか、Dropsの六角形バージョンを作ろうかなぁとか…

RPGだと、ある程度はストーリも考えないといけないのに、そっちの方面の才能は無いしねぇ。

理論は知っている(by よつばと

ドラクエみたいなのを作って、ドラクエと同じように機能やパーティも増えていくっていうのも作っていくぶんには面白そうなんですけどねぇ。


2015/06/22

次なるアプリ

Dropsのことを色々書きながら、次は何をつくろうかと考えています。

頭のなかでは2つほど考えているのがあるのですが、
なかなかまとまりません。

フランチェスカを見ていたので、余計に気が散っちゃいました。

考えがまとまった方から作成することになるとは思うのですが、
いつになることやら…

onTouchEvent

onTouchEventの冒頭部分はこんな感じになっています。
@Override public boolean onTouchEvent(MotionEvent e)
{
    for (int cnt = 0 ; cnt <  e.getPointerCount() ; cnt++)
    {
        int x = (int) (e.getX(cnt) * gl.SCREEN_X / getWidth());
        int y = (int) (e.getY(cnt) * gl.SCREEN_Y / getHeight());

e.getPointerCount では、タッチされた件数を取得します。
2箇所タッチされたら2となります。

cntがイベントのインデックスとなっており、インデックスは0からです。
うまい具合に、forループの条件にタッチ件数とインデックスを使用できます。

e.getX、e.getYにより画面上でタッチされた位置を取得できますが、
ここでは実画面サイズでの位置が取得できます。
setFixedSize された位置ではないので、拡大・縮小分を計算する必要があります。
(最初はこれが解からず、正しく判定できないでいました)

画面の大きさ

端末の大きさはいろいろあります。

仮想端末として使用したNexus5と実機端末であるF-01Fは 1080×1920 ですが、
他のサイズの端末もいろいろあります。

それぞれごとに合わせることは不可能ですので setFixedSize により画面に対する表示サイズを固定します。

setFixedSize(400,800)とすれば、横400×縦800での表示となります。

端末の実サイズに関係なく400×800 で表示されます。

一応、setAntialias をしておくと、ジャギーが防止できるはずなので setAntialias も設定しておきます。
(アンチエイリアスが不要な場合には、setAntialias(false) でアンチエイリアスを解除します)

アンチエイリアスが不要な状態ってあまり思いつかないのですが…
方眼を画面上に表示する時なんかですかねぇ。


2015/06/21

ランキングサーバ

Androidのプログラムを作るのなら、通信もやりたいなぁということでDropsにインターネットランキング機能が付いています。

ネットに繋がっているサーバは既に家にあるので、それを流用することでサーバ部分は大幅に簡略化できました。
ただ、そのままはもちろん使用できないので、Dropsランキング用にプログラムを修正する必要があります。

サーバのプログラムは、直接サーバで修正したかったので、どうしたものかと思案していましたが…

昔作ったプログラムに miw というものがありました。
miw とはマルチウィンドウのファイラーで大本はSVR4で作られたものでした。
(SVR4なんて、知っている人がどれだけいることやら)

miwの実行画面

昔、X68000用に mint というファイラーがありました。
X68000でファイル操作をするなら、mint 以外にありえないと思うぐらいに、とても使いやすいファイラーでした。

仕事先ではSVR4を使っていて、ストレスが溜まりまくる状態に…
このままでは、とてもじゃないけどやっていられなくなり、
ファイル操作だけでももっと簡単に行えるように作成しました。

mint にはインタプリタ言語が搭載されており、それが使いやすくしている要因だったのですが、
とてもそこまで創りだす余裕はありません。

幸いにしてSVR4はUNIXベースです。シェルスクリプトは非常に強力です。
自分でインタプリタが作れないなら、シェルスクリプトで実行できるようにしてしまえばいいだろうと考えました。
お陰でかなり操作しやすくなっていたのですが、時代がWindowsへ移行するに従い、
仕事もWindows関連のものが多くなり、miw の存在も忘却の彼方へと…

現在のサーバはCentOSですので、簡単にmiwを動かすことができました。
が、文字コードが変わっています。
SVR4はEUCでしたが、LinuxはUTF-8です。
全角文字のハンドリングが異なるので、画面が崩れます。

こんな風に…

今後のためにも、半角文字だけを使用するように修正して、きちんと動作するようにしておく必要がありそうです。

※miw は今のところ公開する予定はありません。

Dropsについて

またまた、Dropsの話です。
(まぁ、まだDropsしか出来ていませんからねぇ)

Dropsでは、落下物が下に落ちた状態でも少しの間動かすことができるようになっています。
アーケードゲームのコラムスでも同じように、少しの間は動かすことができました。

この少しの間動かすことができるのがものすごい重要で、これが無いとゲーム性が崩壊してしまうのではないかと思います。
落ちた後に回転させて色を揃えることが、後々のレベルでは非常に重要になっています。
なので、これだけは必ずできるようにする必要がありました。

結構、苦労したんですけどね。

アーケード版といえば、魔法石というものがありました。
魔法石の下の色と同じ色のものが消える…

これは同じように作るのはすぐにできます。
魔法石の場合には別判定にするだけですので、そんなに難しくありません。
でも、作りませんでした。

個人的には魔法石の存在はゲーム性を損なう可能性が高いと思われたからです。

この取捨選択は正しかったのだと信じていたいものです。

Dropsについて(判定編)

Dropsは縦・横・斜めに3つ以上並んだ場合には同じ色が消えることになります。

3つ並んだのかを判定するには、色のデータを2次元配列上に保存しておきます。
あとは、配列に対して8方向に同じ色があるかをチェックします。

(1) 上方向にチェックして連結数を数える
(2) 下方向にチェックして連結数を数える
(3) 上下方向の連結数が3以上か確認する
(4) 左方向にチェックして連結数を数える
(5) 右方向にチェックして連結数を数える
(6) 左右方向の連結数が3以上か確認する
(7) 左上右下方向、左下右上方向に同じチェックを行う

落下物に対してチェックを行うと、3回のチェックで終了することができチェック処理が速くなります。
しかし、チェックの結果落ちてきたものがある場合には、落ちてきたものもチェック対象としなければいけません。
その時には3回のチェックでは終わりません。

今回は全画面で色データがあるものをすべて8方向にチェックを行っています。
それほど大きな配列ではないのであっという間に判定が終わっていますが、本当は高速化する必要があると思います。

高速化の方法としてもう一つ配列を持ち、落ちた部分を設定しておくと落ちた部分のみを判定させる事ができますが、これは速度の問題が発生した時でいいかなぁと思います。

Dropsについて(インターフェース編)

今回作成したゲーム(Drops)ですが、まんまコラムスです。

ゲーム自体は1990年にセガより発売されたものですので、もう25年前になります。
(月日が経つのは早いもので…)

このゲームを作るのは初めてではなく、3回目となります。

1回目は専門学校の卒業研究として。
2回目は会社で昼休みの暇つぶし用として。
3回目が今回での作成となります。


OSは1回目がPC-9801、2回目がOS/2、そして今回がAndroidとなります。

PC系ですとキーボードが必ずありますので、インターフェースに困ることはあまりありません。
しかし、今回はAndroidです。キーボードがあるものもありますが、基本的にキーボードはありません。
ゲーム画面にソフトウェアキーボードを出すわけには行きません。
タッチパネルで、どうやって入力を受け付けるかが問題となります。

フィールド上で現在の落下物に対して左がタッチされたら左へ、右がタッチされたら右へ移動させると
直感的で動かせるかもしれません。
さらにタッチされた部分まで移動させると更に直感的になるでしょう。
じゃあ回転はどうしようとなると、落下物がタッチされたら回転でいいと思います。

しかし、ここは敢えてゲームに対する制限として左右キーを押すこととしています。
本当は、左右キーを押したままにした時にはどんどん移動できるようにしたかったのですが、押しっぱなし状態をうまく取ることができませんでした。この部分は現在の課題ですね。

AdMobについて(2)

AdMobの続きです。

今回のアプリでは、SurfaceView を使用していて、タイトル画面とAdMobの重ね合わせが必要になります。

タイトル画面は画像(pngファイル)で作成されていて、ボタンなどもすべて画像で作成済みの状態です。
(イメージボタンを使えば良かったのかもしれませんが、何故かうまく行かなかったのでOnTouchイベントで処理しています)

タイトル画面との重ね合わせはタイトル画面の view と AdMob の view を FrameLayout で実現できます。

※下のソースコードは、実際のソースコードから抜粋しています。

private FrameLayout flayout;

flayout = new FrameLayout(this); ← FlameLayoutの作成
gm = new GameMain(this , hd);    ← ゲーム本体のビュー作成
flayout.addView(gm, new FrameLayout.LayoutParams(                ← レイアウトへ追加
                    FrameLayout.LayoutParams.MATCH_PARENT,
                    FrameLayout.LayoutParams.MATCH_PARENT));
adview = new AdView(this);       ← AdMobのビュー作成
flayout.addView(adview, params); ← レイアウトへ追加
setContentView(flayout);         ← 表示の設定


表示するときには、flayoutに対してaddViewを行い、非表示にするときにはflayoutに対してremoveViewを行っています。

冷静に考えればそんなに難しくもないとは思うのですが、なにせ初めてのアプリ作成ということで、
ビューの使い方自体がはっきりわかっていなかったため苦労しました。

重ね合わせで行えば、他のゲームを作るときにも使い回すことができると思うので、
これはこれで良かったのかなぁと思います。

AdMobについて(1)

アプリの下とか上とかに表示されたりするバナー広告。
Android(というかGoogle)のサービスでAdMobを使うと簡単に表示できて、うまくいけばお金にもなるらしいのですが


どこが簡単になんだよぉ

結構苦労させられたぞ!

まぁ、表示はすぐに出来た。

表示させるだけなら、他のサイトに書いてある内容で表示できるんだろうけど、
ゲームの場合には、タイトル画面などゲームに直接関係ない画面では表示したい。けれどゲーム中に表示されたら邪魔で仕方がない。

メニュー画面(バナー表示されて良い)


ゲーム画面(バナー表示なんていらない)


表示したり消したりさせることがこんなに難しいとは思ってもいなかった。

普通に表示させる方法はこちらを参照すれば作成できるのですが…

どうやったのかは、次回。



開発 ことはじめ

Android用アプリケーションの開発方法などの覚書として書いていこうかなと思いましたが、
AndroidStudioのセットアップ方法なんかはキレイさっぱり忘れちゃった。
まぁ、他のサイトなどにもセットアップ方法は書いてあるだろうからいいかなぁ。

今のSDKのインストール状況はこんな感じ。






気がついた時にアップデートしているから、ほぼ最新版を使用している感じ。
Google USB DriverとIntel x86 Emulator Accelerator(HAXM install) はインストールしておかないと
実機でのデバッグができないとのこと。

SDKでインストールしただけではHAXMは使用できないらしく、コマンドを実行する必要があるらしく
SDK Path(Android SDK Managerの上の方に表示されています)の中のファイルを実行します。
    ディレクトリ:\extras\intel\Hardware_Accelerated_Execution_Manager
    実行ファイル:intelhaxm-android.exe

実機でデバッグするときには、PCの方に実機のUSBドライバもインストールする必要があります。
(私は Arrows NX F-01Fを所有しており、富士通のサイトから該当のドライバをインストールしました)

この環境を作るだけでも2日位かかったように記憶しています。
(特に実機デバッグの時にUSBドライバが必要というのがわからなくて悩んでいました)

とりあえず、第一弾はここまで。