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

開発停止中

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

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

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

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

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