ゲームアプリ (Catch the Ball)

【Android Studio】ゲームアプリ開発入門 第4回「タイマーで画像を動かす」

Sara

Android Studio Dolphin | 2021.3.1 での動作確認を完了しました。

ここまでの記事はこちら

前回は、タップするたびに青いボックスが少しだけ上に動くようにしました。

今回はタイマーを使って

  • 画面をタップしている間は上に移動
  • 画面から指を離すと下に移動

のように、青いボックスを上下に動かしてみます。

動画

動画では Android Studio 3.1.4 を使用していますが、Android Studio Dolphin でも動作確認済みです。解説の画像は今後更新予定です。

【Android Studio】ゲームアプリ開発入門 #4 青いボックスを上下に動かす

YouTubeチャンネルはこちら

解説

1. タイマーでボックスを動かす

毎秒・毎分など、一定間隔で処理を実行するときはタイマーを使います。

MainActivity.java を開いて、4・5・18~28行目を追加します。

必要な import

必要な import は3つです。

import android.os.Handler;
import java.util.Timer;
import java.util.TimerTask;

タイマーについて

タイマーの処理は少し複雑にみえるかも知れませんが、分解して見てみましょう。
まずは timer.schedule(…) の部分です。

schedule(TimerTask task, long delay, long period)
TimerTask task実行する処理(タスク)
long delayタスクを実行するまでに待機する時間(ミリ秒で指定)
long periodタスクの実行間隔(ミリ秒で指定)

このゲームでは「待機時間は無しで、20ミリ秒毎にタスクを実行する」と設定しています。

Timer task

次に、実行するタスク TimerTask task の部分です。

新しい TimerTask を作成して、run() メソッドを用意します。run() メソッドの中には繰り返し実行する処理を書きます。

changePos メソッド

繰り返し実行する処理は changePos メソッドです。(この後書いていきます。)

changePos メソッドには

  • 画像の位置を変更
  • スコアラベルを更新

をするための処理を書きます。

20 ミリ秒毎に changePos() メソッドを呼び出して画面を更新していきますが、Android 開発では「メインスレッド外で UI を変更することはできない」という決まりがあります。

言い換えると「TimerTask からはスコアラベルを更新できない」ということです。

このとき使うのが Handler です。

Handler

handler.post で UI スレッドに Runnable を渡して UI を更新するという方法で、changePos() メソッドを実行します。

Handler を使わないとOnly the original thread that created a view hierarchy can touch its views. というエラーメッセージが表示されます。

このエラーが出た場合は、タイマー処理を見直してみてください。
*このエラーは API 26 以上の実機やエミュレータでは表示されませんが、API 25 以下の場合はアプリが強制終了となるのでご注意ください。

changePos() メソッドを用意

先ほど用意したタイマーで20ミリ秒毎に実行する changePos() メソッドを書きます。

5~7行目を追加します。* onTouchEvent 内の box.setY(boxY); は削除してください。

タッチイベントの処理を変更

現在は、画面をタップするごとに青いボックスが少しだけ上に移動するようになっています。

ここからは

  • 画面をタップしている間は上に
  • 画面に触れていない時は下に

青いボックスが動くようにします。

6行目を追加します。

changePos メソッドと onTouchEvent メソッドを以下のように書き換えます。

action_flg は「画面をタップしているか、タップしていないか」を判定するために使います。

onTouchEvent メソッドでは

  • 画面をタップしたら action_flg を true
  • 画面から指を離したら action_flg を false

にします。

ボックスの位置を変更する changePos メソッドでは

  • action_flg が true だったら boxY を上へ
  • action_flg が false だったら boxY を下へ

移動させています。

アプリを実行

ここでアプリを実行してみます。

青いボックスが消えてしまいましたね。

onCreate メソッドでタイマーをスタートしているので、画面が描画された時には青いボックスが下に落ち続けてしまっていることが原因です。画面をタップし続けるとまた上がってきます。

画面をタップしてからタイマーが起動するように修正していきましょう。

2. タイマー開始のタイミングを修正

ゲーム開始の判定も boolean 型の変数を使います。

2行目を追加します。

onTouchEvent メソッドを以下のように書き換えます。
* onCreate 内の startLabel.setVisibility(View.INVISIBLE); とタイマー処理は削除してください。

start_flg

  • false の場合はゲーム開始前
  • true の場合はゲームプレイ中

となります。

onTouchEvent メソッドでは、初めて画面がタップされた時に

  1. start_flg を true にする
  2. startLabel を消す
  3. タイマーを開始

    という処理を行います。

    ゲームが始まって start_flg が true になると、21~27行目の action_flg の判定処理が行われるようになります。

    startLabel の非表示について

    startLabel.setVisibility(View.INVISIBLE);
    startLabel.setVisibility(View.GONE);
    

    TextView や ImageView などの要素を非表示にするには setVisibility メソッドを使います。

    INVISIBLE は非表示にするだけ、GONE は完全に消すという違いがあります。

    どちらを使っても問題ありませんが、ゲームが始まったら startLabel は不要なので GONE で完全に消してしまいましょう。

    アプリを実行

    アプリを実行して、このように動いていれば成功です。

    今のままでは、青いボックスが画面の外に移動できてしまいます。

    次はここを修正していきましょう。

    3. 青いボックスが画面から出ないようにする

    ここで必要になるのが

      • frameLayout の高さ
      • 青いボックスの Y 座標
      • 青いボックスのサイズ

    の3つです。

    変数の用意

    5・6行目を追加します。

    7~11行目を追加します。 * onCreate 内の boxY = 500.0f; は削除してください。

    必要な import

    import android.widget.FrameLayout;

    boxSize

    青いボックスの画像は正方形なので box.getHeight() で高さだけ取得しています。

    なぜ onCreate でサイズを取得しないのか?

    「どうして FrameLayout のサイズを onCreate メソッドで取得しないのか?」と思われる方がいるかもしれません。

    理由は onCreate メソッド内ではビューの描画が完了していないからです。

    ビューの描画が完了していない状態で frameLayout の高さや画像サイズを取得しようとしても 0 が返ってきてしまいます。

    そのため、ビューの描画が確実に完了している onTouchEvent メソッド内でサイズを取得しました。

    box の Y 座標である boxY も同じ理由で onTouchEvent メソッド内で取得しています。

    changePos メソッド

    最後に changePos メソッドに 9・11行目を追加します。

    9行目
    if (boxY < 0) boxY = 0;

    boxY が 0 より小さくなった場合は、青いボックスが frame の外に出ている状態です。

    frame から出ないように boxY は 0 未満にならないようにします。

    11行目
    if (boxY > frameHeight – boxSize) boxY = frameHeight – boxSize;

    青いボックスが画面の一番下にある時、boxY は frame の高さからボックスの高さを引いた値になります。

    これ以上 boxY の値が大きくなると画面から出てしまうので、frameHeight – boxSize よりも大きくならないようにします。

    アプリを実行

    アプリを実行してください。

    青いボックスが画面から消えなければ成功です!

    上手く動かない場合は、一番下にここまでのコードを貼っているのでご確認ください。

    次に行うこと

    少しずつゲームらしくなってきましたね!次回はオレンジ・ピンク・ブラックボールを動かしていきましょう。

    ここまでのコード

    Q
    MainActivity.java
    Android Studio
    Android開発のための
    Java入門講座

    アプリを開発しながらJavaの基礎を学ぶことができる講座です。

    • Java の基礎を学んだけれど、次に何をすれば良いかわからない
    • とりあえずアプリを開発してみたい

    という方にオススメです。

    guest
    4 Comments
    古い順
    新しい順 人気順
    Inline Feedbacks
    View all comments
    take
    take
    2 years ago

    最近Androidでの開発にチャレンジをはじめて、こちらのサイトで勉強させていただいています

    1点疑問なのですが、boxYにはonCreateで値を入れてますが、
    onTouchEventで改めてboxY = box.getY()としているのはどのような理由なのでしょうか
    試しにコメントアウトしても同じような動きに見えたので質問です

    take
    take
    2 years ago
    Reply to  Sara

    Saraさん、丁寧な回答ありがとうございます!
    もう少し進めた上で質問するべきでした、、、

    ABOUT ME
    Sara
    Sara
    運営者
    書籍やオンライン講座でプログラミングを勉強してフリーランスのプログラマーになりました。
    このサイトでは「わかりやすく・シンプル」をモットーに、プログラミングの基礎からアプリ開発まで紹介します。
    独学でプログラミングを勉強をしている方、基礎は勉強したけれど次に何をすれば良いか分からない...という方のお役に立てるサイトを目指しています。
    4
    0
    この記事にコメントするx
    記事URLをコピーしました