Prainブログ

ゲーム開発とかIT小話とかその他雑記のブログ

*

ゲーム制作 Cocos2d-x関連 第7回 「ボールとドロップとの衝突検知と跳ね返り」

      2016/07/08

今回はボールとドロップとの衝突検知を実装してきます。

 

前回はボールと画面端との衝突判定と跳ね返りのプログラムを行いました。

 

画面端であれば上下左右の4通りの検知を行えばよかったのですが、画面上に存在するドロップの数が変わり得る場合、ベタにプログラムを書くことは難しいですね。

 

 

スポンサーリンク

 

 

リストを使おう!

リストの用意

そこで便利なのがリストです。リストを使えばプログラムを比較的簡易に記述することができます。以下のプログラムを記述してください。

 

HelloWorldScene.h

std::list<cocos2d::Sprite*> _dropList; // フィールド上のドロップを格納する

 

・std::list

std::listはC++のコンテナです。上記のプログラムはSprite*型の変数を格納できる_dropListを定義した、という意味です。

詳しくはC++プログラミングシリーズで解説予定です。

 

リストへの要素の格納

実際にプログラム中でリストに要素を格納していきましよう

HelloWorldScene.cpp HelloWorld::init

 // ドロップ5種の格納
 _dropList.push_back(dropRed);
 _dropList.push_back(dropYellow);
 _dropList.push_back(dropBlue);
 _dropList.push_back(dropGreen);
 _dropList.push_back(dropPurple);

 

・push_back

リストの最後尾に要素を追加するメソッドです。

 

 

 

それでは続いて衝突判定の繰り返し処理、跳ね返り処理を実装していきましょう。

衝突判定の繰り返し処理、跳ね返り処理

ドロップとボールとの衝突判定の繰り返し処理、跳ね返り処理を行っていきます。

下記のプログラムをHelloWorldScene.cppのupdateメソッド中に追加してください。

 

HelloWorldScene.cpp HelloWorld::update

void HelloWorld::update(float dt){

    if(_isBallMoving){
        float x = _ball->getPosition().x - _speed*cosf(CC_DEGREES_TO_RADIANS(_degree));
        float y = _ball->getPosition().y + _speed*sinf(CC_DEGREES_TO_RADIANS(_degree));
        _ball->setPosition(Vec2(x, y));


        // ドロップに接触した場合の跳ね返りの処理
        std::list<Sprite*>::iterator it; 
        for(it = _dropList.begin(); it != _dropList.end(); it++){
            if(hitDetectionOval(_ball, (*it))){
                // 跳ね返り角度の更新 
                _degree = getDegree(_ball->getPosition(), (*it)->getPosition()); 
            } 
        }

        // 画面端に接触した場合の跳ね返りの処理
        Size visibleSize = Director::getInstance()->getVisibleSize();

        // 画面下端
        if(_ball->getPosition().y - _ball->getContentSize().height / 2 <= 0){
            _degree = _degree * -1;
            y = _ball->getContentSize().height / 2;
        }

        // 画面上端
        if(_ball->getPosition().y + _ball->getContentSize().height / 2 >= visibleSize.height - _enemy->getContentSize().height){
            _degree = _degree * -1;
            y = visibleSize.height - _ball->getContentSize().width / 2 - _enemy->getContentSize().height;
        }

        // 画面左端
        if(_ball->getPosition().x - _ball->getContentSize().width / 2 <= 0){
            _degree = 180 - _degree;
            x = _ball->getContentSize().width / 2;
        }

        // 画面右端
        if(_ball->getPosition().x + _ball->getContentSize().width / 2 >= visibleSize.width){
            _degree = 180 - _degree;
            x = visibleSize.width - _ball->getContentSize().width / 2;
        }

        _ball->setPosition(Vec2(x, y));
}
※ハイライト部分を追記してください。

 

std::list<Sprite*>::iterator it;

for(it = _dropList.begin(); it != _dropList.end(); it++){}

先ほどのリストに格納したドロップをを先頭から最後尾まで取得していくのがこの2行です。この書き方はお決まりなのでリストを使うときは覚えておくと良いと思います。

 

・hitDetectionOval

自作のメソッドです。2つのオブジェクトの円形衝突判定を行います。説明は後述します。

 

・getDegree

前回も使用した自作のメソッドです。2つのオブジェクトの座標から角度を取得します。ドロップがボールにぶつかった際は、このメソッドで新しい角度を取得し、ボールが移動を続けることになります。

 

 

衝突判定の繰り返し処理、跳ね返り処理のプログラム解説

ボールとドロップとの衝突判定の繰り返し処理

リストの先頭からドロップを順番に取り出し、ボールとの円形衝突判定を行っています。衝突している場合には衝突したドロップとボールとの角度を取得して跳ね返りとしています。

 

複数ドロップとの同時衝突の考慮

同時に2つ以上のドロップと衝突した場合は、リストの末尾に格納されているドロップが優先されます。イメージは下図になります。

7

 

この衝突処理は、ゲームとしてどうあるべきかによって実装は変わってきます。例えば以下のようにです。

・リスト末尾に近い要素との衝突を優先する(今回)

・リスト先頭に近い要素との衝突を優先する(衝突を検知した段階でbreak処理を入れるなどして、途中で処理を打ち切る)

・複数の角度を取得し、中間の角度を取得するようにする

 

どの処理が適切であるかは、ゲームの内容によって変わってくるかと思います。

 

 

次に、円形の物体の衝突判定について解説します。

円形の物体の衝突判定を行う

実際に衝突判定を行うメソッドであるhitDetectionOvalメソッドを追加します。

円形での衝突処理のイメージはこんな感じでしょうか。

 

【衝突の直前】

ball_drop1

【衝突の直後】

ball_drop2

 

 

それでは以下のプログラムを追記してください。

HelloWorldScene.h

bool hitDetectionOval(cocos2d::Sprite*, cocos2d::Sprite*); // 円形の接触判定を行う

 

 

HelloWorldScene.cpp HelloWorld::hitDetectionOval

/*
 円形の接触判定を行う。接触していればtrue,接触していなければfalseを返却する
 オブジェクトから算出する円形の縮小率は0.9とする
 
 */
bool HelloWorld::hitDetectionOval(Sprite* o, Sprite* t){
 
    if(((o->getPosition().x - t->getPosition().x) * (o->getPosition().x - t->getPosition().x)
        + (o->getPosition().y - t->getPosition().y) * (o->getPosition().y - t->getPosition().y))
        <= ((o->getContentSize().width / 2 * 0.9 + t->getContentSize().width / 2 * 0.9 ))
        * ((o->getContentSize().width / 2 * 0.9 + t->getContentSize().width / 2 * 0.9))){
        return true;
 
    }else{
        return false;
    }
}

 

 

円形衝突判定のプログラム解説

円形衝突判定のアルゴリズム

3平方の定理より、「2点間の(X方向の距離)^2 +(Y方向の距離)^2」が「(それぞれの円の半径の加算)^2」よりも小さければ接触していることになります。

三平方の定理 プレイン Prain

 

 

円形の衝突判定はググると詳しく解説しているサイトが出てきますのでそちらをどうぞ。

 

当たり感の調整

係数として0.9を乗じることで、見せかけの当たり判定よりも小さくして当たり感を自然にしています。

 

 

シミュレータの起動

さてシミュレータを起動してみましょう

 

どうでしょうか、ボールとドロップが接触すると跳ね返るようになったと思います。

 

ただ、ボールはずっと動き続けていますよね。次はボールの減速処理を追加したいと思います。

 

 

次回

第8回 「より保守性のあるプログラムを目指して 」

 

んん・・・おお・・・??

 

 - ,

        

Comment

  1. うめなつ より:

    衝突判定、自分で実装するとなるとなかなか難しいですよね・・・
    ボールに重力を与えて、ただバウンドするだけのゲームを作ってた頃は「なんだ物理演算って簡単じゃん」とか考えてました。
    それで実際なにか作ろうとしたら、質量、摩擦、反発係数を考えなきゃいけないし、四角いものがぶつかったら回転しなきゃいけない。そんなこんなで挫折した経験があります。
    思えば、当時はボール同士の衝突について解説しているサイトはほとんど無かったように思います。(そのせいで物理の教科書を広げるハメになりました)
    物理とプログラムの両方を理解する必要があるので内容はやや難しいですが、きっとこの講座は役に立つと思います!

    • aki より:

      コメントありがとうございます。

      これから活動の内容を増やしていく予定ですので、お手すきの際、たまにチェックしていただければ幸いです!

Message

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

  関連記事

エフェクト
Cocos2d-x エフェクトアニメーションの終了と同時にエフェクトを消す方法

この記事では、アニメーションを表示し終わった後に、アニメーション表示を消す方法を …

狼ちゃんボール
ゲーム制作 Cocos2d-x関連 第2回 「Cocos2d-xで作ろう!!~画像の表示処理」

いきなり知らない単語が!Cocos2d-x?Cocos2d-xで画像を表示しよう …

メモリー
Cocos2d-x アニメーションをバッファリングして、高速で安定した処理を行う方法

この記事では、Cocos2d-xにおけるアニメーションのバッファリングについて説 …

cocos2dx
Cocos2d-x カスタムイベントを利用してソースコードの可読性をあげる

こんにちは、akiです。 この記事ではカスタムイベントの使い方について説明します …

マップイメージ
ゲーム制作 Cocos2d-x関連 第14回 「プレイヤー攻撃によるダメージ計算、ターン交代」

今回はプレイヤーのターン、敵のターンを交互に行うようにしていきます。また、それぞ …