質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.23%

テトリスの衝突判定がうまくできない

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 4,267

hozumi_45

score 11

下記のコードでテトリスを作ったのですがミノブロックが壁にめり込んでしまいます。考え方自体はあっていると思うのですが、どうしても内部だけで動き、積みあがる処理ができません。大変申し訳ありませんが下記のコードのどこが問題なのかご指摘いただけないでしょうか?よろしくお願いいたします。

コード
import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class Tetris extends Applet implements Runnable,KeyListener {
    /**縦22マス*/
    public static final int HEIGHT=22;
    /**横13マス*/
    public static final int WIDTH=13;
    /**ブロックの大きさ*/
    public static final int SIZE =30;

    private int [][]field=new int[WIDTH][HEIGHT];
    /**色の選択*/
    public static final Color[] COLOR_SELECT={Color.WHITE,Color.ORANGE,Color.GREEN,Color.YELLOW,Color.GRAY,Color.MAGENTA,Color.BLUE,Color.RED,Color.BLACK};
    /**ブロックの向き*/
    public static final int DIRECTION=0;

    public static final int SPACE=0;
    public static final int WALL=1;
    /**中心のブロックの初期座標*/
    Point point = new Point(6,0);
    /**テトリミノの座標*/
    Point minoPoint;

    /**ブロックの種類*/
    private Point data[][][]=    
            //L字型ブロック
        {{{new Point(-1,0),new Point(0,0),new Point(1,0),new Point(-1,1)},// 初期
            {new Point(0,-1),new Point(1,-1),new Point(0,0),new Point(1,1)},// 90度回転
            {new Point(1,0),new Point(0,0),new Point(0,1),new Point(1,1)},    // 180度回転
            {new Point(-1,-1),new Point(0,-1),new Point(0,0),new Point(0,1)},},//270度回転
            //田字型ブロック    
            {{new Point(-1,0),new Point(0,0),new Point(-1,1),new Point(0,1)}, // 初期
                {new Point(-1,0),new Point(0,0),new Point(-1,1),new Point(0,1)}, // 90度回転
                {new Point(-1,0),new Point(0,0),new Point(-1,1),new Point(0,1)}, // 180度回転
                {new Point(-1,0),new Point(0,0),new Point(-1,1),new Point(0,1)},},// 270度回転
                //I字型ブロック
                {{new Point(-2,0),new Point(-1,0),new Point(0,0),new Point(1,0)}, // 初期
                    {new Point(0,-1),new Point(0,0),new Point(0,1),new Point(0,1)}, // 90度回転
                    {new Point(-2,0),new Point(-1,0),new Point(0,0),new Point(1,0)}, // 180度回転
                    {new Point(0,-1),new Point(0,0),new Point(0,1),new Point(0,2)},},// 270度回転
                    //Z字型ブロック
                    {{new Point(0,0),new Point(1,0),new Point(-1,1),new Point(0,1)}, // 初期
                        {new Point(-1,-1),new Point(-1,0),new Point(0,0),new Point(0,1)}, // 90度回転
                        {new Point(0,0),new Point(1,0),new Point(-1,1),new Point(0,1)}, // 180度回転
                        {new Point(-1,-1),new Point(-1,0),new Point(0,0),new Point(0,1)},}, // 270度回転
                        //T字型ブロック
                        {{new Point(-1,0),new Point(0,0),new Point(1,0),new Point(0,1)}, // 初期
                            {new Point(0,-1),new Point(0,0),new Point(1,0),new Point(0,1)}, // 90度回転
                            {new Point(0,0),new Point(-1,1),new Point(0,1),new Point(1,1)}, // 180度回転
                            {new Point(0,-1),new Point(-1,0),new Point(0,0),new Point(0,1)},}, // 270度回転
                            //逆Z字型ブロック
                            {{new Point(-1,0),new Point(0,0),new Point(0,1),new Point(1,1)}, // 初期
                                {new Point(1,-1),new Point(0,0),new Point(1,0),new Point(0,1)}, // 90度回転
                                {new Point(-1,0),new Point(0,0),new Point(0,1),new Point(1,1)}, // 180度回転
                                {new Point(1,-1),new Point(0,0),new Point(1,0),new Point(0,1)},}, // 270度回転
                                //逆L字型ブロック
                                {{new Point(-1,0),new Point(0,0),new Point(1,0),new Point(1,1)}, // 初期
                                    {new Point(0,-1),new Point(1,-1),new Point(0,0),new Point(0,1)}, // 90度回転
                                    {new Point(0,0),new Point(-1,1),new Point(0,1),new Point(1,1)}, // 180度回転
                                    {new Point(0,-1),new Point(0,0),new Point(-1,1),new Point(0,1)},}};// 270度回転

    /**乱数生成*/
    Random random = new Random();
    /**ランダムな色のブロック表示*/
    private int colorSelect =random.nextInt(7)+2;
    /**ランダムな形のブロック表示*/
    private int blockSelect =random.nextInt(7)+2;
    /**初期化処理*/
    @Override
    public void init(){
        addKeyListener(this);
        requestFocus();
        for(int y=0;y<HEIGHT;y++) {
            for(int x=0;x<WIDTH; x++) {
                field[x][y]=SPACE;
            }
        }
        for (int x=0;x<WIDTH;x++) {
            field[x][HEIGHT-1]=WALL;
        }
        for(int y=0;y<HEIGHT;y++) {
            field[0][y]=WALL;
            field[WIDTH-1][y]=WALL;
        }
        initBlock();
        for(int i=0;i<4;i++){
            minoPoint=data[blockSelect][DIRECTION][i];
        }
    }
    /**@param e KeyEvent e*/
    @Override
    public void keyPressed(KeyEvent e){
        int key = e.getKeyCode();
        if (key==KeyEvent.VK_LEFT){// 方向キー左が押されたとき
            if(field[(point.x-1)+minoPoint.x][point.y+minoPoint.y]==SPACE){// ブロックがSPACEのとき
                point.x--;
                repaint();
            }

        }else if (key==KeyEvent.VK_RIGHT){// 方向キー右が押されたとき 
            if(field[(point.x+1)+minoPoint.x][point.y+minoPoint.y]==SPACE){// ブロックがSPACEのとき
                point.x++;
                repaint();
            }

        }else if (key==KeyEvent.VK_DOWN){// 方向キー下が押されたとき 
            if(field[point.x+minoPoint.x][(point.y+1)+minoPoint.y]==SPACE){// ブロックがSPACEのとき
                point.y++;
                repaint();
            }else{// 底辺もしくは他のブロックに接したとき
                initBlock();
                field[point.x+minoPoint.x][point.y+minoPoint.y]=colorSelect;
                repaint();
            }
        }else if(key==KeyEvent.VK_SPACE){//回転させる処理
        }

    }


    @Override
    public void keyReleased(KeyEvent e){
    }

    @Override
    public void keyTyped(KeyEvent e){
    }
    /**スレッド開始*/
    @Override
    public void start(){
        Thread th = new Thread(this);
        th.start();
    }


    /**落下メソッド*/
    @Override
    public void run(){
        while(true){
            try {
                Thread.sleep(1000); // 1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(field[minoPoint.x+point.x][minoPoint.y+(point.y+1)]==SPACE){// ブロックがSPACEのとき
                point.y++;
                repaint();
            }else{//底辺もしくは他のブロックに接したとき
                initBlock();
                field[minoPoint.x+point.x][minoPoint.y+point.y]=colorSelect;
            }
            repaint();
        }
    }



    /**@param g Graphics*/
    @Override
    public void update(Graphics g){
        paint(g);
    }
    /**@param g Graphics*/
    @Override
    public void paint(Graphics g){
        Dimension size=getSize();
        Image back=createImage(size.width, size.height);
        Graphics buffer=back.getGraphics();
        // 移動ブロックの描画
        if(field[point.x+minoPoint.x][point.y+minoPoint.y]==SPACE){
            buffer.setColor(COLOR_SELECT[colorSelect]);
            buffer.fillRect((point.x+minoPoint.x)*SIZE,(point.y+minoPoint.y)*SIZE,SIZE,SIZE);
        }
        // 外壁の描画
        for(int i = 0;i <WIDTH;i++){
            for(int j = 0;j <HEIGHT;j++){
                if(field[i][j]!=SPACE){ // fieldがSPACE以外のとき
                    buffer.setColor(COLOR_SELECT[field[i][j]]);
                    buffer.fillRect(i*SIZE,j*SIZE,SIZE,SIZE);
                }
            }
        }
        buffer.setColor(COLOR_SELECT[colorSelect]);
        for (int i = 0; i<4; i++) {
            buffer.fillRect(SIZE*(point.x+data[blockSelect][DIRECTION][i].x),SIZE*(point.y+data[blockSelect][DIRECTION][i].y),SIZE,SIZE);
        }
        // マス目描画
        buffer.setColor(Color.black);
        for(int i=0;i<=HEIGHT;i++){
            buffer.drawLine(0,i*SIZE,WIDTH*SIZE,i*SIZE);
        }
        for(int j=0;j<=WIDTH;j++){
            buffer.drawLine(j*SIZE,0,j*SIZE,HEIGHT*SIZE);
        }
        g.drawImage(back, 0, 0, this);
    }
    /**ランダムな色で次のブロックを生成*/
    public void initBlock(){
        point = new Point(6, 0);// 初期配置に戻す
        colorSelect = random.nextInt(7)+2;
        blockSelect =random.nextInt(7)+2;
    }
}


  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

まず、キー入力時に壁にめり込んでしまう問題は、ブロックの各セルの位置ごとに判定が必要ですね。
下記のように、「動かせるかどうか」を判定するメソッドを定義して、それを呼ぶようにすると良いです。
        if (key==KeyEvent.VK_LEFT){// 方向キー左が押されたとき
            // if(field[(point.x-1)+minoPoint.x][point.y+minoPoint.y]==SPACE){// ブロックがSPACEのとき
            if (canMove(-1, 0)) { // ★ここ★
                point.x--;
                repaint();
            }
        }
        // 右方向の場合は、canMove(1, 0)、
        // 下方向の場合は、canMove(0, 1)で判定

// (中略)

    /**
     * ブロックが動かせるかどうかを調べる。
     * @param xRel X方向の相対値
     * @param yRel Y方向の相対値
     * @return 動かせるなら true そうでなければ false
     */
    boolean canMove(int xRel, int yRel) {
        Point[] pa = data[blockSelect][DIRECTION];
        for (int i = 0; i < 4; i++) {
            Point p = pa[i];
            if (field[point.x + p.x + xRel][point.y + p.y + yRel] != SPACE) {
                return false;
            }
        }
        return true;
    }


積み上げる部分は、下記のような「ブロックを固定する」処理を行うメソッドを定義して、それを呼ぶようにすると良いと思います。
直接は関係ないですが、普通のスレッドから描画処理を呼ぶのは良くないので、EventQueue.invokeLaterを使います。
    /**落下メソッド*/
    @Override
    public void run() {
        Runnable callRepaint = new Runnable() {
            public void run() {
                repaint();
            }
        };
        while (true) {
            try {
                Thread.sleep(1000); // 1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (canMove(0, 1)) {
                point.y++;
            }
            else {//底辺もしくは他のブロックに接したとき
                fixBlock(); // ★ここ★
                initBlock();
            }
            EventQueue.invokeLater(callRepaint); // ★必ず呼ばれるので1箇所で良いでしょう
        }
    }

    /**
     * ブロックを固定する。
     */
    void fixBlock() {
        Point[] pa = data[blockSelect][DIRECTION];
        for (int i = 0; i < 4; i++) {
            Point p = pa[i];
            field[point.x + p.x][point.y + p.y] = colorSelect;
        }
    }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2014/12/23 15:28

    argiusさん、何度も助けていただきありがとうございます。上記のコードを参考にさせていただきました。おかげ様でなんとか処理を成功させることができました。もう少しで完成させることができそうです!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る