ゲームアプリ (Catch the Ball)

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

Sara

2023年11月 Android Studio Giraffe | 2022.3.1 で動作確認済み。

ここまでの記事はこちら

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

今回はタイマーを使って

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

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

解説

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
Subscribe
Notify of
8 Comments
古い順
新しい順 人気順
Inline Feedbacks
View all comments
take
3 years ago

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

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

take
3 years ago
Reply to  Sara

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

taro
11 months ago

こんにちは、
この記事で紹介されているTimerTaskとクイズアプリを組み合わせて、クイズ画面に制限時間を表示する機能(90から毎秒1ずつ減らしていく処理)を実装しようとしているのですが、クイズ画面に遷移すると、画面が一瞬描画された後にホーム画面に戻されます。一応デスクトップPCでも試して同様のことが起きたので、スペック不足ではないと思います。
調べてみても原因が分からないです…アドバイスいただけると嬉しいです。
以下、ソースコード(一部省略)です。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private int timercount= 90; //制限時間

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

 showNextQuiz();

    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    changeTime();
                }
            });
        }
      }, 0, 1000); //1000ミリ秒ごとに処理を実行
   }
  
  public void changeTime() {
      timerLabel.setText(timercount);
      timercount -= 1;
  }

}
taro
11 months ago
Reply to  Sara

実装できました!
この部分で長時間苦戦していたので本当に助かりました。
丁寧に教えていただきありがとうございます。

ABOUT ME
Sara
Sara
運営者
書籍やオンライン講座でプログラミングを勉強してフリーランスのプログラマーになりました。
このサイトでは「わかりやすく・シンプル」をモットーに、プログラミングの基礎からアプリ開発まで紹介します。
独学でプログラミングを勉強をしている方、基礎は勉強したけれど次に何をすれば良いか分からない...という方のお役に立てるサイトを目指しています。
主な使用言語:Java / Kotlin / PHP
>> 詳しいプロフィール
>> お問い合わせ
>> 書籍を出版しました!
本格的に学びたい方へ

Code for Fun プログラミング講座

POINT 01

動くコード

プログラミングの文法を学んでも、そこからどのようにアプリ開発ができるのかイメージが湧きにくいものです。

Code for Fun のプログラミング講座では、ゲームやカレンダーなどアプリとして機能するものを作りながらプログラミングを学ぶことができます。

POINT 02

自分のペースで

オンライン講座なので、ご自身のペースで学習を進めて頂けます。

受講期限もないので、いつでも前のレッスンに戻ることができるので安心です。

お申し込みしたその日からすぐに始めることができます。

POINT 03

個別サポート

プログラミング学習では、エラーが起きることはよくあります。そんな時はお気軽にお問い合わせください!

コメント欄またはメールによるサポートを回数無制限でご利用頂けます。(*講座に関連するご質問のみ対応)

今すぐ無料でお試し

8
0
この記事にコメントするx
記事URLをコピーしました