Javaで作るブロック崩し①|クラス、タッチイベントを作成する     

AndroidStudio

 Androidで何かアプリを作ってみようと思い、とりあえずブロック崩しを作ってみることに。。。作っていくうちに楽しくなり、様々な面白要素を追加していきました。
以下がこれから実際に作成していくアプリの完成動画となります。デザインを海っぽくしてみました(笑)。
実際に作成していくのは動きの部分だけになるためこのようなデザインにはならないです。。。(汗)
ですが、最後まで完成させ切ることができた方は、おそらく自分でもアプリを作れるようになっていると思うので、ぜひ自分なりのゲームにアレンジしてみてください。

最終的な完成動画がこちら

備忘録も兼ねて、実際に書いたコードを載せ、その説明をしていくことにします。1記事(日記)あたりのコード量を少なく、徐々に作れるように構成していこうと思います。
少しでも皆さんのお役に立てれれば幸いです。

※新しく追加するコードには網掛けをしてあります。また、見やすさの観点からコードが長くなってしまう場合は「・・・」で省略して書くことがあります。

環境:MacOS(Monterey) ツール:AndroidStudio(Arctic Fox 2020.3.1) 言語:Java

参考とした記事:https://qiita.com/Hori-Masayuki/items/9a9fcdc3c2446293b3c3

まずは基本となる、バー、ボールを作成!

完成動画がこちら
ブロック崩しを作る上では、プレイヤーが操作するバー、ボール、ブロックが必須ですが、まずは簡単なバー、ボールから作ります。
では早速、バーのクラス*から作っていきましょう!
*クラスなどの基本が分からない方は、書籍等を活用して学習してください。私のオススメの書籍はこちらです。
スッキリわかるシリーズは、初心者にとって非常に分かりやすくオススメです!特にこの2冊がオススメです。私は分からないことがあったらその都度読み直しています。

スッキリわかるJava入門第3版 [ 中山清喬 ]

価格:2,860円
(2021/11/25 00:22時点)

スッキリわかるJava入門 実践編 第3版【電子書籍】[ 中山 清喬 ]

価格:3,300円
(2021/11/24 14:39時点)

バークラスを作成

package com.example.myapp_block;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;

public class Bar extends View {                        //①描画の際はViewクラスを継承

    public int left,top,right,bottom;                  //②フィールド変数を用意
    public Paint paint;

    public Bar(Context context) {                      //③コンストラクタ生成
        super(context);
        left = 0;
        top = 0;
        right = 0;
        bottom = 0;
        paint = new Paint();                            //④Paintクラスのインスタンスを生成
        paint.setColor(Color.WHITE);                    // 白色で塗る
        paint.setStyle(Paint.Style.FILL_AND_STROKE);    // FILL_AND_STROKE:塗りつぶし+枠線
    }

    protected void onDraw(Canvas canvas) {              //⑤描写メソッド
        super.onDraw(canvas);
        Rect rect = new Rect(left,top,right,bottom);    //⑥四角形を生成
        canvas.drawRect(rect,paint);                    //⑦Canvasに四角形を描く
    }
}
まず、Viewクラスを継承した新規クラスを作成します。(①)
フィールドには、バーを描くために上下左右の位置(left、top、right、bottom)と、オブジェクトに色をつけたりするためのPaintクラスの変数を用意します。(②)
次に、コンストラクタを作成します。(③)
contextというものを引数に渡していますが、これはアプリケーションの実行状態を管理したりするもので、この情報をMainクラスから呼び出す際にBarクラスへと渡しています。
(呼び出し側:Mainクラス、呼び出される側:Barクラス)
またPaintクラスのインスタンスを生成しオブジェクトを白色で塗りつぶします。(④)
⑤では、描写メソッドにて描画したい内容を記述します。ここでは、canvasというものを引数にとっています。
何だかよく分からないなぁと思う方も多いかと思いますが、要するにcanvas(画用紙)をMainクラスからBarクラスへと渡し、その画用紙にPaint(ペンツール)を使って色々と描いていくといったイメージで良いかと思います。
           
今回はバーなので、四角形を作成するためにRect*というクラスを使っています。引数にleft、top、right、bottomを指定することでその位置、大きさの四角形が描かれます。(⑥)
これは、要するに左上(x:left、y:top)と右下(x:right、y:bottom)の2点をとって描いていることになります。
そして、最後にdrawRect()メソッドにてcanvasへの描画を行います。引数にはpaintとrectを渡します。(⑦)
長々と説明してきましたが、今後作成していくオブジェクト(ブロックや障害物など)もこれと同じような構造となります。
次に、ボールのオブジェクトを作りましょう!

*Rectは、Rectangle=長方形という意味です。

ボールクラスを作成

package com.example.myapp_block;

import android.view.View;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Color;
import android.graphics.Canvas;


public class Ball extends View {
    public int x ,y ,radius;
    Paint paint;

    public Ball(Context context){
        super(context);
        radius = 20;                    //①ボールの半径
        x = 0;                          // ボールの初期位置
        y = 0;
        paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
    }
    protected void onDraw(Canvas canvas){
        super.onDraw(canvas);
        canvas.drawCircle(x , y , radius ,paint);     //②canvasに円を描く
    }
}
基本的な形は、バークラスとほとんど同じです。
ただ今回はボールのため、四角形ではなく円を描きます。
円を描く場合、半径と中心の(x、y)座標を指定します。(①)
canvasクラスのメソッドであるdrawCircle()を使って円を描きます。引数に中心座標、半径、paintを渡します。(②)
以上で、バー、ボールのオブジェクトクラスが完成しました!
最後に、Mainクラスを書いていきます。

メインクラスを作成

レイアウト(バー、ボール)を配置

package com.example.myapp_block;

import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Color;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.view.Display;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.RelativeLayout;


public class MainActivity extends AppCompatActivity {     //①AppCompatActivityを継承

    Bar bar;                                              //②フィールド変数を用意                          
    Ball ball;
    Handler handler = new Handler();                      
    Runnable runnable;                                          
    int width, height;                
    int tap;                                              
    int dx = 10, dy = 10;            
    RelativeLayout relativeLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {     //③onCreateメソッド
        super.onCreate(savedInstanceState);

        relativeLayout = new RelativeLayout(this);           //④レイアウト作成
        relativeLayout.setBackgroundColor(Color.BLACK);      // 背景色設定
        setContentView(relativeLayout);                      // レイアウトを設定

                                                          //⑤スクリーンのサイズを取得
        WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);     
        Display display = windowManager.getDefaultDisplay();                                
        Point point = new Point();          
        display.getSize(point);           
        width = point.x;
        height = point.y;
        
        ball = new Ball(MainActivity.this);             //⑥Ballを生成
        ball.x = width / 2;                             // ボールの初期位置(x)
        ball.y = height / 2;                            // ボールの初期位置(y)
        relativeLayout.addView(ball);                   // レイアウトにボールを描写

        bar = new Bar(MainActivity.this);               //⑦barを生成                                                     
        bar.left = width / 3;                           // 左端から1/3の位置
        bar.right = width / 3 * 2;                      // 左端から2/3の位置
        bar.top = height / 5 * 4;                       // 上端から4/5の位置
        bar.bottom = height / 5 * 4 + 20;               // 上端から4/5+20の位置
        relativeLayout.addView(bar);                    // レイアウトにバーを描写
    }

    // ここにタッチイベントメソッドが入る //
}
メインクラスではAppCompatActivityを継承しています。(①)
これは、Androidアプリの画面制御の基本的な機能を提供しているクラスになります。
続いてフィールド変数です。(②)
これらについては以下で詳しく説明していきます。
ここからは、ライフサイクルメソッドの内の一つであるonCreateメソッドを書いていきます。(③)
これは画面が生成されたときに呼び出されるものです。ライフサイクルについては余力があったら後日説明したいと思います。
            
次にレイアウトクラスのインスタンスを生成します。(④)
今回はrelativeLayoutを用います。
relativeLayoutはviewを相対的に配置するためのレイアウトになります。描写する際は、基本的にこのレイアウトにViewの追加、削除を行います。
このクラスにはそのためのaddViewメソッドやremoveViewメソッドが用意されており、引数にはオブジェクトのインスタンスを渡します。
setBackgroundColorメソッドにて背景色も変えることができます。
最後に、このrelativeLayoutをレイアウトとしてセットします。
            
ここで、スクリーンのサイズを取得します。(⑤)
これは端末ごとにレイアウト変えるために必要となります。
取得するにはwindowManager→displayと生成していき、displayクラスのインスタンスメソッドであるgetSizeに引数としてpointを渡します。pointは、(x,y)座標空間内の位置を表す点になります。
ここで渡すことによって、スクリーンの最大幅をx座標、最大高さをy座標で入手することができます。
            
⑥ではボールを生成します。前もって用意したBallクラスのインスタンスを生成することでボールを生成します。ボールの初期位置を設定し、relativeLayoutのaddViewメソッドに引数で渡すことで、レイアウトにビューを追加します。
            
⑦ではバーを生成します。バーの場合もボールとほとんど同様です。
続いて、タッチイベントメソッドを追加します。

タッチイベントメソッドを追加

// タッチイベントメソッド //
@Override
    public boolean onTouchEvent(MotionEvent event) {    
        tap = (int) event.getX();                    //①指のX座標を取得
                                          
        runnable = new Runnable() {                  //②繰り返し処理(動き+当たり判定)
            @Override
            public void run() {                      // runメソッド内に繰り返し処理を書く

                bar.left = tap - (width / 6);        //③ここから動きの部分を書く   
                bar.right = tap + (width / 6);

                ball.x += dx;
                ball.y += dy;

            //画面端                                  //④ここから当たり判定を書く
                //画面左端
                if (ball.x <= ball.radius) {
                    dx = -dx;
                //画面右端
                } else if (ball.x >= width - ball.radius) {
                    dx = -dx;
                //画面上端
                } else if (ball.y <= ball.radius) {
                    dy = -dy;
                //画面下端
                } else if (ball.y >= height - ball.radius) {
                    dx = 0;
                    dy = 0;
                }

            //バー
                if ((ball.y + ball.radius >= bar.top - 5) &&            
                        (ball.y + ball.radius <= bar.top + 5) &&
                        (ball.x <= bar.right) &&                        
                        (ball.x >= bar.left)) {                                        
                    dy = -dy;
                }

                handler.removeCallbacks(runnable);     //⑤handler、Viewの処理
                relativeLayout.removeView(bar);        // 前のViewを削除  
                relativeLayout.removeView(ball);
                relativeLayout.addView(ball);          // Viewを再描写   
                relativeLayout.addView(bar);
                handler.postDelayed(runnable, 10);     // 10ミリ秒ごとに繰り返し
            }
        };
        handler.postDelayed(runnable, 10);             

        return super.onTouchEvent(event);              //⑥親クラスのタッチイベントを返す
    }
            
画面をタッチした際にイベントを発生させるにはonTouchEventメソッドを使います。引数にはeventを取ります。
タッチされた場合、そのタッチ情報がここに渡され、中の処理が実行されます。
今回は単純にタッチされた場合のみなのでシンプルに書いていますが、長押しの場合やダブルタップなど処理条件を細かく分けることもできます。
その場合、switch〜case文を使って分岐するのが主流かと思います。
            
まず初めに、指のX座標を取得します。引数で渡されたタッチ情報の中に入っているX座標を、getXメソッドを使って取り出します。(①)
            
続いて、繰り返し処理を書いていきます。今回はRunnableクラスを使っていきます。(②)
runメソッドをオーバーライドして、その中に繰り返したい内容を入れます。処理内容は動きの部分と当たり判定の2つに分けて書いていきます。
            
③は動きの処理内容になります。バーの中心がタップした際のx座標(tap)となるようにします。
これでバーを画面タッチで動かせるようになります。
また、ボールは一定の速度(dx、dy)で動かして、斜め方向(4方向)に動くようにします。
            
④は当たり判定の処理になります。今回は画面端とバーに対しての当たり判定を書いていきます。
画面端に対しての当たり判定は、ボールの中心x座標(ball.x)と画面端との距離がボールの半径(ball.radius)より大きいかどうかで判断します。
また、バーに対しては、ボール下端がバー上端に触れたときに跳ね返るようにしています。ここで、(ball.y + ball.radius == bar.top)と していないのは、ボールの動くスピードdyが10であり、この条件を満たせずにすり抜けてしまうからです。これはあくまでボールを動かす際にはボールをスピード分だけ徐々に移動させて動かしているためになります。
言葉だけだとイメージしづらいと思いますので以下に図を用意しました。

バーの当たり判定
図. バーの当たり判定
       
            
⑤では、handler、Viewの処理を行います。
1行目ではhandlerのコールバックを削除しています。
2,3行目では、前のViewを削除、4,5行目では、Viewを再描写しています。
その後handlerのpostDelayedメソッドで繰り返しを行っています。第1引数にはrunnableを、第2引数には繰り返し間隔にミリ秒(今回は10に設定)を指定します。
これらを書いておかないと、描画が更新されずオブジェクトの位置は初期のままになります。
            
最後に、親クラスのonTouchEventメソッドを実行してreturn文で返します。引数にはeventを渡します。(⑥)
            
以上で、今回の目標まで完成しました。お疲れ様です!
次回はブロックとテキストを作っていきたいと思います!

コメント

タイトルとURLをコピーしました