Javaで作るブロック崩し②|当たり判定をつけていく  

AndroidStudio
ブロック崩しと言えば、、、そうですねブロックが必要ですね(笑)
まだ作っていなかったので、今回はブロックを作っていきます!

ブロックを作成!

完成動画がこちら
では早速、ブロックのクラスから作っていきましょう!

ブロッククラスを作成

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 Block extends View {

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

    public Block(Context context) {                      //コンストラクタ生成
        super(context);
        left = 0;
        top = 0;
        right = 0;
        bottom = 0;
        paint = new Paint();                           
        paint.setColor(Color.WHITE);                   
        paint.setStyle(Paint.Style.FILL_AND_STROKE);  
    }

    protected void onDraw(Canvas canvas) {               //描写メソッド
        super.onDraw(canvas);
        Rect rect = new Rect(left,top,right,bottom);    
        canvas.drawRect(rect,paint);                   
    }
}
基本的な形は、制作日記①で作成したバークラスとほとんど同じになります。

メインクラスを編集

レイアウト(ブロック)を配置

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.View;
import android.view.WindowManager;
import android.widget.RelativeLayout;


public class MainActivity extends AppCompatActivity {    
    Bar bar;                                                                      
    Ball ball;
    Block[][] block;                    //①ブロックの配列変数を準備
    Handler handler = new Handler();
    Runnable runnable;
    RelativeLayout relativeLayout;
    int width, height;
    int tap;
    int dx = 10, dy = 10;
    int colSize, rowSize;               //②ブロック数(colSize:行, rowSize:列)


    @Override
    protected void onCreate(Bundle savedInstanceState) {     
        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.x = width / 2;                            
        ball.y = height / 2;                           
        relativeLayout.addView(ball);                 

        bar = new Bar(MainActivity.this);              
        bar.left = width / 3;                           
        bar.right = width / 3 * 2;                      
        bar.top = height / 5 * 4;                     
        bar.bottom = height / 5 * 4 + 20;              
        relativeLayout.addView(bar);                    

        colSize = 6;                          
        rowSize = 4;
        block = new Block[colSize][rowSize];        //③ブロックの配列を作成し、変数に代入
        for (int row = 0; row < rowSize; row++) {
            for (int col = 0; col < colSize; col++) {
                block[col][row] = new Block(MainActivity.this);     
                block[col][row].left = (width - (colSize - 1) * 10) / colSize * col + col * 10;
                block[col][row].right = (width - (colSize - 1) * 10) / colSize * (col + 1) + col * 10;
                block[col][row].top = height / 36 * (row + 3) + row * 10;
                block[col][row].bottom = height / 36 * (row + 4) + row * 10;
                relativeLayout.addView(block[col][row]);
            }
        }
    }

    // タッチイベントメソッド //
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        tap = (int) event.getX();                   

        runnable = new Runnable() {                  
            @Override
            public void 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);     
                relativeLayout.removeView(bar);        
                relativeLayout.removeView(ball);
                relativeLayout.addView(ball);         
                relativeLayout.addView(bar);
                handler.postDelayed(runnable, 10);     
            }
        };
        handler.postDelayed(runnable, 10);

        return super.onTouchEvent(event);              
    }


    // ここにブロックを壊すメソッドが入る //

}
まず、フィールド変数にブロックの変数を用意します。ブロックは複数のため、配列を使います。(①)
また、ブロックの行数(colSize), 列数(rowSize)も用意します。(②)
よって、ブロックの総数はcolSize × rowSizeになります。
③では、ブロックの配列を作成しています。
そして、その配列の要素にfor文を用いて、ブロックを格納していきます。ブロックを格納する際に、ブロックの位置も設定します。

leftの位置は、(画面幅 ー ブロックの隙間合計) / ブロックの数 × ブロックの列番号 + ブロックの列番号️ × 隙間 となります。
これは、前半の「(画面幅ーブロックの隙間合計)/ブロックの列数×ブロックの列番号」ではブロックをピッタリ繋ぎ合わせた場合の位置を表し、後半の「ブロックの列番号️×隙間」ではそこまでのブロック隙間分を足しています。
隙間の値は基本的には好きな数値を設定しても良いのですが、値を小さくし過ぎるとブロックの壊れ方がおかしくなってしまいます。
私は今回10に設定しました。

rightの位置はleftの位置からブロック1つ分横にずれた位置(col+1)になります。
top,bottomの場合は、left,rightのように画面幅いっぱいに配置しないため、画面の縦幅を36分割して、上から何分割目の位置から配置開始という形にしています。
            
次に、当たり判定を追加していきます。

当たり判定を追加

// 当たり判定 //
    //バーの角にボールが当たった時の当たり判定
    double leftSpace2 = Math.pow(bar.left - ball.x, 2);          
    double rightSpace2 = Math.pow(bar.right - ball.x, 2);        
    double topSpace2 = Math.pow(bar.top - ball.y, 2);           
    //バー左上角
    if (leftSpace2 + topSpace2 <= Math.pow(ball.radius, 2)) {
        //ボールが右下斜め方向に進んでいた時
        if (dx > 0 && dy > 0) {
            dx = -dx;
            dy = -dy;
        }
        //ボールが左下斜め方向に進んでいた時
        if (dx < 0 && dy > 0) {
            dy = -dy;
        }
    }
    //バー右上角
    if (rightSpace2 + topSpace2 <= Math.pow(ball.radius, 2)) {
        //ボールが右下斜め方向に進んでいた時
        if (dx > 0 && dy > 0) {
            dy = -dy;
        }
        //ボールが左下斜め方向に進んでいた時
        if (dx < 0 && dy > 0) {
            dx = -dx;
            dy = -dy;
        }
    }


    //ブロックの当たり判定
    for (int row = 0; row < rowSize; row++) {
            for (int col = 0; col < colSize; col++) {
                //ブロック左面
                if ((ball.x + ball.radius <= block[col][row].left + 5) &&           
                        (ball.x + ball.radius >= block[col][row].left - 5) &&
                        (ball.y <= block[col][row].bottom) &&                       
                        (ball.y >= block[col][row].top)) {                                         
                    dx = -dx;
                    breakBlock(col, row);                                       
                }
                //ブロック右面
                if ((ball.x - ball.radius <= block[col][row].right + 5) &&         
                        (ball.x - ball.radius >= block[col][row].right - 5) &&
                        (ball.y <= block[col][row].bottom) &&                      
                        (ball.y >= block[col][row].top)) {                                         
                    dx = -dx;
                    breakBlock(col, row);
                }
                //ブロック下面
                if ((ball.y - ball.radius <= block[col][row].bottom + 5) &&          
                        (ball.y - ball.radius >= block[col][row].bottom - 5) &&
                        (ball.x >= block[col][row].left) &&                         
                        (ball.x <= block[col][row].right)) {                                      
                    dy = -dy;
                    breakBlock(col, row);
                }
                //ブロック上面
                if ((ball.y + ball.radius <= block[col][row].top + 5) &&           
                        (ball.y + ball.radius >= block[col][row].top - 5) &&
                        (ball.x >= block[col][row].left) &&                          
                        (ball.x <= block[col][row].right)) {                                   
                    dy = -dy;
                    breakBlock(col, row);
                }
                //ブロック角にボールが当たった時の当たり判定
                //Math.pow:2乗
                double leftSpace = Math.pow(block[col][row].left - ball.x, 2);         
                double bottomSpace = Math.pow(block[col][row].bottom - ball.y, 2);     
                double rightSpace = Math.pow(block[col][row].right - ball.x, 2);       
                double topSpace = Math.pow(block[col][row].top - ball.y, 2);            
                //ブロック左下角
                if (leftSpace + bottomSpace <= Math.pow(ball.radius, 2)) {
                    //ボールが右下斜め方向に進んでいた時
                    if (dx > 0 && dy > 0) {
                        dx = -dx;
                    }
                    //ボールが右上斜め方向に進んでいた時
                    if (dx > 0 && dy < 0) {
                        dx = -dx;
                        dy = -dy;
                    }
                    //ボールが左上斜め方向に進んでいた時
                    if (dx < 0 && dy < 0) {
                        dy = -dy;
                    }
                    breakBlock(col, row);
                }
                //ブロック左上角
                if (leftSpace + topSpace <= Math.pow(ball.radius, 2)) {
                    //ボールが右下斜め方向に進んでいた時
                    if (dx > 0 && dy > 0) {
                        dx = -dx;
                        dy = -dy;
                    }
                    //ボールが右上斜め方向に進んでいた時
                    if (dx > 0 && dy < 0) {
                        dx = -dx;
                    }
                    //ボールが左下斜め方向に進んでいた時
                    if (dx < 0 && dy > 0) {
                        dy = -dy;
                    }
                    breakBlock(col, row);
                }
                //ブロック右下角
                if (rightSpace + bottomSpace <= Math.pow(ball.radius, 2)) {
                    //ボールが左上斜め方向に進んでいた時
                    if (dx < 0 && dy < 0) {
                        dx = -dx;
                        dy = -dy;
                    }
                    //ボールが右上斜め方向に進んでいた時
                    if (dx > 0 && dy < 0) {
                        dy = -dy;
                    }
                    //ボールが左下斜め方向に進んでいた時
                    if (dx < 0 && dy > 0) {
                        dx = -dx;
                    }
                    breakBlock(col, row);
                }
                //ブロック右上角
                if (rightSpace + topSpace <= Math.pow(ball.radius, 2)) {
                    //ボールが左上斜め方向に進んでいた時
                    if (dx < 0 && dy < 0) {
                        dx = -dx;
                    }
                    //ボールが右下斜め方向に進んでいた時
                    if (dx > 0 && dy > 0) {
                        dy = -dy;
                    }
                    //ボールが左下斜め方向に進んでいた時
                    if (dx < 0 && dy > 0) {
                        dx = -dx;
                        dy = -dy;
                    }
                    breakBlock(col, row);
                }
            }
        }
          
ここでは、バーの角に当たった場合の当たり判定とブロックの当たり判定を追加しています。
角に当たった場合の当たり判定も追加しないと、オブジェクトをすり抜けてしまうといった現象が発生してしまいます。
気になる方は角に当たった場合の当たり判定をコメントアウトして実行してみてください。オブジェクトの角に当たった場合にすり抜けてしまうことが実際に確認できると思います。
            
角に当たった場合のイメージについては以下の図のようになります。

ブロック角に当たった場合の当たり判定
図. ブロック角に当たった場合の当たり判定


2乗の場合は、Math.powメソッドを用います。ブロックに当たった際は、ブロックを壊すようにします(breakBlockメソッド)。
breakBlockメソッドはこの後書いていきます。
また、角の当たり判定に関しては、ボールの飛んでくる角度によって跳ね返り方を変えています。
            
では最後に、ブロックを壊すメソッドを追加します。

ブロックを壊すメソッド追加

//ブロックを壊すメソッド
    public void breakBlock(int col, int row) {
        block[col][row].top = -50;          
        block[col][row].bottom = -50;
        block[col][row].setVisibility(View.GONE);       
    }
ここでは、ボールが当たったらブロックを画面外に移動させるようにします。
setVisibilityメソッドでビューの状態を変更します。View.GONEを引数に渡すことで、ビューの存在を消すことができます。
それ以外に、View.INVISIBLEといったものがあります。これは一時的にビューを不可視化する効果があります。再びビューを表示するには、View.VISIBLEを用います。
            
以上で、今回の目標まで完成しました。お疲れ様です!
次回はテキストを作っていきたいと思います!

コメント

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