Javaで作るブロック崩し⑦|ワープと障害物を作成する 

AndroidStudio
今回はワープと障害物を作成したいと思います!
            
ではさっそく始めていきましょう!
完成動画は以下のようになります。

ワープ、障害物を作成する!

完成動画がこちら

オブジェクトクラスを作成

package com.example.myapp_block;

import android.content.Context;
import android.graphics.Canvas;
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;
    boolean breakable;                  //breakable:壊れるブロックかどうか

    public Block(Context context) {                      
        super(context);
        breakable = true;
        left = 0;
        top = 0;
        right = 0;
        bottom = 0;
        paint = new Paint();                            
        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 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 Warp extends View {
    int left, top, right, bottom;
    Paint paint;

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

    //onDraw()メソッド
    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 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 MoveBlock extends View {
    int left, top, right, bottom;
    int min, max, dx;
    Paint paint;

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

    //onDraw()メソッド
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Rect rect = new Rect(left, top, right, bottom);
        canvas.drawRect(rect, paint);
    }

    //move()メソッド
    protected void move() {
        left += dx;
        right += dx;
        if (right >= max) {
            dx = -dx;
        }
        else if (left <= min) {
            dx = -dx;
        }
    }
}
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 Moving extends View {
    int left, top, right, bottom;
    Paint paint;

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

    //onDraw()メソッド
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Rect rect = new Rect(left, top, right, bottom);
        canvas.drawRect(rect, paint);
    }
}
            
ワープや障害物のクラスも基本的にはブロッククラスと同じく四角形を描くクラスとしています。
障害物は、ブロックの一部を反射ブロックにしたもの、動く障害物(MoveBlock)、ボールを壁に飛ばす障害物(Moving)の3種類を作成しています。
 
反射ブロックは既存のブロッククラスに上記のようにコードを追加するだけになります。
 
動く障害物(MoveBlock)は動かす必要があるため、moveメソッドを用意しています。また、動く範囲をmin、maxで設定し、その間を動かすようにしています。

stageファイルを編集

//ステージ1
    //ブロック設計
    colSize = "10"              //ブロックの数
    rowSize = "4"
    margin = "10"               //ブロックの隙間
    blockType = '0000000000'    //1つ1つのブロックの有無を決める(0:ブロックなし 1:通常ブロック 2:反射ブロック)
                '0001111000'
                '0001221000'
                '0001111000'
    blockColor1 = "#663300"        //1通常ブロックの色
    blockColor2 = "#555555"        //2反射ブロックの色

//動く反射ブロック設計
    moveBlockNum = "2"       //動く反射ブロックの数
    //初期位置プロパティ     // ↓ 左から4桁ずつ、moveBlock[0]、moveBlock[1]、moveBlock[2]、、、
    moveBlockProperty1 = "00110020"    //列番号
    moveBlockProperty2 = "03000300"    //ブロックの幅
    moveBlockProperty3 = "00000000"    //初期値(x座標)の符号(0000が+、1111が-)
    moveBlockProperty4 = "05000500"    //初期値(x座標)
    //動作プロパティ
    moveBlockProperty5 = "00000000"    //ブロックの動く範囲(Min)の符号
    moveBlockProperty6 = "00000000"    //ブロックの動く範囲(Min)
    moveBlockProperty7 = "10801080"    //ブロックの動く範囲(Max)        *初期値(x座標)が含まれるようにする(画面幅はだいたい1080)
    moveBlockProperty8 = "00100005"    //ブロックの初期動作(dx、-dx)

//ワープ設計
    warpNum = "3"        //ワープの数
                        // ↓ 左から4桁ずつ、warp[0]、warp[1]、warp[2]、、、
    warpProperty1 = "010002000100"      //warpWidth(ワープの幅)
    warpProperty2 = "010002000100"      //warpHeight(ワープの高さ)
    warpProperty3 = "005005000900"      //leftの位置
    warpProperty4 = "015008001000"      //topの位置

//ムービング設計
    movingNum = "1"        //ムービングの数
                     // ↓ 左から4桁ずつ、moving[0]、moving[1]、moving[2]、、、
    movingProperty1 = "0300"      //movingWidth(ムービングの幅)
    movingProperty2 = "0050"      //movingHeight(ムービングの高さ)
    movingProperty3 = "0100"      //leftの位置
    movingProperty4 = "1200"      //topの位置
            
stageファイルにワープや障害物のプロパティを追加します。
ブロック設計では、0がブロックなし、1が通常ブロック、2が反射ブロックとしています。また、通常ブロックと反射ブロックで色を分けるように設定しています。
 
動く反射ブロック設計では、y座標の位置を列番号で指定しています。これはブロックの列の位置と合わせています。
今回の例だと、ブロックが2〜4列にあるためそれ以外の列番号を指定します。
値は4文字の数字で指定しています。動く反射ブロックの数は今回2つにしているので、1つのプロパティあたり8文字になります。
11列と20列とする場合”00110020″と書きます。これらは後ほどメインクラスにて4文字ずつ抜き出して利用します。
このように各オブジェクトのプロパティを設定していきます。

ボールクラスを編集

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, dx, dy, radius;
    Paint paint;

    public Ball(Context context){
        super(context);
        radius = 15;                    
        x = 0;                         
        y = 0;
        dx = 0;                          //ボールの初期速度
        dy = 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);    
    }
}
            
ここでボールクラスを編集します。ボールの半径が少し大きかったためここで一回り小さくさせます。(20→15)
こちらに関しては、お好みで良いかと思います(笑)。
さらにボールの初期速度dx、dyをボールクラスのフィールドとして定義するようにします。
よってこの後編集するメインクラスにて今までdx、dyとしていたのをball.dx、ball.dyに変更します。

メインクラスを編集

package com.example.myapp_block;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Point;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.RelativeLayout;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class MainActivity extends AppCompatActivity {
    Bar bar;
    Ball ball;
    Block[][] block;
    MoveBlock[] moveBlock;              //動く反射ブロックの配列変数
    Warp[] warp;
    Moving[] moving;
    TextView[] textView;
    Random r, r1, r2;
    Intent intent;
    Window window;
    Handler handler, handler1, handler2;
    Runnable runnable;
    RelativeLayout relativeLayout;
    RelativeLayout.LayoutParams params;
    TranslateAnimation translateAnimation;
    AlphaAnimation alphaAnimation;
    int width, height;
    int tap;
    int breakBlocks = 0;
    int WC = ViewGroup.LayoutParams.WRAP_CONTENT;
    boolean isBreak = false;
    boolean gameGamenFlag = false;

    //assetsフォルダ読み込み用
    InputStream inputStream;
    BufferedReader bufferedReader;
    StringBuilder stringBuilder;
    String str;
    Pattern pattern1, pattern2;
    Matcher matcher1, matcher2;
    List<String> list1, list2;

    //ステージデータ
    //(ブロック)
    int colSize, rowSize;
    int margin;
    String blockType;
    String blockColor1, blockColor2;
    //動く反射ブロック
    int moveBlockNum;
    String moveBlockProperty1, moveBlockProperty2, moveBlockProperty3, moveBlockProperty4, moveBlockProperty5,
            moveBlockProperty6, moveBlockProperty7, moveBlockProperty8;
    //ワープ
    int warpNum;
    String warpProperty1, warpProperty2, warpProperty3, warpProperty4;
    //ムービング
    int movingNum;
    String movingProperty1, movingProperty2, movingProperty3, movingProperty4;


    @RequiresApi(api = Build.VERSION_CODES.O)
    @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;

        //ステージ情報を設定する(assetsファイルから文字列で入手)
        inputStream = null;
        bufferedReader = null;
        stringBuilder = null;
        try {
            inputStream = getAssets().open("stage1");                 
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));    
            stringBuilder = new StringBuilder();
            while ((str = bufferedReader.readLine()) != null) {                 
                stringBuilder.append(str);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) inputStream.close();
                if (bufferedReader != null) bufferedReader.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (stringBuilder != null) {
            // ""(ダブルクォーテーション)で挟まれた文字列を抽出する
            pattern1 = Pattern.compile("\".+?\"");
            matcher1 = pattern1.matcher(stringBuilder.toString());
            list1 = new ArrayList<String>();
            while (matcher1.find()) {
                list1.add(matcher1.group());
            }
            // ''(シングルクォーテーション)で挟まれた文字列を抽出する
            pattern2 = Pattern.compile("\'.+?\'");
            matcher2 = pattern2.matcher(stringBuilder.toString());
            list2 = new ArrayList<String>();
            while (matcher2.find()) {
                list2.add(matcher2.group());
            }
            //ブロック
            colSize = Integer.parseInt(list1.get(0).substring(1, list1.get(0).length()-1));
            rowSize = Integer.parseInt(list1.get(1).substring(1, list1.get(1).length()-1));
            margin = Integer.parseInt(list1.get(2).substring(1, list1.get(2).length()-1));
            blockType = String.join("", list2).replace("'", "");
            blockColor1 = list1.get(3).substring(1, list1.get(3).length()-1);
            blockColor2 = list1.get(4).substring(1, list1.get(4).length()-1);
            //動く反射ブロック
            moveBlockNum = Integer.parseInt(list1.get(5).substring(1, list1.get(5).length()-1));
            moveBlockProperty1 = list1.get(6).substring(1, list1.get(6).length()-1);
            moveBlockProperty2 = list1.get(7).substring(1, list1.get(7).length()-1);
            moveBlockProperty3 = list1.get(8).substring(1, list1.get(8).length()-1);
            moveBlockProperty4 = list1.get(9).substring(1, list1.get(9).length()-1);
            //動作プロパティ
            moveBlockProperty5 = list1.get(10).substring(1, list1.get(10).length()-1);
            moveBlockProperty6 = list1.get(11).substring(1, list1.get(11).length()-1);
            moveBlockProperty7 = list1.get(12).substring(1, list1.get(12).length()-1);
            moveBlockProperty8 = list1.get(13).substring(1, list1.get(13).length()-1);
            //ワープ
            warpNum = Integer.parseInt(list1.get(14).substring(1, list1.get(14).length()-1));
            warpProperty1 = list1.get(15).substring(1, list1.get(15).length()-1);
            warpProperty2 = list1.get(16).substring(1, list1.get(16).length()-1);
            warpProperty3 = list1.get(17).substring(1, list1.get(17).length()-1);
            warpProperty4 = list1.get(18).substring(1, list1.get(18).length()-1);
            //ムービング
            movingNum = Integer.parseInt(list1.get(19).substring(1, list1.get(19).length()-1));
            movingProperty1 = list1.get(20).substring(1, list1.get(20).length()-1);
            movingProperty2 = list1.get(21).substring(1, list1.get(21).length()-1);
            movingProperty3 = list1.get(22).substring(1, list1.get(22).length()-1);
            movingProperty4 = list1.get(23).substring(1, list1.get(23).length()-1);
        }

        //ボール生成
        ball = new Ball(MainActivity.this);
        ball.x = width / 2;
        ball.y = height / 2;
        ball.dx = 10;
        ball.dy = 10;
        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);

        //ブロック生成
        block = new Block[colSize][rowSize];
        int order = 0;       
        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) * margin) / colSize * col + col * margin;
                block[col][row].right = (width - (colSize - 1) * margin) / colSize * (col + 1) + col * margin;
                block[col][row].top = height / 36 * row + row * margin;
                block[col][row].bottom = height / 36 * (row + 1) + row * margin;
                relativeLayout.addView(block[col][row]);

                //ブロックの配置する
                String type = blockType.substring(order, order+1);   
                if (type.equals("0")) {
                    block[col][row].top = -50;
                    block[col][row].bottom = -50;
                    block[col][row].setVisibility(View.GONE);       
                    breakBlocks++;
                }
                else if (type.equals("1")) {
                    block[col][row].paint.setColor(Color.parseColor(blockColor1));
                }
                else if (type.equals("2")) {
                    block[col][row].paint.setColor(Color.parseColor(blockColor2));
                    block[col][row].breakable = false;       //反射ブロックとする
                    breakBlocks++;
                }

                order++;
            }
        }

        //動く反射ブロック生成
        moveBlock = new MoveBlock[moveBlockNum];
        for (int num = 0; num < moveBlockNum; num++) {
            moveBlock[num] = new MoveBlock(this);
            int property1 = Integer.parseInt(moveBlockProperty1.substring(num*4, (num+1)*4));    //プロパティを切り出す(列番号、4文字)
            int property2 = Integer.parseInt(moveBlockProperty2.substring(num*4, (num+1)*4));    //プロパティを切り出す(初期値のx座標、4文字)
            int property3 = Integer.parseInt(moveBlockProperty3.substring(num*4, (num+1)*4));    //プロパティを切り出す(初期値のx座標の符号、4文字)
            int property4 = Integer.parseInt(moveBlockProperty4.substring(num*4, (num+1)*4));    //プロパティを切り出す(ブロックの幅、4文字)

            // height / 36 * row :高さを36分割して、最初のブロックの位置を画面上端の位置からにする(row=0の時。ブロック1つ分の高さ:height/36)   
            moveBlock[num].top = height / 36 * (property1 - 1) + (property1 - 1) * margin;             //property1:列番号
            moveBlock[num].bottom = height / 36 * property1 + (property1 - 1) * margin;

            //初期位置プロパティを設定
            if (property3 == 0){
                moveBlock[num].left = property4;        //property4:初期値(x座標)  property3:その符号(0なら+、それ以外ならー)
            }
            else {
                moveBlock[num].left = -property4;
            }
            moveBlock[num].right = moveBlock[num].left + property2;         //property2:ブロックの幅

            //動く反射ブロックを描写
            relativeLayout.addView(moveBlock[num]);

            //動作プロパティを設定
            int property5 = Integer.parseInt(moveBlockProperty5.substring(num * 4, (num + 1) * 4));    //プロパティを切り出す(ブロックの動く範囲(Min)の符号、4文字)
            int property6 = Integer.parseInt(moveBlockProperty6.substring(num * 4, (num + 1) * 4));    //プロパティを切り出す(ブロックの動く範囲(Min)、4文字)
            int property7 = Integer.parseInt(moveBlockProperty7.substring(num * 4, (num + 1) * 4));    //プロパティを切り出す(ブロックの動く範囲(Max)、4文字)
            int property8 = Integer.parseInt(moveBlockProperty8.substring(num * 4, (num + 1) * 4));    //プロパティを切り出す(ブロックの初期動作(dx、-dx)、4文字)
            moveBlock[num].min = property6;
            if (property5 != 0)    moveBlock[num].min = -property6;     //property5:符号(0なら+、それ以外ならー)
            moveBlock[num].max = property7;
            moveBlock[num].dx = property8;
        }

        //ワープ生成
        warp = new Warp[warpNum];
        for (int num = 0; num < warpNum; num++) {
            warp[num] = new Warp(this);
            int warpWidth = Integer.parseInt(warpProperty1.substring(num*4, (num+1)*4));        //プロパティを切り出す(warpの幅、4文字)
            int warpHeight = Integer.parseInt(warpProperty2.substring(num*4, (num+1)*4));       //プロパティを切り出す(warpの高さ、4文字)
            warp[num].left = Integer.parseInt(warpProperty3.substring(num*4, (num+1)*4));       //プロパティを切り出す(left、4文字)
            warp[num].top = Integer.parseInt(warpProperty4.substring(num*4, (num+1)*4));        //プロパティを切り出す(top、4文字)
            warp[num].right = warp[num].left + warpWidth;
            warp[num].bottom = warp[num].top + warpHeight;
            relativeLayout.addView(warp[num]);
        }

        //ムービング生成
        moving = new Moving[movingNum];
        for (int num = 0; num < movingNum; num++) {
            moving[num] = new Moving(this);
            int movingWidth = Integer.parseInt(movingProperty1.substring(num*4, (num+1)*4));        //プロパティを切り出す(movingの幅、4文字)
            int movingHeight = Integer.parseInt(movingProperty2.substring(num*4, (num+1)*4));       //プロパティを切り出す(movingの高さ、4文字)
            moving[num].left = Integer.parseInt(movingProperty3.substring(num*4, (num+1)*4));       //プロパティを切り出す(left、4文字)
            moving[num].top = Integer.parseInt(movingProperty4.substring(num*4, (num+1)*4));        //プロパティを切り出す(top、4文字)
            moving[num].right = moving[num].left + movingWidth;
            moving[num].bottom = moving[num].top + movingHeight;
            relativeLayout.addView(moving[num]);
        }

        //テキスト生成
        textView = new TextView[7];
        for (int num = 0; num < 7; num++) {
            textView[num] = new TextView(this);
            textView[num].setTextColor(Color.WHITE);
            textView[num].setText("");
            switch (num) {
                case 0: textViewProperty(num, 32, 400, 1800);
                    break;
                case 1: textViewProperty(num, 64, 80, 700);
                    break;
                case 2: textViewProperty(num, 28, 80, 1100);
                    break;
                case 3: textViewProperty(num, 28, 610, 1100);
                    break;
                case 4: textViewProperty(num, 28, 650, 1100);
                    break;
                case 5: textViewProperty(num, 64, 130, 700);
                    break;
                case 6: textViewProperty(num, 64, 210, 700);
                    break;
            }
        }

        //スタートプロセス
        gameProcess('s');
    }


    //テキストプロパティ設定メソッド
    public void textViewProperty(int num, int size, int leftMargin, int topMargin) {
        textView[num].setTextSize(size);
        //レイアウトの属性を設定(LayoutParamsクラス)
        params = new RelativeLayout.LayoutParams(WC, WC);
        params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
        params.setMargins(leftMargin, topMargin, 0, 0);
        relativeLayout.addView(textView[num], params);
    }


    //プロセス移行メソッド
    @RequiresApi(api = Build.VERSION_CODES.O)
    public void gameProcess(char s){
        
                   ・
                   ・
                   ・

            break;
        }
    }


    // タッチイベントメソッド //
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        textView[0].setText("");

        tap = (int) event.getX();

        runnable = new Runnable() {
            @RequiresApi(api = Build.VERSION_CODES.O)
            @Override
            public void run() {
                //ゲーム進行
                if (gameGamenFlag) {
                    //ここから動きの部分
                    bar.left = tap - (width / 6);
                    bar.right = tap + (width / 6);

                    //ボールを動かす
                    ball.x += ball.dx;
                    ball.y += ball.dy;

                    //反射ブロックを動かす
                    for (int num = 0; num < moveBlockNum; num++) {
                        moveBlock[num].move();
                    }

                    //ブロックを全て壊したらゲームクリア
                    if (breakBlocks == rowSize * colSize) {
                        //ゲームクリアプロセス
                        gameProcess('c');
                    }

                    //ボールが画面下に出たら、ゲームオーバー
                    if (ball.dx == 0 && breakBlocks != rowSize * colSize){
                        //ゲームオーバープロセス
                        gameProcess('o');
                    }

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

                    //バー
                    //当たり判定メソッドを呼び出す
                    ballHit(bar.left, bar.top, bar.right, bar.bottom, 0, 0, false);

                    //ブロック
                    for (int row = 0; row < rowSize; row++) {
                        for (int col = 0; col < colSize; col++) {

                            //当たり判定メソッドを呼び出す
                            ballHit(block[col][row].left, block[col][row].top, block[col][row].right, block[col][row].bottom, 0, 0, block[col][row].breakable);

                            //ボールに当たった場合にブロックを壊す
                            if(isBreak) {
                                breakBlock(col, row);
                                //フラグを元に戻す
                                isBreak = false;
                            }
                        }
                    }

                    //動く反射ブロック
                    for (int num = 0; num < moveBlockNum; num++) {
                        //当たり判定メソッドを呼び出す
                        ballHit(moveBlock[num].left, moveBlock[num].top, moveBlock[num].right, moveBlock[num].bottom, moveBlock[num].dx, 0, false);
                    }

                    //③ワープ
                    for (int num = 0; num < warpNum; num++) {
                        if ((ball.x > warp[num].left) &&
                                (ball.x < warp[num].right) &&
                                (ball.y > warp[num].top) &&
                                (ball.y < warp[num].bottom)) {
                            r1 = new Random();
                            int random1 = r1.nextInt(warpNum);
                            if (random1 == num){
                                for (;;){
                                    r = new Random();
                                    int random = r.nextInt(warpNum);
                                    random1 = random;
                                    if (random1 != num)     break;
                                }
                            }
                            r2 = new Random();
                            int random2 = r2.nextInt(4);
                            switch (random2){
                                case 0: {   //ワープの左上から出てくる
                                    ball.x = warp[random1].left;
                                    ball.y = warp[random1].top;
                                    if (ball.dx > 0){
                                        ball.dx = -ball.dx;
                                    }
                                    if (ball.dy > 0){
                                        ball.dy = -ball.dy;
                                    }
                                    break;
                                }
                                case 1: {   //ワープの左下から出てくる
                                    ball.x = warp[random1].left;
                                    ball.y = warp[random1].bottom;
                                    if (ball.dx > 0){
                                        ball.dx = -ball.dx;
                                    }
                                    if (ball.dy < 0){
                                        ball.dy = -ball.dy;
                                    }
                                    break;
                                }
                                case 2: {   //ワープの右下から出てくる
                                    ball.x = warp[random1].right;
                                    ball.y = warp[random1].bottom;
                                    if (ball.dx < 0){
                                        ball.dx = -ball.dx;
                                    }
                                    if (ball.dy < 0){
                                        ball.dy = -ball.dy;
                                    }
                                    break;
                                }
                                case 3: {   //ワープの右上から出てくる
                                    ball.x = warp[random1].right;
                                    ball.y = warp[random1].top;
                                    if (ball.dx < 0){
                                        ball.dx = -ball.dx;
                                    }
                                    if (ball.dy > 0){
                                        ball.dy = -ball.dy;
                                    }
                                    break;
                                }
                            }
                        }
                    }

                    //④ムービング
                    for (int num = 0; num < movingNum; num++) {
                        if ((ball.x + ball.radius > moving[num].left) &&
                                (ball.x - ball.radius < moving[num].right) &&
                                (ball.y > (moving[num].top + moving[num].bottom) / 2 - (moving[num].bottom - moving[num].top) / 4 &&
                                        (ball.y < (moving[num].top + moving[num].bottom) / 2 + (moving[num].bottom - moving[num].top) / 4 ))) {
                            if (ball.dy != 0){
                                r = new Random();
                                int random = r.nextInt(2);
                                if (random == 0){
                                    ball.dx = -ball.dx;             //1/2の確率で水平方向反転する
                                }
                            }
                            ball.dy = 0;
                        }
                        if(ball.dy == 0){
                            //画面左面
                            if (ball.x <= ball.radius) {
                                ball.dy = 10;
                            }
                            //画面右面
                            else if (ball.x >= width - ball.radius) {
                                ball.dy = 10;
                            }
                        }
                    }

                    handler.removeCallbacks(runnable);
                    //バー
                    relativeLayout.removeView(bar);
                    relativeLayout.addView(bar);
                    //ボール
                    relativeLayout.removeView(ball);
                    relativeLayout.addView(ball);
                    //動く反射ブロック
                    for (int num = 0; num < moveBlockNum; num++) {
                        relativeLayout.removeView(moveBlock[num]);
                        relativeLayout.addView(moveBlock[num]);
                    }
                }
                handler.postDelayed(runnable, 10);
            }
        };
        handler.postDelayed(runnable, 10);

        return super.onTouchEvent(event);
    }


    //②当たり判定メソッド(オブジェクト用)
    public void ballHit(int left, int top, int right, int bottom, int dx, int dy, boolean isBreakObject) {
        //ボールの相対速度x(ボールのx速度ー動くオブジェクトのx速度)から、判定の境界線範囲を決める
        int rangeX = (ball.dx - dx) / 2;
        if (rangeX < 0){
            rangeX = -rangeX;
        }
        int rangeY = (ball.dy - dy) / 2;
        if (rangeY < 0){
            rangeY = -rangeY;
        }

        //左面
        if ((ball.x + ball.radius <= left + rangeX) &&
                (ball.x + ball.radius >= left - rangeX) &&
                (ball.y <= bottom + rangeY) &&
                (ball.y >= top - rangeY) &&
                (ball.dx > 0)) {
            ball.dx = -ball.dx;
            if (isBreakObject) {
                isBreak = true;
            }
        }
        //右面
        if ((ball.x - ball.radius <= right + rangeX) &&
                (ball.x - ball.radius >= right - rangeX) &&
                (ball.y <= bottom + rangeY) &&
                (ball.y >= top - rangeY) &&
                (ball.dx < 0)) {
            ball.dx = -ball.dx;
            if (isBreakObject) {
                isBreak = true;
            }
        }
        //下面
        if ((ball.y - ball.radius <= bottom + rangeY) &&
                (ball.y - ball.radius >= bottom - rangeY) &&
                (ball.x >= left - rangeX) &&
                (ball.x <= right + rangeX) &&
                (ball.dy < 0)) {
            ball.dy = -ball.dy;
            if (isBreakObject) {
                isBreak = true;
            }
        }
        //上面
        if ((ball.y + ball.radius <= top + rangeY) &&
                (ball.y + ball.radius >= top - rangeY) &&
                (ball.x >= left - rangeX) &&
                (ball.x <= right + rangeX) &&
                (ball.dy > 0)) {
            ball.dy = -ball.dy;
            if (isBreakObject) {
                isBreak = true;
            }
        }
        //角にボールが当たった時の当たり判定
        //Math.pow:2乗
        double leftSpace = Math.pow(left - ball.x, 2);
        double bottomSpace = Math.pow(bottom - ball.y, 2);
        double rightSpace = Math.pow(right - ball.x, 2);
        double topSpace = Math.pow(top - ball.y, 2);
        //左下角
        if (leftSpace + bottomSpace <= Math.pow(ball.radius, 2)) {
            //ボールが右下斜め方向に進んでいた時
            if (ball.dx > 0 && ball.dy > 0) {
                ball.dx = -ball.dx;
            }
            //ボールが右上斜め方向に進んでいた時
            if (ball.dx > 0 && ball.dy < 0) {
                ball.dx = -ball.dx;
                ball.dy = -ball.dy;
            }
            //ボールが左上斜め方向に進んでいた時
            if (ball.dx < 0 && ball.dy < 0) {
                ball.dy = -ball.dy;
            }
            if (isBreakObject) {
                isBreak = true;
            }
        }
        //左上角
        if (leftSpace + topSpace <= Math.pow(ball.radius, 2)) {
            //ボールが右下斜め方向に進んでいた時
            if (ball.dx > 0 && ball.dy > 0) {
                ball.dx = -ball.dx;
                ball.dy = -ball.dy;
            }
            //ボールが右上斜め方向に進んでいた時
            if (ball.dx > 0 && ball.dy < 0) {
                ball.dx = -ball.dx;
            }
            //ボールが左下斜め方向に進んでいた時
            if (ball.dx < 0 && ball.dy > 0) {
                ball.dy = -ball.dy;
            }
            if (isBreakObject) {
                isBreak = true;
            }
        }
        //右下角
        if (rightSpace + bottomSpace <= Math.pow(ball.radius, 2)) {
            //ボールが左上斜め方向に進んでいた時
            if (ball.dx < 0 && ball.dy < 0) {
                ball.dx = -ball.dx;
                ball.dy = -ball.dy;
            }
            //ボールが右上斜め方向に進んでいた時
            if (ball.dx > 0 && ball.dy < 0) {
                ball.dy = -ball.dy;
            }
            //ボールが左下斜め方向に進んでいた時
            if (ball.dx < 0 && ball.dy > 0) {
                ball.dx = -ball.dx;
            }
            if (isBreakObject) {
                isBreak = true;
            }
        }
        //右上角
        if (rightSpace + topSpace <= Math.pow(ball.radius, 2)) {
            //ボールが左上斜め方向に進んでいた時
            if (ball.dx < 0 && ball.dy < 0) {
                ball.dx = -ball.dx;
            }
            //ボールが右下斜め方向に進んでいた時
            if (ball.dx > 0 && ball.dy > 0) {
                ball.dy = -ball.dy;
            }
            //ボールが左下斜め方向に進んでいた時
            if (ball.dx < 0 && ball.dy > 0) {
                ball.dx = -ball.dx;
                ball.dy = -ball.dy;
            }
            if (isBreakObject) {
                isBreak = true;
            }
        }
    }


    //ブロックを壊すメソッド
    public void breakBlock(int col, int row) {
        block[col][row].top = -50;
        block[col][row].bottom = -50;
        block[col][row].setVisibility(View.GONE);
        breakBlocks++;                                 
    }


    //オブジェクト出現切り替えメソッド
    public void objectsAppear(int status){
        switch (status){
            case 0: {       //画面OUT
                //ボール
                startAnimation(ball, height, 0);
                //ブロック
                for (int row = 0; row < rowSize; row++) {
                    for (int col = 0; col < colSize; col++) {
                        startAnimation(block[col][row], height, 0);
                    }
                }
                //動く反射ブロック
                for (int num = 0; num < moveBlockNum; num++) {
                    startAnimation(moveBlock[num], height, 0);
                }
                //ワープ
                for (int num = 0; num < warpNum; num++) {
                    startAnimation(warp[num], height, 0);
                }
                //ムービング
                for (int num = 0; num < movingNum; num++) {
                    startAnimation(moving[num], height, 0);
                }
            }
            break;
            case 1: {       //画面IN
                //ボール
                startAnimation(ball, height, 1);
                //ブロック
                for (int row = 0; row < rowSize; row++) {
                    for (int col = 0; col < colSize; col++) {
                        startAnimation(block[col][row], height, 1);
                    }
                }
                //動く反射ブロック
                for (int num = 0; num < moveBlockNum; num++) {
                    startAnimation(moveBlock[num], height, 1);
                }
                //ワープ
                for (int num = 0; num < warpNum; num++) {
                    startAnimation(warp[num], height, 1);
                }
                //ムービング
                for (int num = 0; num < movingNum; num++) {
                    startAnimation(moving[num], height, 1);
                }
            }
            break;
        }
    }


    //アニメーション実装メソッド
    private void startAnimation(View view, int height, int type) {
        
                   ・
                   ・
                   ・

            break;
        }
    }

}
            
最後にメインクラスにて、先ほど作成したオブジェクトを生成し、プロパティを設定していきます。
各オブジェクト生成において、プロパティを4文字ずつstageファイルから取り出しています。
そしてそれらを使ってオブジェクトの位置や幅などを設定します。
網掛け①、②の当たり判定では、ボールの進行方向がオブジェクトや壁方向の時のみ反射するように変更しています。
→ ball.dx < 0 (ball.dx > 0) or ball.dy < 0 (ball.dy > 0)を追加
このようにしておかないと、ボールが壁やオブジェクトの側面を沿って動くといったエラーがたまに起こります。
また、②の当たり判定メソッドでは、ボールの相対速度(ボールの速度ーオブジェクトの速度)を求めてから当たり判定の境界線範囲(rangeX or rangeY)を決めています。相対速度は絶対値を取る必要があるためマイナスの場合は-をかけて+にします。
その後、境界線範囲内にボールが入るように設定します。境界線範囲はー相対速度/2 〜 相対速度/2の範囲にします。
例えば、相対速度が10の場合は、(ball.x+ball.radius<=left+5) && (ball.x+ball.radius>=left-5)となります。
網掛け③のワープの当たり判定では、当たったワープとは別のワープから出てくるよう実装します。そのために最初にランダム値random1を生成し、0〜ワープ数のランダムな整数を取り出せるようにします。そして実際に取り出したものが当たったワープのインデントと同じ場合はやり直し、違う場合はループから抜けます。(for (;;)とすることで無限ループが可能になります)
これで当たったワープとは別のワープを取り出すことが可能になりました。
続いて、ランダム値random2を生成し、ワープの4方向(左上、右上、右下、左下)のどこから出てくるかを決めます。
これでワープは完成となります。
次にボールを壁まで吹き飛ばすムービングの当たり判定(網掛け④)を実装していきます。ここに関してもそこまで難しいことはしていません。
まず当たった際にx軸方向を反転させるかランダムに決めます。その後、dyを0にして水平に移動させます。そして壁にぶつかったらdyを10にして下斜め方向にボールが動くようにさせています。
これでムービングは完成となります。大したことないですよね(笑)
以上でワープと障害物の作成は終了です。
お疲れ様でした!

コメント

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