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

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

ただいまの
回答率

89.64%

衝突判定

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 929

nakamura-

score 46

シューティングゲームのようなものを作っています
(上から降ってくる桜の花びらを撃つ)
下のプログラムはその途中のものです。

コード
public class Part01 extends JApplet{
    /*初期メソッド*/
    public void init(){
        setSize(1000, 700);            
        MyPanel mp = new MyPanel();
        getContentPane().add(mp);
   }
}
コード
public class MyPanel extends JPanel implements Runnable{
    Player player;

    int gunCount = 0;
    Gun gun;
    ArrayList<Gun> gunList = new ArrayList<Gun>();

    Sakura sakura;
    ArrayList<Sakura> sakuraList = new ArrayList<Sakura>();

    Thread loopPaint;
    Random rnd = new Random();

    /*MyPanelのコンストラクタ*/
    public MyPanel(){
        setBackground(Color.BLACK);        
        /*再描画ループスレッド*/
        loopPaint = new Thread(this);
        loopPaint.start();

        /*画像読み込み、切り取り*/
        try{
            player = new Player("jiki.gif", 0, 0, 32, 32);
        }
        catch(IOException ioe){
            System.out.println("画像がありません");
        }

        loopGunThread();
        loopCreateSakuraThread();
        loopSakuraThread();
    }

    /*キー入力処理*/
    @Override
    protected void processKeyEvent(KeyEvent ke){
        if(ke.getID() == KeyEvent.KEY_PRESSED){

            player.updata(ke.getKeyCode());

            if(ke.getKeyCode() == KeyEvent.VK_ENTER){
                gun = new Gun(10000, 10000);
                gunList.add(gun);                

                gunList.get(gunCount).startLine(player.m_x, player.m_y);
                gunCount++;
            }
        }
    }

    /*描画メソッド*/
    @Override
    protected void paintComponent(Graphics g){
        super.paintComponent(g);
        requestFocusInWindow();        //キー入力有効化

        player.draw(g);

        if(gun != null){                                //gunがインスタンス化されている場合(Enter押された場合)
            for(int i = 0; i < gunList.size(); i++){    //弾の数だけ繰り返し
                gunList.get(i).draw(g);
            }
        }

        if(sakura != null){
            for(int i = 0; i < sakuraList.size(); i++){
                sakuraList.get(i).draw(g);
            }
        }

        if(gun != null && sakura != null){
            for(int i = 0; i < gunList.size(); i++){
                for(int k = 0; k < sakuraList.size(); k++){
                    if(    sakuraList.get(k).Screen_vData[0][0] > gunList.get(i).m_x &&
                        sakuraList.get(k).Screen_vData[1][0] < gunList.get(i).m_x &&
                        sakuraList.get(k).Screen_vData[0][1] > gunList.get(i).m_y &&
                        sakuraList.get(k).Screen_vData[1][1] < gunList.get(i).m_y     ){
                            System.out.println("-----衝突-----");
                    }
                }
            }
        }
    }

    /*runメソッド(loopPaintスレッド)*/
    public void run(){
        while(true){
            repaint();
        }
    }

    /*弾ループスレッド作成*/
    public void loopGunThread(){
        Thread loopGun = new Thread(new Runnable(){
            public void run(){
                while(true){
                    try{
                        Thread.sleep(50);
                    }
                    catch(InterruptedException e){
                    }

                    if(gun != null){                                //gunがインスタンス化されている場合(Enter押された場合)
                        for(int i = 0; i < gunList.size(); i++){    //弾の数だけ繰り返し
                            if(gunList.get(i).m_y > -10){
                                gunList.get(i).updata();
                            }
                        }
                    }
                }
            }
        });
        loopGun.start();
    }

    /*桜作成ループスレッド作成*/
    public void loopCreateSakuraThread(){
        Thread createSakura = new Thread(new Runnable(){
            public void run(){
                while(true){
                    try{
                        Thread.sleep((long)(Math.random() * 1000));
                    }
                    catch(InterruptedException e){
                    }

                    sakura = new Sakura(     1,  1, 1,
                                            -1, -1, 1,
                                            -1,  1, 0,
                                             1, -1, 0,
                                        400, -100,
                                        rnd.nextInt(6) + 5);
                    sakuraList.add(sakura);
                }
            }
        });
        createSakura.start();
    }

    /*桜ループスレッド作成*/
    public void loopSakuraThread(){
        Thread loopSakura = new Thread(new Runnable(){
            public void run(){

                while(true){
                    try{
                        Thread.sleep(20);
                    }
                    catch(InterruptedException e){
                    }

                    if(sakura != null){
                        for(int i = 0; i < sakuraList.size(); i++){
                            sakuraList.get(i).updata();
                        }
                    }
                }
            }
        });
        loopSakura.start();
    }

}
コード
public class Player{

    BufferedImage m_bi;
    int m_x = 485;
    int m_y = 640;

    /*Playerのコンストラクタ*/
    public Player(BufferedImage bi){
        m_bi = bi;
    }
    public Player(BufferedImage bi, int x, int y, int w, int h){
        m_bi = bi.getSubimage(x, y, w, h);
    }
    public Player(File file, int x, int y, int w, int h) throws IOException{
        BufferedImage bi = ImageIO.read(file);
        m_bi = bi.getSubimage(x, y, w, h);
    }
    public Player(String str, int x, int y, int w, int h) throws IOException{
        File file = new File(str);
        BufferedImage bi = ImageIO.read(file);
        m_bi = bi.getSubimage(x, y, w, h);
    }

    /*位置更新メソッド*/
    public void updata(int keyCode){
        if(keyCode == KeyEvent.VK_RIGHT){
            m_x += 32;
        }
        if(keyCode == KeyEvent.VK_LEFT){
            m_x -= 32;
        }
    }

    /*描画メソッド*/
    public void draw(Graphics g){
        g.drawImage(m_bi, m_x, m_y, null);
    }

}
コード
public class Gun{

    int m_x;
    int m_y;

    /*Gunのコンストラクタ*/
    public Gun(int x, int y){
        m_x = x;
        m_y = y;
    }

    /*攻撃開始位置設定メソッド*/
    public void startLine(int x, int y){
        m_x = x;
        m_y = y;
    }

    /*位置更新メソッド*/
    public void updata(){
        m_y -= 10;
    }

    /*描画メソッド*/
    public void draw(Graphics g){
        g.setColor(Color.WHITE);
        g.fillRect(m_x, m_y, 2, 2);
    }

}
コード
public class Sakura{

    double Vertex_data[][] = new double[2][3];    //頂点データ
    double Screen_vData[][] = new double[2][3];    //表示頂点データ

    double Control_data[][] = new double[2][3];    //曲線描くときのコントロールデータ
    double Screen_cData[][] = new double[2][3];    //表示コントロールデータ

    double pointX;            //表示開始位置のx座標
    double pointY;            //表示開始位置のy座標
    int scale;                //描画倍数

    double phi;            //x軸周りの回転角
    double theta;            //y軸周りの回転角

    Random rnd = new Random();

    /*Sakuraクラスのコンストラクタ*/
    public Sakura(    double Vertex_data00,  double Vertex_data01,  double Vertex_data02,
                    double Vertex_data10,  double Vertex_data11,  double Vertex_data12,
                    double Control_data00, double Control_data01, double Control_data02,
                    double Control_data10, double Control_data11, double Control_data12,
                    double pointX, double pointY, int scale){
        this.Vertex_data[0][0] = Vertex_data00;
        this.Vertex_data[0][1] = Vertex_data01;
        this.Vertex_data[0][2] = Vertex_data02;
        this.Vertex_data[1][0] = Vertex_data10;
        this.Vertex_data[1][1] = Vertex_data11;
        this.Vertex_data[1][2] = Vertex_data12;

        this.Control_data[0][0] = Control_data00;
        this.Control_data[0][1] = Control_data01;
        this.Control_data[0][2] = Control_data02;
        this.Control_data[1][0] = Control_data10;
        this.Control_data[1][1] = Control_data11;
        this.Control_data[1][2] = Control_data12;

        this.pointX = pointX;
        this.pointY = pointY;
        this.scale = scale;

        phi = 0;
        theta = 0;
    }

    /*位置変更メソッド*/
    public void updata(){
        pointX += (Math.random() * 1);

        phi += Math.PI / (rnd.nextInt(201) + 30);
        theta += Math.PI / (rnd.nextInt(151) + 30);
        pointY += (Math.random() + 2);    

        setRotPosition();
    }

    /*回転・落下後の座標を求めるメソッド*/
    public void setRotPosition(){
        double Xsin = Math.sin(phi);
        double Xcos = Math.cos(phi);
        double Ysin = Math.sin(theta);
        double Ycos = Math.cos(theta);

        for(int i = 0; i < Vertex_data.length; i++){
            double rvx = Vertex_data[i][0] * Ycos +
                                Vertex_data[i][2] * Ysin;
            double rvy = Vertex_data[i][0] * Xsin * Ysin +
                                Vertex_data[i][1] * Xcos -
                                    Vertex_data[i][2] * Xcos * Ycos;
            double rvz = - Vertex_data[i][0] * Xcos * Ysin +
                                Vertex_data[i][1] * Xsin +
                                    Vertex_data[i][2] * Xcos * Ycos;

            Screen_vData[i][0] = (float)(pointX + (rvx * scale));
            Screen_vData[i][1] = (float)(pointY - (rvy * scale));
            Screen_vData[i][2] = (float)(rvz * scale);
        }

        for(int i = 0; i < Control_data.length; i++){
            double rcx = Control_data[i][0] * Ycos +
                                Control_data[i][2] * Ysin;
            double rcy = Control_data[i][0] * Xsin * Ysin +
                                Control_data[i][1] * Xcos -
                                    Control_data[i][2] * Xcos * Ycos;
            double rcz = - Control_data[i][0] * Xcos * Ysin +
                                Control_data[i][1] * Xsin +
                                    Control_data[i][2] * Xcos * Ycos;

            Screen_cData[i][0] = (float)(pointX + (rcx * scale));
            Screen_cData[i][1] = (float)(pointY - (rcy * scale));
            Screen_cData[i][2] = (float)(rcz * scale);
        }
    }

    /*描画メソッド*/
    public void draw(Graphics g){
        Graphics2D g2D = (Graphics2D)g;
        GeneralPath path = new GeneralPath();

        path.moveTo(Screen_vData[0][0], Screen_vData[0][1]);
        path.quadTo(Screen_cData[0][0], Screen_cData[0][1], Screen_vData[1][0], Screen_vData[1][1]);
        path.quadTo(Screen_cData[1][0], Screen_cData[1][1], Screen_vData[0][0], Screen_vData[0][1]);
        path.closePath();

        g2D.setColor(Color.pink);
        g2D.fill(path);
    }

}


MyPanelクラスのpaintComponentメソッドで衝突判定をしているのですが、桜を描画しているxとyの幅の中に弾が存在すれば(桜と弾が重なれば)「衝突」と表示するようにしているのですが、弾が桜を通り抜けても表示されませんでした。
衝突判定方法がなにか間違っているのでしょうか?
それとも他に何か原因があるのでしょうか?
よろしくお願いします

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

0

3D的な座標管理、描画されていますので、当たり判定も3D的に考えなければいけません。しかし、3Dの当たり判定をやるにはハードルが高すぎます。そこで簡単なのが「だいたいの大きさで当たり判定をとる」という考え方です。実際のゲームでも労力、処理の複雑さとを天秤にかけ、アバウトな当たり判定を行うことはよくあります。
今回の場合、Sakuraの基準座標xyを中心としたscale分の大きさの円、そして弾のxyの当たり判定を取るのが一番それっぽいかつ簡単でしょう。

// Sakuraの基準位置をx1,y1とする。
double x1 = sakuraList.get(k).Screen_vData[0][0];
double y1 = sakuraList.get(k).Screen_vData[0][1];

// gunの基準位置をx2,y2とする。
double x2 = gunList.get(i).m_x;
double y2 = gunList.get(i).m_y;

// ベクトルの計算を使い、基準位置同士の距離を計算する。
double length = Math.sqrt(Math.pow(x1-x2,2) + Math.pow(y1-y2,2));

// 距離がSakuraの大きさ(scale)より小さければ衝突とする
if(length < sakuraList.get(k).scale){
    System.out.println("-----衝突-----");
}

Sakuraの基準位置が微妙に感じると思うのでしたら、vData[1][0]、[1][1]との中間位置を計算してそこを基準にしたらよいかもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/07/08 16:50

    回答ありがとうございます。
    中間位置を基準にするときれいにできました!
    ありがとうございました。

    キャンセル

0

コードをざっと眺めただけですが、桜の花びらの大きさは2×2なのに、弾丸は毎ステップ10ピクセル移動していきますので、重なる瞬間が一回もないまま向こう側へ行ってしまいそうです。
重なる瞬間が発生しないのに当たり判定をする方法、熱力学シミュレーションなんかだと厳密な方法があるのですがゲームだからそれっぽく見えればいいわけで、花びらの当たり判定を上方向に10ピクセル延ばせば十分ではないかと。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/07/04 15:15

    回答ありがとうございます。
    花びらの大きさは5~10のランダムで設定して弾を2×2にしています。
    目視でも重なっているのは確認できます。

    当たり判定を上方向に10ピクセル延ばすというのはどういうことでしょうか?

    キャンセル

0

衝突判定からみたデータの持ち方ってこれであってますか?

座標(x,y)=(0,0)が左上とした場合
左上座標(Screen_vData[1][0], Screen_vData[1][1])
この間に弾座標(m_x, m_y)があれば衝突判定
右下座標(Screen_vData[0][0],Screen_vData[0][1])

感覚的なものだけど、Screen_vData[0]とScreen_vData[1]の大小関係が怪しそう。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/07/08 16:49

    回答ありがとうございます。
    確かに回転もしているので大小関係がおかしいかもしれません。
    確認したいと思います。
    ご指摘ありがとうございます!

    キャンセル

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

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

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